From 74070dc6b16620fa7190b8d26aaf2b8f1104027d Mon Sep 17 00:00:00 2001 From: Christopher Date: Tue, 30 Jun 2020 09:54:27 +0200 Subject: [PATCH] move source into dll --- BouncyCastle.Crypto.dll | Bin 0 -> 2787928 bytes MailKit.dll | Bin 0 -> 819712 bytes MimeKit.dll | Bin 0 -> 998912 bytes prebuild.xml | 4 + src/MailKit/AccessControl.cs | 137 - src/MailKit/AccessControlList.cs | 66 - src/MailKit/AccessRight.cs | 232 - src/MailKit/AccessRights.cs | 317 - src/MailKit/AlertEventArgs.cs | 69 - src/MailKit/Annotation.cs | 81 - src/MailKit/AnnotationAccess.cs | 53 - src/MailKit/AnnotationAttribute.cs | 251 - src/MailKit/AnnotationEntry.cs | 521 - src/MailKit/AnnotationScope.cs | 61 - src/MailKit/AnnotationsChangedEventArgs.cs | 93 - src/MailKit/AuthenticatedEventArgs.cs | 68 - src/MailKit/BodyPart.cs | 716 - src/MailKit/BodyPartBasic.cs | 245 - src/MailKit/BodyPartCollection.cs | 276 - src/MailKit/BodyPartMessage.cs | 124 - src/MailKit/BodyPartMultipart.cs | 141 - src/MailKit/BodyPartText.cs | 123 - src/MailKit/BodyPartVisitor.cs | 137 - src/MailKit/CommandException.cs | 102 - src/MailKit/CompressedStream.cs | 435 - src/MailKit/ConnectedEventArgs.cs | 88 - src/MailKit/DeliveryStatusNotification.cs | 61 - src/MailKit/DeliveryStatusNotificationType.cs | 54 - src/MailKit/DisconnectedEventArgs.cs | 70 - src/MailKit/DuplexStream.cs | 395 - src/MailKit/Envelope.cs | 592 - src/MailKit/FolderAccess.cs | 53 - src/MailKit/FolderAttributes.cs | 135 - src/MailKit/FolderCreatedEventArgs.cs | 67 - src/MailKit/FolderFeature.cs | 82 - src/MailKit/FolderNamespace.cs | 74 - src/MailKit/FolderNamespaceCollection.cs | 235 - src/MailKit/FolderNotFoundException.cs | 149 - src/MailKit/FolderNotOpenException.cs | 184 - src/MailKit/FolderQuota.cs | 124 - src/MailKit/FolderRenamedEventArgs.cs | 85 - src/MailKit/IMailFolder.cs | 5077 ----- src/MailKit/IMailService.cs | 1118 -- src/MailKit/IMailSpool.cs | 511 - src/MailKit/IMailStore.cs | 549 - src/MailKit/IMailTransport.cs | 179 - src/MailKit/IMessageSummary.cs | 460 - src/MailKit/IProtocolLogger.cs | 116 - src/MailKit/ITransferProgress.cs | 58 - src/MailKit/MailFolder.cs | 16501 ---------------- src/MailKit/MailService.cs | 1582 -- src/MailKit/MailSpool.cs | 1588 -- src/MailKit/MailStore.cs | 872 - src/MailKit/MailTransport.cs | 507 - src/MailKit/MessageEventArgs.cs | 98 - src/MailKit/MessageFlags.cs | 78 - src/MailKit/MessageFlagsChangedEventArgs.cs | 269 - src/MailKit/MessageLabelsChangedEventArgs.cs | 170 - src/MailKit/MessageNotFoundException.cs | 92 - src/MailKit/MessageSentEventArgs.cs | 87 - src/MailKit/MessageSorter.cs | 295 - src/MailKit/MessageSummary.cs | 758 - src/MailKit/MessageSummaryFetchedEventArgs.cs | 67 - src/MailKit/MessageSummaryItems.cs | 222 - src/MailKit/MessageThread.cs | 108 - src/MailKit/MessageThreader.cs | 603 - src/MailKit/MessagesVanishedEventArgs.cs | 79 - src/MailKit/Metadata.cs | 74 - src/MailKit/MetadataChangedEventArgs.cs | 67 - src/MailKit/MetadataCollection.cs | 59 - src/MailKit/MetadataOptions.cs | 108 - src/MailKit/MetadataTag.cs | 156 - src/MailKit/ModSeqChangedEventArgs.cs | 86 - src/MailKit/Net/Imap/AsyncImapClient.cs | 965 - src/MailKit/Net/Imap/IImapClient.cs | 628 - src/MailKit/Net/Imap/IImapFolder.cs | 869 - src/MailKit/Net/Imap/ImapCallbacks.cs | 68 - src/MailKit/Net/Imap/ImapCapabilities.cs | 348 - src/MailKit/Net/Imap/ImapClient.cs | 2616 --- src/MailKit/Net/Imap/ImapCommand.cs | 918 - src/MailKit/Net/Imap/ImapCommandException.cs | 190 - src/MailKit/Net/Imap/ImapCommandResponse.cs | 55 - src/MailKit/Net/Imap/ImapEncoding.cs | 155 - src/MailKit/Net/Imap/ImapEngine.cs | 2905 --- src/MailKit/Net/Imap/ImapEventGroup.cs | 692 - src/MailKit/Net/Imap/ImapFolder.cs | 6224 ------ src/MailKit/Net/Imap/ImapFolderAnnotations.cs | 567 - .../Net/Imap/ImapFolderConstructorArgs.cs | 113 - src/MailKit/Net/Imap/ImapFolderFetch.cs | 6770 ------- src/MailKit/Net/Imap/ImapFolderFlags.cs | 2888 --- src/MailKit/Net/Imap/ImapFolderSearch.cs | 1773 -- src/MailKit/Net/Imap/ImapImplementation.cs | 217 - src/MailKit/Net/Imap/ImapProtocolException.cs | 109 - src/MailKit/Net/Imap/ImapResponseCode.cs | 373 - .../Net/Imap/ImapSearchQueryOptimizer.cs | 83 - src/MailKit/Net/Imap/ImapStream.cs | 1190 -- src/MailKit/Net/Imap/ImapToken.cs | 80 - src/MailKit/Net/Imap/ImapUtils.cs | 1670 -- src/MailKit/Net/NetworkStream.cs | 298 - src/MailKit/Net/Pop3/AsyncPop3Client.cs | 1496 -- src/MailKit/Net/Pop3/IPop3Client.cs | 309 - src/MailKit/Net/Pop3/Pop3Capabilities.cs | 129 - src/MailKit/Net/Pop3/Pop3Client.cs | 3401 ---- src/MailKit/Net/Pop3/Pop3Command.cs | 74 - src/MailKit/Net/Pop3/Pop3CommandException.cs | 179 - src/MailKit/Net/Pop3/Pop3Engine.cs | 655 - src/MailKit/Net/Pop3/Pop3Language.cs | 71 - src/MailKit/Net/Pop3/Pop3ProtocolException.cs | 101 - src/MailKit/Net/Pop3/Pop3Stream.cs | 950 - src/MailKit/Net/Proxy/HttpProxyClient.cs | 248 - src/MailKit/Net/Proxy/IProxyClient.cs | 208 - src/MailKit/Net/Proxy/ProxyClient.cs | 406 - .../Net/Proxy/ProxyProtocolException.cs | 97 - src/MailKit/Net/Proxy/Socks4Client.cs | 299 - src/MailKit/Net/Proxy/Socks4aClient.cs | 88 - src/MailKit/Net/Proxy/Socks5Client.cs | 421 - src/MailKit/Net/Proxy/SocksClient.cs | 100 - src/MailKit/Net/SelectMode.cs | 36 - src/MailKit/Net/Smtp/AsyncSmtpClient.cs | 689 - src/MailKit/Net/Smtp/ISmtpClient.cs | 246 - src/MailKit/Net/Smtp/SmtpCapabilities.cs | 106 - src/MailKit/Net/Smtp/SmtpClient.cs | 2465 --- src/MailKit/Net/Smtp/SmtpCommandException.cs | 258 - src/MailKit/Net/Smtp/SmtpDataFilter.cs | 140 - src/MailKit/Net/Smtp/SmtpProtocolException.cs | 101 - src/MailKit/Net/Smtp/SmtpResponse.cs | 68 - src/MailKit/Net/Smtp/SmtpStatusCode.cs | 190 - src/MailKit/Net/Smtp/SmtpStream.cs | 903 - src/MailKit/Net/SocketUtils.cs | 118 - src/MailKit/Net/SslStream.cs | 44 - src/MailKit/NullProtocolLogger.cs | 113 - src/MailKit/ProgressStream.cs | 185 - src/MailKit/Properties/AssemblyInfo.cs | 84 - src/MailKit/ProtocolException.cs | 100 - src/MailKit/ProtocolLogger.cs | 341 - src/MailKit/Search/AnnotationSearchQuery.cs | 105 - src/MailKit/Search/BinarySearchQuery.cs | 100 - src/MailKit/Search/DateSearchQuery.cs | 62 - src/MailKit/Search/FilterSearchQuery.cs | 94 - src/MailKit/Search/HeaderSearchQuery.cs | 91 - src/MailKit/Search/ISearchQueryOptimizer.cs | 32 - src/MailKit/Search/NumericSearchQuery.cs | 60 - src/MailKit/Search/OrderBy.cs | 223 - src/MailKit/Search/OrderByAnnotation.cs | 91 - src/MailKit/Search/OrderByType.cs | 90 - src/MailKit/Search/SearchOptions.cs | 69 - src/MailKit/Search/SearchQuery.cs | 1136 -- src/MailKit/Search/SearchResults.cs | 116 - src/MailKit/Search/SearchTerm.cs | 288 - src/MailKit/Search/SortOrder.cs | 50 - src/MailKit/Search/TextSearchQuery.cs | 75 - src/MailKit/Search/UidSearchQuery.cs | 94 - src/MailKit/Search/UnarySearchQuery.cs | 82 - .../Security/AuthenticationException.cs | 94 - src/MailKit/Security/KeyedHashAlgorithm.cs | 137 - src/MailKit/Security/Ntlm/BitConverterLE.cs | 112 - .../Security/Ntlm/ChallengeResponse2.cs | 280 - src/MailKit/Security/Ntlm/DES.cs | 232 - src/MailKit/Security/Ntlm/HMACMD5.cs | 202 - src/MailKit/Security/Ntlm/MD4.cs | 410 - src/MailKit/Security/Ntlm/MessageBase.cs | 99 - src/MailKit/Security/Ntlm/NtlmAuthLevel.cs | 51 - src/MailKit/Security/Ntlm/NtlmFlags.cs | 222 - src/MailKit/Security/Ntlm/TargetInfo.cs | 261 - src/MailKit/Security/Ntlm/Type1Message.cs | 167 - src/MailKit/Security/Ntlm/Type2Message.cs | 191 - src/MailKit/Security/Ntlm/Type3Message.cs | 284 - src/MailKit/Security/RandomNumberGenerator.cs | 52 - src/MailKit/Security/SaslException.cs | 158 - src/MailKit/Security/SaslMechanism.cs | 602 - src/MailKit/Security/SaslMechanismCramMd5.cs | 215 - .../Security/SaslMechanismDigestMd5.cs | 571 - src/MailKit/Security/SaslMechanismLogin.cs | 297 - src/MailKit/Security/SaslMechanismNtlm.cs | 230 - src/MailKit/Security/SaslMechanismOAuth2.cs | 179 - src/MailKit/Security/SaslMechanismPlain.cs | 293 - .../Security/SaslMechanismScramBase.cs | 376 - .../Security/SaslMechanismScramSha1.cs | 151 - .../Security/SaslMechanismScramSha256.cs | 151 - src/MailKit/Security/SecureSocketOptions.cs | 68 - src/MailKit/Security/SslHandshakeException.cs | 305 - .../ServiceNotAuthenticatedException.cs | 97 - src/MailKit/ServiceNotConnectedException.cs | 97 - src/MailKit/SpecialFolder.cs | 75 - src/MailKit/StatusItems.cs | 88 - src/MailKit/ThreadingAlgorithm.cs | 48 - src/MailKit/UniqueId.cs | 428 - src/MailKit/UniqueIdMap.cs | 223 - src/MailKit/UniqueIdRange.cs | 494 - src/MailKit/UniqueIdSet.cs | 988 - src/MailKit/UriExtensions.cs | 70 - src/MailKit/mailkit.snk | Bin 596 -> 0 bytes src/MailKitMailModule.cs | 25 +- src/MimeKit/AsyncMimeParser.cs | 706 - src/MimeKit/AttachmentCollection.cs | 653 - src/MimeKit/BodyBuilder.cs | 186 - src/MimeKit/CancellationToken.cs | 76 - src/MimeKit/ContentDisposition.cs | 916 - src/MimeKit/ContentEncoding.cs | 83 - src/MimeKit/ContentType.cs | 881 - .../Cryptography/ApplicationPgpEncrypted.cs | 97 - .../Cryptography/ApplicationPgpSignature.cs | 106 - .../Cryptography/ApplicationPkcs7Mime.cs | 924 - .../Cryptography/ApplicationPkcs7Signature.cs | 105 - src/MimeKit/Cryptography/ArcSigner.cs | 729 - src/MimeKit/Cryptography/ArcVerifier.cs | 694 - .../AsymmetricAlgorithmExtensions.cs | 373 - .../Cryptography/AuthenticationResults.cs | 1366 -- .../BouncyCastleCertificateExtensions.cs | 365 - .../BouncyCastleSecureMimeContext.cs | 1339 -- .../CertificateNotFoundException.cs | 115 - src/MimeKit/Cryptography/CmsRecipient.cs | 256 - .../Cryptography/CmsRecipientCollection.cs | 208 - src/MimeKit/Cryptography/CmsSigner.cs | 464 - .../Cryptography/CryptographyContext.cs | 648 - src/MimeKit/Cryptography/DbExtensions.cs | 50 - .../Cryptography/DefaultSecureMimeContext.cs | 668 - src/MimeKit/Cryptography/DigestAlgorithm.cs | 112 - .../DigitalSignatureCollection.cs | 54 - .../DigitalSignatureVerifyException.cs | 147 - src/MimeKit/Cryptography/DkimBodyFilter.cs | 72 - .../DkimCanonicalizationAlgorithm.cs | 59 - src/MimeKit/Cryptography/DkimHashStream.cs | 370 - .../Cryptography/DkimPublicKeyLocatorBase.cs | 189 - .../Cryptography/DkimRelaxedBodyFilter.cs | 167 - .../Cryptography/DkimSignatureAlgorithm.cs | 53 - .../Cryptography/DkimSignatureStream.cs | 361 - src/MimeKit/Cryptography/DkimSigner.cs | 483 - src/MimeKit/Cryptography/DkimSignerBase.cs | 293 - .../Cryptography/DkimSimpleBodyFilter.cs | 140 - src/MimeKit/Cryptography/DkimVerifier.cs | 308 - src/MimeKit/Cryptography/DkimVerifierBase.cs | 495 - .../Cryptography/Ed25519DigestSigner.cs | 112 - .../Cryptography/EncryptionAlgorithm.cs | 142 - src/MimeKit/Cryptography/GnuPGContext.cs | 268 - .../Cryptography/IDigitalCertificate.cs | 92 - src/MimeKit/Cryptography/IDigitalSignature.cs | 99 - .../Cryptography/IDkimPublicKeyLocator.cs | 96 - .../Cryptography/IX509CertificateDatabase.cs | 196 - src/MimeKit/Cryptography/LdapUri.cs | 161 - src/MimeKit/Cryptography/MD5.cs | 749 - .../Cryptography/MacSecureMimeContext.cs | 267 - .../Cryptography/MultipartEncrypted.cs | 1207 -- src/MimeKit/Cryptography/MultipartSigned.cs | 581 - .../Cryptography/NpgsqlCertificateDatabase.cs | 264 - .../Cryptography/OpenPgpBlockFilter.cs | 208 - src/MimeKit/Cryptography/OpenPgpContext.cs | 1185 -- .../Cryptography/OpenPgpContextBase.cs | 1887 -- src/MimeKit/Cryptography/OpenPgpDataType.cs | 62 - .../Cryptography/OpenPgpDetectionFilter.cs | 344 - .../Cryptography/OpenPgpDigitalCertificate.cs | 184 - .../Cryptography/OpenPgpDigitalSignature.cs | 164 - .../Cryptography/OpenPgpKeyCertification.cs | 65 - .../PrivateKeyNotFoundException.cs | 152 - .../Cryptography/PublicKeyAlgorithm.cs | 90 - .../PublicKeyNotFoundException.cs | 115 - .../Cryptography/RsaEncryptionPadding.cs | 251 - .../RsaEncryptionPaddingScheme.cs | 47 - .../Cryptography/RsaSignaturePadding.cs | 153 - .../Cryptography/RsaSignaturePaddingScheme.cs | 47 - .../Cryptography/SecureMailboxAddress.cs | 219 - src/MimeKit/Cryptography/SecureMimeContext.cs | 842 - .../SecureMimeDigitalCertificate.cs | 149 - .../SecureMimeDigitalSignature.cs | 265 - src/MimeKit/Cryptography/SecureMimeType.cs | 65 - .../Cryptography/SqlCertificateDatabase.cs | 832 - .../Cryptography/SqliteCertificateDatabase.cs | 394 - .../Cryptography/SubjectIdentifierType.cs | 50 - .../TemporarySecureMimeContext.cs | 462 - .../Cryptography/WindowsSecureMimeContext.cs | 1248 -- .../WindowsSecureMimeDigitalCertificate.cs | 148 - .../WindowsSecureMimeDigitalSignature.cs | 212 - .../X509Certificate2Extensions.cs | 153 - .../Cryptography/X509CertificateChain.cs | 381 - .../Cryptography/X509CertificateDatabase.cs | 985 - .../Cryptography/X509CertificateRecord.cs | 315 - .../Cryptography/X509CertificateStore.cs | 544 - src/MimeKit/Cryptography/X509CrlRecord.cs | 172 - src/MimeKit/Cryptography/X509KeyUsageFlags.cs | 121 - src/MimeKit/DomainList.cs | 477 - src/MimeKit/EncodingConstraint.cs | 52 - src/MimeKit/Encodings/Base64Decoder.cs | 251 - src/MimeKit/Encodings/Base64Encoder.cs | 337 - src/MimeKit/Encodings/HexDecoder.cs | 232 - src/MimeKit/Encodings/HexEncoder.cs | 216 - src/MimeKit/Encodings/IMimeDecoder.cs | 117 - src/MimeKit/Encodings/IMimeEncoder.cs | 132 - src/MimeKit/Encodings/PassThroughDecoder.cs | 172 - src/MimeKit/Encodings/PassThroughEncoder.cs | 178 - src/MimeKit/Encodings/QEncoder.cs | 260 - .../Encodings/QuotedPrintableDecoder.cs | 277 - .../Encodings/QuotedPrintableEncoder.cs | 317 - src/MimeKit/Encodings/UUDecoder.cs | 418 - src/MimeKit/Encodings/UUEncoder.cs | 375 - src/MimeKit/Encodings/YDecoder.cs | 544 - src/MimeKit/Encodings/YEncoder.cs | 272 - src/MimeKit/FormatOptions.cs | 366 - src/MimeKit/GroupAddress.cs | 747 - src/MimeKit/Header.cs | 1582 -- src/MimeKit/HeaderId.cs | 792 - src/MimeKit/HeaderList.cs | 1547 -- src/MimeKit/HeaderListChangedEventArgs.cs | 74 - src/MimeKit/HeaderListCollection.cs | 254 - src/MimeKit/IMimeContent.cs | 178 - src/MimeKit/IO/BoundStream.cs | 718 - src/MimeKit/IO/ChainedStream.cs | 700 - src/MimeKit/IO/FilteredStream.cs | 812 - src/MimeKit/IO/Filters/ArmoredFromFilter.cs | 167 - src/MimeKit/IO/Filters/BestEncodingFilter.cs | 273 - src/MimeKit/IO/Filters/CharsetFilter.cs | 229 - src/MimeKit/IO/Filters/DecoderFilter.cs | 159 - src/MimeKit/IO/Filters/Dos2UnixFilter.cs | 121 - src/MimeKit/IO/Filters/EncoderFilter.cs | 163 - src/MimeKit/IO/Filters/IMimeFilter.cs | 74 - src/MimeKit/IO/Filters/MimeFilterBase.cs | 235 - src/MimeKit/IO/Filters/PassThroughFilter.cs | 100 - .../IO/Filters/TrailingWhitespaceFilter.cs | 140 - src/MimeKit/IO/Filters/Unix2DosFilter.cs | 120 - src/MimeKit/IO/ICancellableStream.cs | 95 - src/MimeKit/IO/MeasuringStream.cs | 427 - src/MimeKit/IO/MemoryBlockStream.cs | 557 - src/MimeKit/InternetAddress.cs | 1361 -- src/MimeKit/InternetAddressList.cs | 1110 -- src/MimeKit/MacInterop/CFArray.cs | 56 - src/MimeKit/MacInterop/CFData.cs | 85 - src/MimeKit/MacInterop/CFDictionary.cs | 177 - src/MimeKit/MacInterop/CFObject.cs | 76 - src/MimeKit/MacInterop/CFRange.cs | 55 - src/MimeKit/MacInterop/CFString.cs | 131 - .../MacInterop/CssmDbAttributeFormat.cs | 41 - src/MimeKit/MacInterop/CssmKeyUse.cs | 43 - .../MacInterop/CssmTPAppleCertStatus.cs | 39 - src/MimeKit/MacInterop/Dlfcn.cs | 228 - src/MimeKit/MacInterop/OSStatus.cs | 40 - src/MimeKit/MacInterop/SecCertificate.cs | 69 - src/MimeKit/MacInterop/SecExternalFormat.cs | 57 - src/MimeKit/MacInterop/SecItemAttr.cs | 60 - src/MimeKit/MacInterop/SecItemClass.cs | 40 - src/MimeKit/MacInterop/SecItemExportFlags.cs | 34 - src/MimeKit/MacInterop/SecKeyAttribute.cs | 59 - src/MimeKit/MacInterop/SecKeychain.cs | 471 - .../MacInterop/SecKeychainAttribute.cs | 45 - .../MacInterop/SecKeychainAttributeList.cs | 43 - src/MimeKit/MailboxAddress.cs | 1098 - src/MimeKit/MessageDeliveryStatus.cs | 152 - src/MimeKit/MessageDispositionNotification.cs | 129 - src/MimeKit/MessageIdList.cs | 378 - src/MimeKit/MessageImportance.cs | 50 - src/MimeKit/MessagePart.cs | 288 - src/MimeKit/MessagePartial.cs | 511 - src/MimeKit/MessagePriority.cs | 50 - src/MimeKit/MimeContent.cs | 353 - src/MimeKit/MimeEntity.cs | 1732 -- src/MimeKit/MimeEntityConstructorArgs.cs | 51 - src/MimeKit/MimeFormat.cs | 51 - src/MimeKit/MimeIterator.cs | 455 - src/MimeKit/MimeMessage.cs | 3185 --- src/MimeKit/MimeParser.cs | 2028 -- src/MimeKit/MimePart.cs | 761 - src/MimeKit/MimeTypes.cs | 1074 - src/MimeKit/MimeVisitor.cs | 385 - src/MimeKit/Multipart.cs | 828 - src/MimeKit/MultipartAlternative.cs | 184 - src/MimeKit/MultipartRelated.cs | 344 - src/MimeKit/MultipartReport.cs | 151 - src/MimeKit/Parameter.cs | 713 - src/MimeKit/ParameterEncodingMethod.cs | 58 - src/MimeKit/ParameterList.cs | 1092 - src/MimeKit/ParseException.cs | 146 - src/MimeKit/ParserOptions.cs | 405 - src/MimeKit/Properties/AssemblyInfo.cs | 83 - src/MimeKit/RfcComplianceMode.cs | 45 - src/MimeKit/StreamExtensions.cs | 46 - src/MimeKit/Text/CharBuffer.cs | 93 - src/MimeKit/Text/FlowedToHtml.cs | 423 - src/MimeKit/Text/FlowedToText.cs | 177 - src/MimeKit/Text/HeaderFooterFormat.cs | 45 - src/MimeKit/Text/HtmlAttribute.cs | 143 - src/MimeKit/Text/HtmlAttributeCollection.cs | 125 - src/MimeKit/Text/HtmlAttributeId.cs | 656 - src/MimeKit/Text/HtmlEntityDecoder.cs | 258 - src/MimeKit/Text/HtmlEntityDecoder.g.cs | 12445 ------------ src/MimeKit/Text/HtmlNamespace.cs | 133 - src/MimeKit/Text/HtmlTagCallback.cs | 42 - src/MimeKit/Text/HtmlTagContext.cs | 223 - src/MimeKit/Text/HtmlTagId.cs | 871 - src/MimeKit/Text/HtmlTextPreviewer.cs | 253 - src/MimeKit/Text/HtmlToHtml.cs | 357 - src/MimeKit/Text/HtmlToken.cs | 656 - src/MimeKit/Text/HtmlTokenKind.cs | 65 - src/MimeKit/Text/HtmlTokenizer.cs | 2937 --- src/MimeKit/Text/HtmlTokenizerState.cs | 495 - src/MimeKit/Text/HtmlUtils.cs | 756 - src/MimeKit/Text/HtmlWriter.cs | 928 - src/MimeKit/Text/HtmlWriterState.cs | 53 - src/MimeKit/Text/ICharArray.cs | 77 - src/MimeKit/Text/PlainTextPreviewer.cs | 158 - src/MimeKit/Text/TextConverter.cs | 363 - src/MimeKit/Text/TextFormat.cs | 70 - src/MimeKit/Text/TextPreviewer.cs | 239 - src/MimeKit/Text/TextToFlowed.cs | 213 - src/MimeKit/Text/TextToHtml.cs | 352 - src/MimeKit/Text/TextToText.cs | 107 - src/MimeKit/Text/Trie.cs | 353 - src/MimeKit/Text/UrlScanner.cs | 618 - src/MimeKit/TextPart.cs | 538 - src/MimeKit/TextRfc822Headers.cs | 123 - src/MimeKit/Tnef/RtfCompressedToRtf.cs | 346 - src/MimeKit/Tnef/RtfCompressionMode.cs | 50 - src/MimeKit/Tnef/TnefAttachFlags.cs | 59 - src/MimeKit/Tnef/TnefAttachMethod.cs | 61 - src/MimeKit/Tnef/TnefAttributeLevel.cs | 45 - src/MimeKit/Tnef/TnefAttributeTag.cs | 213 - src/MimeKit/Tnef/TnefComplianceMode.cs | 46 - src/MimeKit/Tnef/TnefComplianceStatus.cs | 123 - src/MimeKit/Tnef/TnefException.cs | 126 - src/MimeKit/Tnef/TnefNameId.cs | 156 - src/MimeKit/Tnef/TnefNameIdKind.cs | 45 - src/MimeKit/Tnef/TnefPart.cs | 647 - src/MimeKit/Tnef/TnefPropertyId.cs | 2570 --- src/MimeKit/Tnef/TnefPropertyReader.cs | 1658 -- src/MimeKit/Tnef/TnefPropertyTag.cs | 5741 ------ src/MimeKit/Tnef/TnefPropertyType.cs | 125 - src/MimeKit/Tnef/TnefReader.cs | 758 - src/MimeKit/Tnef/TnefReaderStream.cs | 270 - src/MimeKit/Utils/BufferPool.cs | 180 - src/MimeKit/Utils/ByteExtensions.cs | 245 - src/MimeKit/Utils/CharsetUtils.cs | 590 - src/MimeKit/Utils/Crc32.cs | 179 - src/MimeKit/Utils/DateUtils.cs | 723 - src/MimeKit/Utils/MimeUtils.cs | 461 - src/MimeKit/Utils/OptimizedOrdinalComparer.cs | 140 - src/MimeKit/Utils/PackedByteArray.cs | 261 - src/MimeKit/Utils/ParseUtils.cs | 558 - src/MimeKit/Utils/Rfc2047.cs | 1566 -- src/MimeKit/Utils/StringBuilderExtensions.cs | 158 - src/MimeKit/XMessagePriority.cs | 61 - src/MimeKit/mimekit.snk | Bin 596 -> 0 bytes 438 files changed, 28 insertions(+), 211295 deletions(-) create mode 100644 BouncyCastle.Crypto.dll create mode 100644 MailKit.dll create mode 100644 MimeKit.dll delete mode 100644 src/MailKit/AccessControl.cs delete mode 100644 src/MailKit/AccessControlList.cs delete mode 100644 src/MailKit/AccessRight.cs delete mode 100644 src/MailKit/AccessRights.cs delete mode 100644 src/MailKit/AlertEventArgs.cs delete mode 100644 src/MailKit/Annotation.cs delete mode 100644 src/MailKit/AnnotationAccess.cs delete mode 100644 src/MailKit/AnnotationAttribute.cs delete mode 100644 src/MailKit/AnnotationEntry.cs delete mode 100644 src/MailKit/AnnotationScope.cs delete mode 100644 src/MailKit/AnnotationsChangedEventArgs.cs delete mode 100644 src/MailKit/AuthenticatedEventArgs.cs delete mode 100644 src/MailKit/BodyPart.cs delete mode 100644 src/MailKit/BodyPartBasic.cs delete mode 100644 src/MailKit/BodyPartCollection.cs delete mode 100644 src/MailKit/BodyPartMessage.cs delete mode 100644 src/MailKit/BodyPartMultipart.cs delete mode 100644 src/MailKit/BodyPartText.cs delete mode 100644 src/MailKit/BodyPartVisitor.cs delete mode 100644 src/MailKit/CommandException.cs delete mode 100644 src/MailKit/CompressedStream.cs delete mode 100644 src/MailKit/ConnectedEventArgs.cs delete mode 100644 src/MailKit/DeliveryStatusNotification.cs delete mode 100644 src/MailKit/DeliveryStatusNotificationType.cs delete mode 100644 src/MailKit/DisconnectedEventArgs.cs delete mode 100644 src/MailKit/DuplexStream.cs delete mode 100644 src/MailKit/Envelope.cs delete mode 100644 src/MailKit/FolderAccess.cs delete mode 100644 src/MailKit/FolderAttributes.cs delete mode 100644 src/MailKit/FolderCreatedEventArgs.cs delete mode 100644 src/MailKit/FolderFeature.cs delete mode 100644 src/MailKit/FolderNamespace.cs delete mode 100644 src/MailKit/FolderNamespaceCollection.cs delete mode 100644 src/MailKit/FolderNotFoundException.cs delete mode 100644 src/MailKit/FolderNotOpenException.cs delete mode 100644 src/MailKit/FolderQuota.cs delete mode 100644 src/MailKit/FolderRenamedEventArgs.cs delete mode 100644 src/MailKit/IMailFolder.cs delete mode 100644 src/MailKit/IMailService.cs delete mode 100644 src/MailKit/IMailSpool.cs delete mode 100644 src/MailKit/IMailStore.cs delete mode 100644 src/MailKit/IMailTransport.cs delete mode 100644 src/MailKit/IMessageSummary.cs delete mode 100644 src/MailKit/IProtocolLogger.cs delete mode 100644 src/MailKit/ITransferProgress.cs delete mode 100644 src/MailKit/MailFolder.cs delete mode 100644 src/MailKit/MailService.cs delete mode 100644 src/MailKit/MailSpool.cs delete mode 100644 src/MailKit/MailStore.cs delete mode 100644 src/MailKit/MailTransport.cs delete mode 100644 src/MailKit/MessageEventArgs.cs delete mode 100644 src/MailKit/MessageFlags.cs delete mode 100644 src/MailKit/MessageFlagsChangedEventArgs.cs delete mode 100644 src/MailKit/MessageLabelsChangedEventArgs.cs delete mode 100644 src/MailKit/MessageNotFoundException.cs delete mode 100644 src/MailKit/MessageSentEventArgs.cs delete mode 100644 src/MailKit/MessageSorter.cs delete mode 100644 src/MailKit/MessageSummary.cs delete mode 100644 src/MailKit/MessageSummaryFetchedEventArgs.cs delete mode 100644 src/MailKit/MessageSummaryItems.cs delete mode 100644 src/MailKit/MessageThread.cs delete mode 100644 src/MailKit/MessageThreader.cs delete mode 100644 src/MailKit/MessagesVanishedEventArgs.cs delete mode 100644 src/MailKit/Metadata.cs delete mode 100644 src/MailKit/MetadataChangedEventArgs.cs delete mode 100644 src/MailKit/MetadataCollection.cs delete mode 100644 src/MailKit/MetadataOptions.cs delete mode 100644 src/MailKit/MetadataTag.cs delete mode 100644 src/MailKit/ModSeqChangedEventArgs.cs delete mode 100644 src/MailKit/Net/Imap/AsyncImapClient.cs delete mode 100644 src/MailKit/Net/Imap/IImapClient.cs delete mode 100644 src/MailKit/Net/Imap/IImapFolder.cs delete mode 100644 src/MailKit/Net/Imap/ImapCallbacks.cs delete mode 100644 src/MailKit/Net/Imap/ImapCapabilities.cs delete mode 100644 src/MailKit/Net/Imap/ImapClient.cs delete mode 100644 src/MailKit/Net/Imap/ImapCommand.cs delete mode 100644 src/MailKit/Net/Imap/ImapCommandException.cs delete mode 100644 src/MailKit/Net/Imap/ImapCommandResponse.cs delete mode 100644 src/MailKit/Net/Imap/ImapEncoding.cs delete mode 100644 src/MailKit/Net/Imap/ImapEngine.cs delete mode 100644 src/MailKit/Net/Imap/ImapEventGroup.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolder.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolderAnnotations.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolderConstructorArgs.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolderFetch.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolderFlags.cs delete mode 100644 src/MailKit/Net/Imap/ImapFolderSearch.cs delete mode 100644 src/MailKit/Net/Imap/ImapImplementation.cs delete mode 100644 src/MailKit/Net/Imap/ImapProtocolException.cs delete mode 100644 src/MailKit/Net/Imap/ImapResponseCode.cs delete mode 100644 src/MailKit/Net/Imap/ImapSearchQueryOptimizer.cs delete mode 100644 src/MailKit/Net/Imap/ImapStream.cs delete mode 100644 src/MailKit/Net/Imap/ImapToken.cs delete mode 100644 src/MailKit/Net/Imap/ImapUtils.cs delete mode 100644 src/MailKit/Net/NetworkStream.cs delete mode 100644 src/MailKit/Net/Pop3/AsyncPop3Client.cs delete mode 100644 src/MailKit/Net/Pop3/IPop3Client.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Capabilities.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Client.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Command.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3CommandException.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Engine.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Language.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3ProtocolException.cs delete mode 100644 src/MailKit/Net/Pop3/Pop3Stream.cs delete mode 100644 src/MailKit/Net/Proxy/HttpProxyClient.cs delete mode 100644 src/MailKit/Net/Proxy/IProxyClient.cs delete mode 100644 src/MailKit/Net/Proxy/ProxyClient.cs delete mode 100644 src/MailKit/Net/Proxy/ProxyProtocolException.cs delete mode 100644 src/MailKit/Net/Proxy/Socks4Client.cs delete mode 100644 src/MailKit/Net/Proxy/Socks4aClient.cs delete mode 100644 src/MailKit/Net/Proxy/Socks5Client.cs delete mode 100644 src/MailKit/Net/Proxy/SocksClient.cs delete mode 100644 src/MailKit/Net/SelectMode.cs delete mode 100644 src/MailKit/Net/Smtp/AsyncSmtpClient.cs delete mode 100644 src/MailKit/Net/Smtp/ISmtpClient.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpCapabilities.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpClient.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpCommandException.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpDataFilter.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpProtocolException.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpResponse.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpStatusCode.cs delete mode 100644 src/MailKit/Net/Smtp/SmtpStream.cs delete mode 100644 src/MailKit/Net/SocketUtils.cs delete mode 100644 src/MailKit/Net/SslStream.cs delete mode 100644 src/MailKit/NullProtocolLogger.cs delete mode 100644 src/MailKit/ProgressStream.cs delete mode 100644 src/MailKit/Properties/AssemblyInfo.cs delete mode 100644 src/MailKit/ProtocolException.cs delete mode 100644 src/MailKit/ProtocolLogger.cs delete mode 100644 src/MailKit/Search/AnnotationSearchQuery.cs delete mode 100644 src/MailKit/Search/BinarySearchQuery.cs delete mode 100644 src/MailKit/Search/DateSearchQuery.cs delete mode 100644 src/MailKit/Search/FilterSearchQuery.cs delete mode 100644 src/MailKit/Search/HeaderSearchQuery.cs delete mode 100644 src/MailKit/Search/ISearchQueryOptimizer.cs delete mode 100644 src/MailKit/Search/NumericSearchQuery.cs delete mode 100644 src/MailKit/Search/OrderBy.cs delete mode 100644 src/MailKit/Search/OrderByAnnotation.cs delete mode 100644 src/MailKit/Search/OrderByType.cs delete mode 100644 src/MailKit/Search/SearchOptions.cs delete mode 100644 src/MailKit/Search/SearchQuery.cs delete mode 100644 src/MailKit/Search/SearchResults.cs delete mode 100644 src/MailKit/Search/SearchTerm.cs delete mode 100644 src/MailKit/Search/SortOrder.cs delete mode 100644 src/MailKit/Search/TextSearchQuery.cs delete mode 100644 src/MailKit/Search/UidSearchQuery.cs delete mode 100644 src/MailKit/Search/UnarySearchQuery.cs delete mode 100644 src/MailKit/Security/AuthenticationException.cs delete mode 100644 src/MailKit/Security/KeyedHashAlgorithm.cs delete mode 100644 src/MailKit/Security/Ntlm/BitConverterLE.cs delete mode 100644 src/MailKit/Security/Ntlm/ChallengeResponse2.cs delete mode 100644 src/MailKit/Security/Ntlm/DES.cs delete mode 100644 src/MailKit/Security/Ntlm/HMACMD5.cs delete mode 100644 src/MailKit/Security/Ntlm/MD4.cs delete mode 100644 src/MailKit/Security/Ntlm/MessageBase.cs delete mode 100644 src/MailKit/Security/Ntlm/NtlmAuthLevel.cs delete mode 100644 src/MailKit/Security/Ntlm/NtlmFlags.cs delete mode 100644 src/MailKit/Security/Ntlm/TargetInfo.cs delete mode 100644 src/MailKit/Security/Ntlm/Type1Message.cs delete mode 100644 src/MailKit/Security/Ntlm/Type2Message.cs delete mode 100644 src/MailKit/Security/Ntlm/Type3Message.cs delete mode 100644 src/MailKit/Security/RandomNumberGenerator.cs delete mode 100644 src/MailKit/Security/SaslException.cs delete mode 100644 src/MailKit/Security/SaslMechanism.cs delete mode 100644 src/MailKit/Security/SaslMechanismCramMd5.cs delete mode 100644 src/MailKit/Security/SaslMechanismDigestMd5.cs delete mode 100644 src/MailKit/Security/SaslMechanismLogin.cs delete mode 100644 src/MailKit/Security/SaslMechanismNtlm.cs delete mode 100644 src/MailKit/Security/SaslMechanismOAuth2.cs delete mode 100644 src/MailKit/Security/SaslMechanismPlain.cs delete mode 100644 src/MailKit/Security/SaslMechanismScramBase.cs delete mode 100644 src/MailKit/Security/SaslMechanismScramSha1.cs delete mode 100644 src/MailKit/Security/SaslMechanismScramSha256.cs delete mode 100644 src/MailKit/Security/SecureSocketOptions.cs delete mode 100644 src/MailKit/Security/SslHandshakeException.cs delete mode 100644 src/MailKit/ServiceNotAuthenticatedException.cs delete mode 100644 src/MailKit/ServiceNotConnectedException.cs delete mode 100644 src/MailKit/SpecialFolder.cs delete mode 100644 src/MailKit/StatusItems.cs delete mode 100644 src/MailKit/ThreadingAlgorithm.cs delete mode 100644 src/MailKit/UniqueId.cs delete mode 100644 src/MailKit/UniqueIdMap.cs delete mode 100644 src/MailKit/UniqueIdRange.cs delete mode 100644 src/MailKit/UniqueIdSet.cs delete mode 100644 src/MailKit/UriExtensions.cs delete mode 100644 src/MailKit/mailkit.snk delete mode 100644 src/MimeKit/AsyncMimeParser.cs delete mode 100644 src/MimeKit/AttachmentCollection.cs delete mode 100644 src/MimeKit/BodyBuilder.cs delete mode 100644 src/MimeKit/CancellationToken.cs delete mode 100644 src/MimeKit/ContentDisposition.cs delete mode 100644 src/MimeKit/ContentEncoding.cs delete mode 100644 src/MimeKit/ContentType.cs delete mode 100644 src/MimeKit/Cryptography/ApplicationPgpEncrypted.cs delete mode 100644 src/MimeKit/Cryptography/ApplicationPgpSignature.cs delete mode 100644 src/MimeKit/Cryptography/ApplicationPkcs7Mime.cs delete mode 100644 src/MimeKit/Cryptography/ApplicationPkcs7Signature.cs delete mode 100644 src/MimeKit/Cryptography/ArcSigner.cs delete mode 100644 src/MimeKit/Cryptography/ArcVerifier.cs delete mode 100644 src/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs delete mode 100644 src/MimeKit/Cryptography/AuthenticationResults.cs delete mode 100644 src/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs delete mode 100644 src/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/CertificateNotFoundException.cs delete mode 100644 src/MimeKit/Cryptography/CmsRecipient.cs delete mode 100644 src/MimeKit/Cryptography/CmsRecipientCollection.cs delete mode 100644 src/MimeKit/Cryptography/CmsSigner.cs delete mode 100644 src/MimeKit/Cryptography/CryptographyContext.cs delete mode 100644 src/MimeKit/Cryptography/DbExtensions.cs delete mode 100644 src/MimeKit/Cryptography/DefaultSecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/DigestAlgorithm.cs delete mode 100644 src/MimeKit/Cryptography/DigitalSignatureCollection.cs delete mode 100644 src/MimeKit/Cryptography/DigitalSignatureVerifyException.cs delete mode 100644 src/MimeKit/Cryptography/DkimBodyFilter.cs delete mode 100644 src/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs delete mode 100644 src/MimeKit/Cryptography/DkimHashStream.cs delete mode 100644 src/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs delete mode 100644 src/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs delete mode 100644 src/MimeKit/Cryptography/DkimSignatureAlgorithm.cs delete mode 100644 src/MimeKit/Cryptography/DkimSignatureStream.cs delete mode 100644 src/MimeKit/Cryptography/DkimSigner.cs delete mode 100644 src/MimeKit/Cryptography/DkimSignerBase.cs delete mode 100644 src/MimeKit/Cryptography/DkimSimpleBodyFilter.cs delete mode 100644 src/MimeKit/Cryptography/DkimVerifier.cs delete mode 100644 src/MimeKit/Cryptography/DkimVerifierBase.cs delete mode 100644 src/MimeKit/Cryptography/Ed25519DigestSigner.cs delete mode 100644 src/MimeKit/Cryptography/EncryptionAlgorithm.cs delete mode 100644 src/MimeKit/Cryptography/GnuPGContext.cs delete mode 100644 src/MimeKit/Cryptography/IDigitalCertificate.cs delete mode 100644 src/MimeKit/Cryptography/IDigitalSignature.cs delete mode 100644 src/MimeKit/Cryptography/IDkimPublicKeyLocator.cs delete mode 100644 src/MimeKit/Cryptography/IX509CertificateDatabase.cs delete mode 100644 src/MimeKit/Cryptography/LdapUri.cs delete mode 100644 src/MimeKit/Cryptography/MD5.cs delete mode 100644 src/MimeKit/Cryptography/MacSecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/MultipartEncrypted.cs delete mode 100644 src/MimeKit/Cryptography/MultipartSigned.cs delete mode 100644 src/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpBlockFilter.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpContext.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpContextBase.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpDataType.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpDetectionFilter.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpDigitalSignature.cs delete mode 100644 src/MimeKit/Cryptography/OpenPgpKeyCertification.cs delete mode 100644 src/MimeKit/Cryptography/PrivateKeyNotFoundException.cs delete mode 100644 src/MimeKit/Cryptography/PublicKeyAlgorithm.cs delete mode 100644 src/MimeKit/Cryptography/PublicKeyNotFoundException.cs delete mode 100644 src/MimeKit/Cryptography/RsaEncryptionPadding.cs delete mode 100644 src/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs delete mode 100644 src/MimeKit/Cryptography/RsaSignaturePadding.cs delete mode 100644 src/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs delete mode 100644 src/MimeKit/Cryptography/SecureMailboxAddress.cs delete mode 100644 src/MimeKit/Cryptography/SecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs delete mode 100644 src/MimeKit/Cryptography/SecureMimeDigitalSignature.cs delete mode 100644 src/MimeKit/Cryptography/SecureMimeType.cs delete mode 100644 src/MimeKit/Cryptography/SqlCertificateDatabase.cs delete mode 100644 src/MimeKit/Cryptography/SqliteCertificateDatabase.cs delete mode 100644 src/MimeKit/Cryptography/SubjectIdentifierType.cs delete mode 100644 src/MimeKit/Cryptography/TemporarySecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/WindowsSecureMimeContext.cs delete mode 100644 src/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs delete mode 100644 src/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs delete mode 100644 src/MimeKit/Cryptography/X509Certificate2Extensions.cs delete mode 100644 src/MimeKit/Cryptography/X509CertificateChain.cs delete mode 100644 src/MimeKit/Cryptography/X509CertificateDatabase.cs delete mode 100644 src/MimeKit/Cryptography/X509CertificateRecord.cs delete mode 100644 src/MimeKit/Cryptography/X509CertificateStore.cs delete mode 100644 src/MimeKit/Cryptography/X509CrlRecord.cs delete mode 100644 src/MimeKit/Cryptography/X509KeyUsageFlags.cs delete mode 100644 src/MimeKit/DomainList.cs delete mode 100644 src/MimeKit/EncodingConstraint.cs delete mode 100644 src/MimeKit/Encodings/Base64Decoder.cs delete mode 100644 src/MimeKit/Encodings/Base64Encoder.cs delete mode 100644 src/MimeKit/Encodings/HexDecoder.cs delete mode 100644 src/MimeKit/Encodings/HexEncoder.cs delete mode 100644 src/MimeKit/Encodings/IMimeDecoder.cs delete mode 100644 src/MimeKit/Encodings/IMimeEncoder.cs delete mode 100644 src/MimeKit/Encodings/PassThroughDecoder.cs delete mode 100644 src/MimeKit/Encodings/PassThroughEncoder.cs delete mode 100644 src/MimeKit/Encodings/QEncoder.cs delete mode 100644 src/MimeKit/Encodings/QuotedPrintableDecoder.cs delete mode 100644 src/MimeKit/Encodings/QuotedPrintableEncoder.cs delete mode 100644 src/MimeKit/Encodings/UUDecoder.cs delete mode 100644 src/MimeKit/Encodings/UUEncoder.cs delete mode 100644 src/MimeKit/Encodings/YDecoder.cs delete mode 100644 src/MimeKit/Encodings/YEncoder.cs delete mode 100644 src/MimeKit/FormatOptions.cs delete mode 100644 src/MimeKit/GroupAddress.cs delete mode 100644 src/MimeKit/Header.cs delete mode 100644 src/MimeKit/HeaderId.cs delete mode 100644 src/MimeKit/HeaderList.cs delete mode 100644 src/MimeKit/HeaderListChangedEventArgs.cs delete mode 100644 src/MimeKit/HeaderListCollection.cs delete mode 100644 src/MimeKit/IMimeContent.cs delete mode 100644 src/MimeKit/IO/BoundStream.cs delete mode 100644 src/MimeKit/IO/ChainedStream.cs delete mode 100644 src/MimeKit/IO/FilteredStream.cs delete mode 100644 src/MimeKit/IO/Filters/ArmoredFromFilter.cs delete mode 100644 src/MimeKit/IO/Filters/BestEncodingFilter.cs delete mode 100644 src/MimeKit/IO/Filters/CharsetFilter.cs delete mode 100644 src/MimeKit/IO/Filters/DecoderFilter.cs delete mode 100644 src/MimeKit/IO/Filters/Dos2UnixFilter.cs delete mode 100644 src/MimeKit/IO/Filters/EncoderFilter.cs delete mode 100644 src/MimeKit/IO/Filters/IMimeFilter.cs delete mode 100644 src/MimeKit/IO/Filters/MimeFilterBase.cs delete mode 100644 src/MimeKit/IO/Filters/PassThroughFilter.cs delete mode 100644 src/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs delete mode 100644 src/MimeKit/IO/Filters/Unix2DosFilter.cs delete mode 100644 src/MimeKit/IO/ICancellableStream.cs delete mode 100644 src/MimeKit/IO/MeasuringStream.cs delete mode 100644 src/MimeKit/IO/MemoryBlockStream.cs delete mode 100644 src/MimeKit/InternetAddress.cs delete mode 100644 src/MimeKit/InternetAddressList.cs delete mode 100644 src/MimeKit/MacInterop/CFArray.cs delete mode 100644 src/MimeKit/MacInterop/CFData.cs delete mode 100644 src/MimeKit/MacInterop/CFDictionary.cs delete mode 100644 src/MimeKit/MacInterop/CFObject.cs delete mode 100644 src/MimeKit/MacInterop/CFRange.cs delete mode 100644 src/MimeKit/MacInterop/CFString.cs delete mode 100644 src/MimeKit/MacInterop/CssmDbAttributeFormat.cs delete mode 100644 src/MimeKit/MacInterop/CssmKeyUse.cs delete mode 100644 src/MimeKit/MacInterop/CssmTPAppleCertStatus.cs delete mode 100644 src/MimeKit/MacInterop/Dlfcn.cs delete mode 100644 src/MimeKit/MacInterop/OSStatus.cs delete mode 100644 src/MimeKit/MacInterop/SecCertificate.cs delete mode 100644 src/MimeKit/MacInterop/SecExternalFormat.cs delete mode 100644 src/MimeKit/MacInterop/SecItemAttr.cs delete mode 100644 src/MimeKit/MacInterop/SecItemClass.cs delete mode 100644 src/MimeKit/MacInterop/SecItemExportFlags.cs delete mode 100644 src/MimeKit/MacInterop/SecKeyAttribute.cs delete mode 100644 src/MimeKit/MacInterop/SecKeychain.cs delete mode 100644 src/MimeKit/MacInterop/SecKeychainAttribute.cs delete mode 100644 src/MimeKit/MacInterop/SecKeychainAttributeList.cs delete mode 100644 src/MimeKit/MailboxAddress.cs delete mode 100644 src/MimeKit/MessageDeliveryStatus.cs delete mode 100644 src/MimeKit/MessageDispositionNotification.cs delete mode 100644 src/MimeKit/MessageIdList.cs delete mode 100644 src/MimeKit/MessageImportance.cs delete mode 100644 src/MimeKit/MessagePart.cs delete mode 100644 src/MimeKit/MessagePartial.cs delete mode 100644 src/MimeKit/MessagePriority.cs delete mode 100644 src/MimeKit/MimeContent.cs delete mode 100644 src/MimeKit/MimeEntity.cs delete mode 100644 src/MimeKit/MimeEntityConstructorArgs.cs delete mode 100644 src/MimeKit/MimeFormat.cs delete mode 100644 src/MimeKit/MimeIterator.cs delete mode 100644 src/MimeKit/MimeMessage.cs delete mode 100644 src/MimeKit/MimeParser.cs delete mode 100644 src/MimeKit/MimePart.cs delete mode 100644 src/MimeKit/MimeTypes.cs delete mode 100644 src/MimeKit/MimeVisitor.cs delete mode 100644 src/MimeKit/Multipart.cs delete mode 100644 src/MimeKit/MultipartAlternative.cs delete mode 100644 src/MimeKit/MultipartRelated.cs delete mode 100644 src/MimeKit/MultipartReport.cs delete mode 100644 src/MimeKit/Parameter.cs delete mode 100644 src/MimeKit/ParameterEncodingMethod.cs delete mode 100644 src/MimeKit/ParameterList.cs delete mode 100644 src/MimeKit/ParseException.cs delete mode 100644 src/MimeKit/ParserOptions.cs delete mode 100644 src/MimeKit/Properties/AssemblyInfo.cs delete mode 100644 src/MimeKit/RfcComplianceMode.cs delete mode 100644 src/MimeKit/StreamExtensions.cs delete mode 100644 src/MimeKit/Text/CharBuffer.cs delete mode 100644 src/MimeKit/Text/FlowedToHtml.cs delete mode 100644 src/MimeKit/Text/FlowedToText.cs delete mode 100644 src/MimeKit/Text/HeaderFooterFormat.cs delete mode 100644 src/MimeKit/Text/HtmlAttribute.cs delete mode 100644 src/MimeKit/Text/HtmlAttributeCollection.cs delete mode 100644 src/MimeKit/Text/HtmlAttributeId.cs delete mode 100644 src/MimeKit/Text/HtmlEntityDecoder.cs delete mode 100644 src/MimeKit/Text/HtmlEntityDecoder.g.cs delete mode 100644 src/MimeKit/Text/HtmlNamespace.cs delete mode 100644 src/MimeKit/Text/HtmlTagCallback.cs delete mode 100644 src/MimeKit/Text/HtmlTagContext.cs delete mode 100644 src/MimeKit/Text/HtmlTagId.cs delete mode 100644 src/MimeKit/Text/HtmlTextPreviewer.cs delete mode 100644 src/MimeKit/Text/HtmlToHtml.cs delete mode 100644 src/MimeKit/Text/HtmlToken.cs delete mode 100644 src/MimeKit/Text/HtmlTokenKind.cs delete mode 100644 src/MimeKit/Text/HtmlTokenizer.cs delete mode 100644 src/MimeKit/Text/HtmlTokenizerState.cs delete mode 100644 src/MimeKit/Text/HtmlUtils.cs delete mode 100644 src/MimeKit/Text/HtmlWriter.cs delete mode 100644 src/MimeKit/Text/HtmlWriterState.cs delete mode 100644 src/MimeKit/Text/ICharArray.cs delete mode 100644 src/MimeKit/Text/PlainTextPreviewer.cs delete mode 100644 src/MimeKit/Text/TextConverter.cs delete mode 100644 src/MimeKit/Text/TextFormat.cs delete mode 100644 src/MimeKit/Text/TextPreviewer.cs delete mode 100644 src/MimeKit/Text/TextToFlowed.cs delete mode 100644 src/MimeKit/Text/TextToHtml.cs delete mode 100644 src/MimeKit/Text/TextToText.cs delete mode 100644 src/MimeKit/Text/Trie.cs delete mode 100644 src/MimeKit/Text/UrlScanner.cs delete mode 100644 src/MimeKit/TextPart.cs delete mode 100644 src/MimeKit/TextRfc822Headers.cs delete mode 100644 src/MimeKit/Tnef/RtfCompressedToRtf.cs delete mode 100644 src/MimeKit/Tnef/RtfCompressionMode.cs delete mode 100644 src/MimeKit/Tnef/TnefAttachFlags.cs delete mode 100644 src/MimeKit/Tnef/TnefAttachMethod.cs delete mode 100644 src/MimeKit/Tnef/TnefAttributeLevel.cs delete mode 100644 src/MimeKit/Tnef/TnefAttributeTag.cs delete mode 100644 src/MimeKit/Tnef/TnefComplianceMode.cs delete mode 100644 src/MimeKit/Tnef/TnefComplianceStatus.cs delete mode 100644 src/MimeKit/Tnef/TnefException.cs delete mode 100644 src/MimeKit/Tnef/TnefNameId.cs delete mode 100644 src/MimeKit/Tnef/TnefNameIdKind.cs delete mode 100644 src/MimeKit/Tnef/TnefPart.cs delete mode 100644 src/MimeKit/Tnef/TnefPropertyId.cs delete mode 100644 src/MimeKit/Tnef/TnefPropertyReader.cs delete mode 100644 src/MimeKit/Tnef/TnefPropertyTag.cs delete mode 100644 src/MimeKit/Tnef/TnefPropertyType.cs delete mode 100644 src/MimeKit/Tnef/TnefReader.cs delete mode 100644 src/MimeKit/Tnef/TnefReaderStream.cs delete mode 100644 src/MimeKit/Utils/BufferPool.cs delete mode 100644 src/MimeKit/Utils/ByteExtensions.cs delete mode 100644 src/MimeKit/Utils/CharsetUtils.cs delete mode 100644 src/MimeKit/Utils/Crc32.cs delete mode 100644 src/MimeKit/Utils/DateUtils.cs delete mode 100644 src/MimeKit/Utils/MimeUtils.cs delete mode 100644 src/MimeKit/Utils/OptimizedOrdinalComparer.cs delete mode 100644 src/MimeKit/Utils/PackedByteArray.cs delete mode 100644 src/MimeKit/Utils/ParseUtils.cs delete mode 100644 src/MimeKit/Utils/Rfc2047.cs delete mode 100644 src/MimeKit/Utils/StringBuilderExtensions.cs delete mode 100644 src/MimeKit/XMessagePriority.cs delete mode 100644 src/MimeKit/mimekit.snk diff --git a/BouncyCastle.Crypto.dll b/BouncyCastle.Crypto.dll new file mode 100644 index 0000000000000000000000000000000000000000..c73649fab1236a9f50295cf58e3a6faa14675ebe GIT binary patch literal 2787928 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

Ma~6tm=|ClYjvZxv34g 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¥j7AnUBt01uxXk=8x{Uq9<3+P0HaBDIS$&X2BGG6s_>UM({VY&iW zAN;JH;jY}yfB`o07haMTz)M!}awC%k{Kc3qB7W-X_55rT$1o~~cP`@dF9Q2ya{i4-+Gwah z*Q8{V=AZhy^1c;DkFT;9y%uUpjo?p*zi8`_QH3g}{)16tFAOwK1rR{zIR+*=37fcC zCikj*@6X*nj*i4NiMx$9%xM(scI0>Md@sCQT>O=dC!VxpIG&t!Bxnm%)ryZ9g2dCD z*s&a*x5zjpXIA#)s(Bgm!1lv!J{3!ePL(7&zM*QV9LIs@LYXSG0I5pG@cD45wfRFR zh6KMy^KE>o{U>nX?Xl*wn6DXcfQ9oE3+L0wv@akMkS}?a?t+;oy|(B({WSa(0HlSMSnsA9V=PJ#~=gR(1aZp3f_M)Btm6L4fe6f0L$-J>TGJFqrs+T#zd~6T(c|oVeG{Fl9*sI-2 zuMrU(Ti}EZ6pULUGq|C^5>hGN9pQhUxzAlRRbIy}?(+zSx#SSHnQJ{HnQgc%!|19b%UMAvV9R#{=o?@f!}EGVtCb8 zIW79uOIR~8V|9jCGy28i<}c=;rdhT~--aC6EPLZcda}Y36(B=Yk=7P)humTfobdW2 z9weXTbL69VyCE6>3ZrdL0d!gftNCmq%8onnWN{V3Tv0y_nSz{C3EEe~r|MKzt7VhZ zVieVHJ$el24GV=lPpr1#wE!o42Rffp%~nKn!dDOwtobC-7W51>jyR0=>SK+|D1YKU zBP|++feQ4+D4FeJ^gth@?rHQfx||kE!cvI_dKuM5YlvvGMgVJ&@Q%42ZP}8Yf%SK$ zP*q;Np8~GXlA{8kiBkZDIH9OrvO2gKIM~Ukm5Z$FO1Kh=L7xdH2Iptd7lSgL zG>zL_3Q>6>wY`@DQSpoR=brL(?0I@*o+bOb>K%;NJx;&ZelBwB#{Js&w*L)PjA8l2$MaK>6X_IdPlSUX;D_+2g_va z#N#;X&O%_}L<}5w-_`>^Xyo+7=_eST^r?eGFcddgWV*eL2A(z8E@q~E9ZWoT;O7Ri zI5AIaw2XUpvty7w_db*XYd*Tm3%9rMbfaf(R$8ohPAb4;caU~lxENrQeSkW1TGSY z*iVB9J$5ySX4}~yn&Twe#h@F_LwYmKlXw^!7>E9kxiPL(>PHx-dlrn-dj`e_9|Plx z??0rDTBrEt9+G=Lj=OWe>A;a;t~VV(FVhI-!1V%}IzHWpHtdVWCk!snLl{mq;VnaD z`}uI|C8yJ(U2Zxt?S7c^=nh79^_765VQ;taQ||XR;lPEn3FM8{MB6higuv6`(Fn-M z@8l$&6_8;I{OT^=|G)?(@tg0!sTRjCO^q<%)Z`^$9L!ACV07;{@4aMTn@qSpacj}_ zrCPGoYQR?j)RCC>e8;Mv<_)g%q#|6c+Me10tPQlKGt3x&@tn&G~d>NjD8MT^Su}GTxjBc8F5=jG1`$v zB6cDh{t1iXv8r4f7SdDRg_tT#^!pJV^}d~b5^r)9@|gVwFz@=1G?~OGj4mw|0~?W! zciPBakprZJ9Isn~0W9VPSl-}+0h(9%2CsvK9({#}_$>4kfqC$lQJf9tBt>QhvKXW2 z4Uv2G@6RB>RBcRAZB_)~yMXb5-a@bBQ|L8w4RmtBibNEJ9bjIg03WmD-aV1rYkJMY zh3r6oZAbw_q<((kv4Cr30n|Rw4f(sDm^0nakh51_W{J3CPSrlm{o7#;w zoiA-lGP1NnQdC;u#zyD^>k3|xJSo`D1niDXjzR#4RRbu|4{AKD!|j77T8Hklp6 z#S(x3efv|8XO<-i&b?W#L7YD&HGs|->ZpL$CuCyY4l+$S;u3Hdt;_e+>fhA{1xs+{A-?Unqy>7s_;fNE3x`C52(s7Hv~txtM}> zAPqmW;Q4SBK!C_IKRM8ofgp5`eOL-;i7HXZqyA>(d^gDe3R)n~+-qyJML5-?Y5@w8 zm5xHX2UFRR1!y0<1B&soG_||O*ZcI+Bpz-ovO|%M^}!g7f$7zt%IsujO}f}yTj6D) z`~iHwg$&6nv)jLP@0Pu&SH_!oBn$b|x>E$e9NHo2UOjV$pw$|Dl2i!tHlQsXoXTwn#vq<4kT331j79F4TN$ake2APR1Vt^ELwIR( z{oj3X0$>L`!hI(;vf}szkVQQU*a?4m-6?szKC6iibVL zTjCDE!xcTGkQWR*)k8Ysi5^nO4MGc=XjbcFbclXDLnU@S}1*dA3UM?P4BH z+CV~y;i2>izsNeaq{;EDlu|!wtxKP(q2BOQh#vku)2LNsHx5EhIOF)SWJNOEb;JwM zjzAq9Mz1O9xY`X~d2Jus=RcV#+iGU`)RGf^f@W4UEM65gs#I@i6;=prFOet$?Q6@B z7?!TW+$}+Skh#J1aI*SUlqZTpO#@R=D7qd*NW!17B9)xP#`B;p_MS12i{vLf?I$~i z3K$gLhNQh&h=Rv492J`RDRQbA|7=JlpiWklh=vStDtUz{{33F2Dm}(^55jb;hD0PC znz0DYXhVc`BmhLxUJ@mjP!_6*B!{uNaxm>TTT^E1S7udz=;o}d4#mhrFd&m9szbpE zHUO&C~QahqP6pyZAB|C-H8U%qM{QECb)m~H8c=$+kqlykB zQ9mkAEnaur0g(V(DlKZqwCNL>FO`y`yoYd5xmELCI-aB%ZDI6W%Zs2mNULr|w!0lo zSeOz?C_-P_y+~iiH&SGq0ApDv80g{99>X4<7%Q|#rNhr5t3=XDzoxb2lin+o$qIjs zMvD4S6&EBNx?Ity<}6l>8)_Fd!3wziO8bVC-?MWvIgY7=(G7lsw!^^`6-jDpqkWP0 zdzsXo=;;HTXH`EUmO~jI*=J;9BYXO5EZNPPGyZ+%t|CW9*IO*qrN`Cvo$Dd zG!MyYK?&&#O7e4-DTaD2~`a;3L7)`)YIhn{NKoByAsj5coxx}>z zl!BFv!7T@N@usFmGn9JD5g;R*FDe^zN4yICAZNt5UPQaru9QmevWffAIlK=RJT8XG}&(l9-_q{2p7ikOhzT!+3!juBa2g|Z!UqOH-CB=n~C zF1?NF87pt(*^!g_#=H~C&LUJv*5HuMNDZPNSLQd*-@p!Uv=2Ms6r6gle?O+;MY2ZJ zwXBfRoxq>=PbICp;5Mp>F$kC=PVKv@nb`0EXfNZ#)ND-lFHXOz5LV@H&?}^byOE<^ zhlzG#b~)?uUIezK^X#ghp-oyp(KM|rqOpdO_%TVS*dh8ilO>2ld!H@a6#hQsuY!Uk zcu&tA02y5(=vNYa^?F&6g2et*=EWnBESv=9{H(cOXM&jG`*M&gRLElKByJ=zfI@9C zYDb_oNR$!NSr3diHIYay#-fXVwhT#i?VqNL=xJs0BBB&zu{WAS32r_vv~8@4UeKfZdaTZ-KFN! zNKr32 zfcg}LR$m3*IGsGWlLvlZif5%lo)tMQ#_)o9z)AaWVAU|E#q4CoGaB@vpQF(w4UeO6 z>epI_;G!Dl+6tcQ)su$tgn+iZs3Xt6V4z{HIb9o}tz~7@q3B!sV>-1HW=Y_W3e7jS zqG(*?G+zsJQV-fIkykcI6L8i6G9nJ_TA}8BIIe2HhTd*?7_daOe!HY_ia&bc50DvD z|6>ll8aC*F6lulql{g*&x{Rb^^@lF-u^Y+4%h5$gGg@smP{Ugg8=g{aHH;^3s%>*h z3rf9Qxf_HPJe=uZEueB2Po12Do4dtPd_BhGRVv*{v$hN&5JfILhYHEphZT zK2eXh_L(*@M3xhx2Ydu>u#WU1c;Fz)B>LVKZrfWofh6#RTeQa_Es=-mBQVR-78+x1 z6vMneo2^v!=*m^WP>g>R4efzSBua<2iNAgM-b&2~$!Wo3hoeOpwCz2(dex=^w=+D# z_p$8=^S*pvWjtp@>xKglbXZ%d$xX971I>c=8dm_I?GeCAJXDsQ1C>yIzBQ@-bmJ_T^~>Y2j-0n-oXusY!p$dqg_%F*?h zJ;}(QKWY+1XfveiCP>$vm2_1;fU*W(5*fh=|7KP0hJOcWcwn@|^LInP5_k^#vXzbT zJTFo>xHg;kUiF8zh0%Uy6mhq20P00ZgO>KIf%0niP(y{J41n&u8N-Gf0DC%Y#1s_M zNA)Ic(E2>`c+J*1$#Bigp~*{3LjbQwm&7K8!x2NI8;dLW6GJTg>q}1X9SjD|`C0h# z+>7gWd1E;E6(Ww7!c7RtPrnu!dCiZ2tnj?^G^G+nj@=D@hgjjeco^5||NF>a*9W8# zxzm@#vh2gz6%M`slkg0pY^9@Fi%>uQ zG$Fe66^=K`=V-VS8Tw_0{>Bk?lpGtf`1Sx>l#(Air;v-@ldLH}iK8 z{eiG@z9Y>;$KToXW&h`NS`YZ3Aqd7Td<7KE?=tFODl$mz>H; z2z#eGk9sr1_K0Ka)Z!CgMcc?r00rXQC=Lkbq!4l)BR0in_dNjqQFuEe|AgOTsEFJd zc>{i@P6p#a>Vd@fAng3C-(Tvs>8xq&CpH`obi{tux2wooc zmzwY8F!{~pc1B)x57Ly@PuHfMkvs28rB+WmBd=4xt`r78z5n8Z#slNgwcq(X+V`3i$P0oGtm>>Lo5 zaqMEb^Moppb!vLrO{I5o$ZNb3=lnZg2ir7%f?op%J2^fy&N%@AuV(;%G=M`!@emybK;uAC%Qpt;fq0GiDX#g}bo8LiJF;OlnP>S21JL_Tw@uPTU7qv7c*C zuFP5<0KBnx z)|E5A+PrceD{OslvF0~_zntfxJ!sYfqWjHNShS30c!3u7!y|kJK|;%I=jsHr?T0{k zWHUcSAG>S50r8th;-{J~Gu}cZs-A<>QZr>OVJ7clv%|B;crJ}Rl?J)7mAMEKIXt7# z$!Zz1;uIis8pr8L#2MB&mpOHhlU2-S?*w1x!{~^D>!*4SU z+N^5kyNYNTgRky6b2&9=@&;EcQrJ_wa@_-@!a4pRsn zp%{k;6XWoDN8aeJlVFNKUJ;7I1#i2qwi4?prH2(Tho#-XK8=OJfaBpRmAurx%aw4n zHh8!Qp2%gB!xy^2`DlHMAIL$ZD(pw$LwVr8h$j?^u+*V>FIWj&_0(`=G)SK;_@BiG zoWX7nascaexDHqk0>rcVS$s=Td2lZC{K~9_O0He=IWq2zpj>E{N6$}aYe^RYKiJ*l z>5i)zeg`M~IQ7+5xC&XioUh>^Rp3sS^58Kv2WG%WM1o#@ke$HmIe~qO!`%g#CI^_a|J(jvzhfkmeX>H%xU^8ctY; zkq>M5^%V7&H9x>9&vlxE$U6KWr@Rkw%0qhYTWT;5CLHNt91xfZ@Z%VAc*I4NXM&>< zpmX5II0s5P-Y_mlxw0XOYgG|iFbp{!akO-Mh!1#i@fToMa8OT1!*F&{$2U;3c8bHUP z(H`;zGzOLqhDr7gXr?!zy|fWhuv=Igj+%&!f%yi&1;#9t z#|NNvHV>|herTgBG@l8GYt%!Vr*`|u``N6>LbtH6Fkfpj0PBFH(3*9V1yBf zvu_Q+2MrnhBat;mn(%diRKmD2e@3fp!#_O)naHeBF#6X%_Ng9)GoHrxdvXW{> z!Q}w*3iJ3$OZFRhUTy}twuWxQiz80h&vm1vF<{$wN_5M9j27T82RqY(n>XhRUPhI#|S$Ye_ z>PCx=>a*uvOI19_idk-)Bj)196=DuGc8Iy8u}jRQjoZas*7%5+!;LSBS;;l_sU_d| zjaqsd-Zjj+(%TqPOJ8GLE&YuX)H1(uzFHPEwyLGjm{!Ze#;esb(6~b_gN=Vw%c92p zYAH5;sFqUWcWNm&@LVhlUECN}%TVK3wJd2|Ld#CBRftGsgAYyxIv?M;?pv7JKH(#+qf8Ny&b<^uo}6y-z6SAKy_No2(2x*etAl2p%cPeClaABw?V&TqlaINJd(QV}6wPYdcu5Ue3y z-VGlhlzXu;z{az+nI~zbxlhtgnQ=rW$HaFa+XFo&<{0yAz`(!gOt`XON)C9q0Gf}F zCRZ&qaR{tuzeEO988!6HoS>kqrJKH+!lGB^>C#&d(` zM}CO7$VA+c^c*yv9q?GsBs>a^8?>U-XE{qu;_D;7)14vl3)+zv=#KOkpCJq+&JbRJ zXtSL&B;)L4oJBgXCG(k|$mhn6e0Z*gVX%PhWPGIYBF&}~mPLsOFP@{E!9<+7;7!Kq zgg1hvn8@ZP1(&cy;Y$+XFNworyd^VuAtOK|em8ED6~3V(PE^iNqMVm%lmobL7!t08 z>QJzqNsYmJQ~-;Nd)Q-`VZmH{dEs~E9`#ms2OLw$y#)>~Wd$fQ1-2fKmB*8Cu4V_w z{gwAA9GIE72Es(w4sAG^ez)Brr^p=eGcXr;gGzM#odU6W6u7J z$XD`UJC>(?55m&hZ*y_oxX2mViKOlK!3%HfwBHZ2HXqkY>N6}JuCjnuoOJU=KtTJw zOfTY2;LKhGUSmnsi(m@QOR&>oBs>6z16Irvbcld^`vb5UDXgPlh2j;bML(Q8kPlzN zS#`ollVNBa&Pgo*%D0}K7Aq(0Y&dA3^v&oPo~Nqe1XgGrYzajXf=v6v^0>)Z4sYA% z?ORDjS4XbwLxA9s()BezYn=dB3=r^h4E-_an+Bbu-)NxM4saA`LKE- zPfA=*YPt)nTXE2%I9lzvRh*a;UBR(D2%K;l)7a|-|IINWR$K6Qh=8@}2i5ox3^}j8 z6Sy6Z=!XxPQ`4MI;wE)HRM zI}Ebfl(myEyv1p9Ag56_F3Sk0&4;Xkfa$dbOh_Pz(^sdC%e=o~Uw46jhSvLmFlqgi$-{gFF7(O*Z{ox zz@0DjaE+{43Fpkb=_}ymkuoWFtqu z^zkEw4hp#-$O2mT_)E6 z(SEq~bp%pP7R^Xa79EDoaDufT6yX*aBeQQy44_#`o3uff_%A zQT>S1A`RA8Kw!bNNs@o_n?#|AU4YkoA7$#@)Jf({i*p#c(47`3JbX21%W09oja3l4 zf}ko>w8#d7+0jzJZR};zB%~Vy3wYd`H{(F9eZTmTQ}l>Wq7js7^T^w2ZJx4$#R=Et zVJIUwEq1SiJA6{?rULr#^w_vPsAyJ# zja7kbUO>1BvGTr}2vy=nWoJHKh6*-cDfYW^uFE41{v8dx^7GXxa#>TN z2$maZp30wvZwbB)Z^Mta0B1;f9ukXE6jF`)M`E|laVZh+s}heM!Pms9?e_|RTU-Ms zUAAT%UERS~7?ZxummwaOJK}>2UbuMqkVrS_Px%5;yyaw{%A`IGs6k4F4IhAINR|?~ zm?;hwz3KmdD0xft$7y3yk9d}%lC#r^Qp1XgFI_+UEJ~I| zcxK`)RO~`JkV?p$osml(fRM_RRvlXu%HcR#uaky2SVDGk;k7tp$E6|k*x8=j)0n0w zF3s{gkSf&CBFek|Q8Y>9k~QZ7V3|G`p7lA@49Apo>P(cqhHD~e12-H6@cWMCQu<~< z-uM%W@xsru5_kZha<+K*;^E^-0^^YtkLXPT;&vVxeel=cpIW5vYrwudRlwPojAH$V zrOLcRAq*kn#V~{`0Ycr`w>he9G^y=7ZpU8-nNCX0yHTGV&`)=mfS6joJT>oJ9LLbL zd_@fJ@*`q+m!EFo&48OT39l?%FBZdZ0Wq~z z@@l!Ple$6{A*!1$Nwjona)Fa6O)YStr~DAmo6!Ceph4&j#Db{4M)euhm#D{F$*%hm zw>Xh@%WIZ2z7A+zJ?l9d)Z7?s0D80wwLN0KBG9sSe;Pn#T{A;5g9sLad_bHNb~J!+ z`GIvqzL?K=kn^4a6keG2CKW8tpUit1JSDkRr$6kZ*Ryo#RZfd@pz_0>joAJ&rw2>W#?rd9tRKl|WCU){Kg``Svv4 zNl)$mQi2=44Mo(>U!sGz7VCw#!E>ciSG!~q%=27Y3N-#Iss{T8n%0gKHglX%avf1w zFkX!IKykVytM*vbU=-@x7Qq&Noq63Jq2r5j5YX>PungsA6!I<3X~Lviyl0BZJ+Wb`C2CXRI)=#&4$Qp`uoGi4io@H4jF~(!a-T-4YP-Qwcro||$W+ecz6y)(Ar3KkcEh(5PDd7OSV`tkS5XMOuZL{ry7~F8)#2)= z2A7Da2Gvs-+BQKDU z7PwEl9OioVw^+0@Upq1(*onhCtq?X7`E6K!F6m8Y!TC3D?VvB`*}fz1pWqFV&$0Ex=PYEN&fEI-}t$ zQBCCq(Km#{SHZ*6hg7t6-RJkMsboG0>gmA7{3`eJRJtFjGC)}!Mr04o?C06R!nel5Qp9Oa0+P#ky+ zL7%nOlhIFbc2YGxVjWnWPb~(-{L{01k@>{gu-EVRXM^}{4>xszMoaL8@conpZlyfl z#o_S~WNKe6d6kqm%N9>y% zfsLFg1Ka<>)PRj^ppU|o`$4D0!b2CMO#2rsro~@8>Ci1Or~ZAi#nAz-2Idn+R+o`d z>t726wS+$bONaOw!;IAGzrk7?GYQLUm*YeuA>JyLZ?WozDdX3)$~>WS@QYE04V;%$ z0&;$Q9QinTTK*jwcJj2)C;S}5N)l&?fQsJRegqtb1H{vB2}ot6OI*?L1IO};T+i^n z7!#9$5%19SQ$Vq-0*cZW=~Nr0@RqnTO`B#AnZbVO;&ECe3nO$Wlwn2k=ummtU%`f* zlcXuH#S$w<-VCl3!Dx~z;0*YMEFcI>nhpPq85>um;?STF z-2mS0~p`RCB1vkcLw2$9P>d{0Uf= z%3&RB!yqohTkT?(QF29UJEro-HDLZ10KkCBwJ4d$Io3SRk>&SD**UV%XO@wL-a4`@ zBSrluoHIri-NRN={bN?n;0fb@yaMg7e5Nef5dR-w)p^2At!6t)|DQo)c}rvL6+{&@ zN07!O`3|Q=Gw$N*#`%B*&)HP(on7E;Igm3N%a^Hm8IHC}5Ga$dr1+ST9=k@Q$7aZ` zF}jr|6Z<*tb`I@zym`ryAKM48@^Ig?19gW6h`Hs<=Bs>J$%>Xi&zVS(5kX0QJIH$JYmO`2KXv%)vsqn?J`-Pp!`hR{$!nc_W=ZuR$nm=2yp?KRXDq zetIxK#?7rro{=8YsqKL6XuwukZH})*CxjOsgdXdNW+Lo8{|%YOIrR7?*O6!{#PHOs9Jlo3ilQxtlk4sg-kNL@YDTn7Yyy7~BV~Ugy zIU)mGua=q>;IaXg`}YZ0+2*4tc(gbJEZzPRsy=z`$FKK!@=~BGTH55vyNJ2sZjr{` ze5og|Bj!rhy=AoY^ev-hd_zy)QrcXax?&(*9LUs`K;IYs?=GaT*qjw}^A>0&%}{}4 zAyZjB@x2@>r*&JAsy#)|jO?Aca1p99$5QnKyt$LYnFkhwf#`HRCH63?9gmHCRKp`_ z>TckOf7I{z4V+C_{f_^Q&VtjyN9m;pr*4~mGxR$a^%bBF{fPKTN?To&PoZO`dchV!57#0vsah{mMJA!^5!x1fwB$Fu2VKQ@IN}vj zDIC~sV+I^s3noxSawg`JCDDEK=5b&meK3e&&QVozPIxxs&939H+NB%Lw(9WOA+5Jj z!pOJ9t>(i>kz5oR{NU@&0ex;2nu3ipDDROgOqio$<&>vMR;u7|maNJr+yK&@E4K%c zb0xp(9FwnY?l%MZwPO;v#OA`5C}g8cH|WabZT{Fm6sX!5PbElr(pMl;Ws5T)gUXW; zCp-sHI%@MINuyc|XGsIhJj2wbcNJbtVfqAYR%xnHkgA$IxndOZJ`$0`v!npk?B|4! zkcf>|RjX;WL%*l3nV$7t3RLo}7wclOl`56+6~{z`U`g4 z@CaZA1=~H~LpLH91N~7ojv&`XefTMBfkU${B5MP(&6G zT9py)A^_=oBBGPahFjnQg_+x#eSJFy(v`JP2lWa7gPUN?olA+~gHpTRmzt;d)v&)m zC2uQm{`(L{;fZ zcvK~E8UgPyq5!pbt3C^B62!Lf+v}tNb|;M%))VOnUpOWnhCYt-q0Uae_ zP06ca#YHxW$X5ay_HiX?G=74WOFBBAHn18$BaoC#S0km#YYpL*jZ6P z2{|7=JP+j>B3^0|Pryr+5`I^5B>mMl%+RWdbqx54>(DIAXhPm{%@fmeNHrR|;?%hl zoJ0gwHHcDmF!>-i5v7M}HEJ$w5@R1Tdk*Go6 zqv&g|CpEzmJiYpR)R$^agvaglaUdPC16s;VQ1fvstAkVA6eE!1cA|v zsoJnRXi)DKL52mxnpWw@cJLnnP7y3=pMdG;#_s{-SiP*$3)XPG8g_+-%mwgeIw2G% zqM2b4W(E;-Vc$6q2;`sg|8hrDf1cgZAj8VYngxxG@KJBv;*%LgwtsDhBS5FG2Pn?k-!I?XKVNslZ1nE!ePOl&*%QyFlX((cd)3R*v4RzIp zwcUT&pMtDGIR+>hb^%Ql*3sOrHJp0gdf5wg%ypQv2E{8;&XLEvx7X2TO zSgJW6Se5EOM$hB!HwTi7#F{%|Nfl3%9_E0g$mnU!D~ct0z{X){`erMcN!pC{IIjr^SMgcqtpvocYls zAzKdg%k>L+Py4btRhg*<&WQNH9Xxr8P*lTs`dQHwip3#f$Y~T_F~ajA_O(2gf=%fa zXmy^li0V*3ya=S}>zOgnv;F|EP8B>vAV7SCulWdN^mpnI%?9TYM!$X>5KJYZ;BD2I zC`iT3cSvziC)I~u`6lxnGJg$5`nn&-03~3;d@iPci>EI=9SE! zY(8varzO3v#r_(ek1Xu-xDf_5hK{&Lpxsc0@V^7srxDi)@LhQ+HvxV=Us)9aKX;A_ zGxQG)r}BiEWA;Ym{~hk@gr>*9R;ZOHTEYteC~uCG+{Nosc!}P$E)6?2@52t$ynjfY zuF^{{N0S_YPS;qXsX)A?+Y4!A+5<3nVntt?*3Do_2lvXd!wp`D;$+F8d~J(RKhCX) zkPzOy0g)g(dwTK`HV}p(&Xvn;nhrTbNYxyH!Fp6A#;xpMr*67Vm2yWqBSZJ#K6_Y2 z-0jIEVC^@szHBa7`xCIfTww)FgpK;28Hvsk3e+sT)+Ud=v2>Q*RjM<-w!$8%$x@W}Qn6Td^adGmJC7+a6?_9P6}bhX00` zJg3j&b=WSi(~bKVh~fl?A~D&;CDkuEMGiGz!s)ToFqx3EP*^RAm$cmXvz?Xq4jj|P zlJG`A6}VDz6Mu6gzvzW2~@7VSYZ`Y!&D`;dW_T;67NX@jt&$l0F25P(B){~W;|VerAY<$^E=l=)#A+fzNub%vk5SA1MY&FZlTf#)8`22478_f&Vd z5K)UF&4_*lqBC6MYr?W5yaNf#QNqW{V;A#4RL#TWhD!XYnRXk!h#~r;0kVN_k9)_p^1diW|fEWa9);W+>eO^!_=UOoI526Haw4V@{Y z5BjAQ*eLg3$bBBikQk2F0FI9ljxwZEqL>d?uPCFmP0Aaq*#L}tS;Y;Y)rFbS-uz&?wZ^4QOHsG~JruLDt#=KkGpAq2#nj zR3Lg2Kli~`XAuX>-ry{Zp1Swt8KJEA1g-IEK+tdE*fn7ZY3;;@8OP2E(bmnQ~iHOobW&+>ip-+x6n zqhER35*+cYz*v%BtOWLzJ=UiupzsD>TCE)lZ5*}5Sv;n)+t2Nmq7?W7<_2CWfk$ZK z`#HRxnu9`=iLc^xm3qrbV_(2Ne-LsTA4PN27rNnw5G)tA`c{vX7$daPXG!R7we>}ZS7Esb!y zgJs`xL>9;HfoJNzMv^}>iEL4!C)i2x^59I;Fea-v#gmm>EtGIxE|!thPDqR`Xnc!@ z;1Wv?!OI$I=0?>a>WFknzILdJw`?27NnwvWHsFf}a}=`T`8ZmalU+X@UI{`m(kx%L z(kuz_5_Dvqoj9qdq}IJCmMJ+OFrNjO%^PqY?oRnkZQR?+7xwr1sS->UMBsNYXR<4U6Wth8%S`-;Ky$r?!xUWyVG$dza*7_;w*rJ) zH|41crk^4X#;aoZ2rMfLX>chQF*b$buk3>w1!DRd1>B?Ag10Hll3-4WWUT;Nz&eqy zUgsummo+es0pWA%IEvcsZYE2V zdCW-LIFs6le4Z+_L?VG`9rZc%c;S&SypT_=NuyiF%bcHegDh#s@f(F55NmgXd317I ze+Tk92YG2z=CuuxmmtrNkKVz(smn@XcqGQ2K79~Q7Ydo%27J7Oh}YBYvAym~c&zWc zMKqe6@cTJ)ZV#n3XiM?)a>bmr$_uHNBMlx)%o1zLJL#n-UXRvD+hYo$mra2A++_W! z$7F0ktFCAe1k`{HvZeGG4%Dpc2YB!aN_Uj)!f%WL8;~)b#`{0Lj+^x>s}(cam)`Vv zag$xV$MA9EiP*~y|;ECh#*7As1Qd!JY4YL zLFC`Iy~8K+Q4TiiZ0zZZwOK|g8ehmNRfAkG%zBfe^bjork-*CHGYRhLv7zv%QMUJeBJ z)8`j)LOl)sVEjToB7RSi-&5uHH2!wiN9Rn)d8VEw{p4|9e48r9^>n+ccauoJ6?>Rn3h#$;e5bZ z$z!@q1IOI-FU3Y*=i*%D`0V7~;AqU_el~ZzIRN;+Up=;@%XdeYHZm8G=8*-?>4!i~ z(ECw@M^EC*!9LgLBaB}%-1^OCdJyO>t@FxNsxP*zMoNcwv*Y8>8F}A5;Ie-N9MtTF zpM}>o*wkE$uZ{0Ie9tp>y6p#+yT8cJ{ASC;`W>iAIPC|vj?`=)mI_=h%=jo0&G-yU zrrQknY~O*x_C&|GuqNwb{hsKuY<|xGE@t~k6QluK{{GxM5O=l@M+&Jae1ENJ;b5C? z!?!>8Z0$?G+3HF^^JLG-??eB17ryu7!?Y_?*h~Kh{Ft}>IAqw5W!aB@w9jSUfG})9 zQ*)W!b+Gxo3u*4eN70fG|1bS+MZKt%X)g0NguMmd|Fk(*`PTn>!0-xu)_ei%yYN}l z`dicbTeI7L0~_5jlIkmw9MD+&qNoHjjhf9$|e?~;W7mW2K33Hw#Xeqq8dX}9?vsChl|br%r}?6ZI^C1y4~IByWJKpYi_s)1)F&bPl=~eFTyvCk8<=c!S_OZIRBJaahFGF2sUsYJE(IWdnkkInf=$JPAc_te80h$w9{=r8&3Cu*qPsKd04*#HCgsw`u!4V zXZz;L??7RDqT|m7tMz-L%d+{+g^MT3|EFk!C*mU>er4M8@tPKQkH<{TV=K`8{$Kcb zL>b_l%N#-2V}0Fqu=&v6+IjTIDu}_pRURP;cM*r`PbB z@E$~&sub3r?~MGTSbNr=Vc!^@zkXxwBt7rr8^jIdGgUcgeGRF^Irc9?5eCeFiEX4N$;6Y4N56Y4N56XGaU zebuXp98wSq$d+)Wdpa1Z0l1rB(v*6Ih;sqY*vGu!SL6;*Rt>OEC73Yusu|DU8WoS@ z)cAEdZe*avRHLzAJbP|jqTbS1HIMgMHIE6clDId~JLVOs4HxMdw`FW6KhM(%IR1)q zF1>RJuIY-mpoDolmx}dlT9=7+6RpFrsyyH5SI)Lv96PCh)%sQ=-x2r*C17GFZ~yfE zW>Skw8J_cc(G3qqeZa1umJt*}c7>PmQ2k${L-lmM1ZAX^!SMx+2N|;bdVAashV_Gy zI~diMvyfq@MJG*h|3Tey!)+COF<>lPRMyBU)G|>%6jJr{#|t+!aMk!Ek^%aW>e8F! zorCD#8pSO*RJR(%PQgxI#Y0L+SfGUep)|g*w#0CwEV1z_gQ{t`C-D-r-&_ggL~;hw zXD{i(&jMVEmL~Xsbq6EIx~Tr?v7E@HE^ul?09!bcA@g`4A~fC@#R8T_Q7vF;B@AMR zPMYGrL7M}y4$)Wvjp+~Jw|yuh#?mXv2pJ8$$!RfuxEWWnjc0!w8aP{w6Y z4~`V|1yjWLB3~yw9LUs?@yyDVC>u}m3Se${8C}O37X&PL;molnle!Wx;V>Dz`UDB( zEN`Qn*8*s0kCIyPc3V0JWZmci27S4E)}p-dDujbKpJ;Nz4S>Nf-sYEX3*Q23@4nuZ zja#D}!Jwk@M8~CurtFv0oVCV%_k~C=rkl_ZEn&0bK9C3?AU# zmx|<3c}p5>N`V(7H4kzrQp;x$BR6Q`?NC$Wko{1d+vp6pG6F|7pvGxFf}+|r6of}< z?F(Mw9BMqLow}C&CY`zw^}<00c(dI4F{ZT#wY5jXFQU2g(&!7E^!5*e0)YHg)3%YF z^fvmn>nNjENy~L2B0N7C;X^ip%@@jDg3n+;AgufNTWA1fK9$2!YUVRFo~(&p{6XBG;fZj)71M z$AZJ#;b)@19aeIaJc=y9&KCX@W(cWmh#($@U)O|}50O=IO&;7^2h+A1{BL;q)_7(To(c#AFr{=; zavItI2#keSt1y83Rjm@9(`9wQt)&QK`-Hz^ZNY}1L8zmt8+=%6%?lhYI!s8LSbkd! zXRf@AK764t%H&JwqSsu2RCrhn^4hqEKN~NdRnsE!mZZ#8Kz)KT#W~b?*;`1N+U^HH zn|#r!v>AniHu=i7(B_kn189?B+XzoMh7izZR1|5GjwEf0+i9_2rOohkk|k(Vk_nAM zGRl+Jy&?4UlwMHaZ}IGR_y~X6__5SLX;!pF{WAqWqT6&A-C9yXqf6Y^N5q>Z@n&wf zbvC0XlZD-Xcce$36ssW0I;8Sugrd z>;j77GWdwO!6BP0TnD#^4(Ec!s4Inq!{5VFM-i|-L_e@;)wh9uPoe|-O!X@Z{`5F8 zs=aE`LEy;( z@Tq9w>hD|}B=L-l%i?_E!+KWy=wDj|UbkNWmZ~wso9Gyv*&EjnZw;t~w@Vwp}5d>e*SM0+hlJu^?#dK~HT;+ft)XUAk|)wJ-M z%-Sy{9NkW!;tRvt?Z{vFd(;z$@}NV4h15@Hua@-y)?m60C_=n&5N+myCAlxlxuH^Y7l(qox%cO6I%qVI+0l1luXG>y|@m74{iZy*PXkbd*y z0wml?tMHhmOXlnhV!GLf3pu6uXt)(>GSNW4n(MBpH`=KL<8LO_hrW1egafQc-uQ&z zKrqmoo(7+Q=_wQWbT;n6nU0W5A_R43pYF@({FBw%rlGFmVt99WLbx(+Rqfn@@wjti z?k%8aO81tLja@kI<9V46H^G+Ha`X}UncEHut3WXNA2i@7l7n2n{Y7bDY-(hAW}LtFQL&se@#!L zvJ*XE9Lv8QgCy5X^8*#g7v!Wcr$z9agX}{egm?HM{BVE_KaFL(JjgCXY7(g&*L2|G zszxdCuAIzX6v_=h!<5m4hSpM2mbNzOG7;h0X@vFtChCieH1u887j%0eAvbGz;z!^? zrK2v7S%Nk0H3;a_9+>T@bQi(;NrJ@{Ttv#5M2IPZ^v4ySOe$FVU@}Q3=#FTL=9a_+ zJK(do)q@%w1D{Te919;{xYVg4V z=TpPp>G+{bINg&53g<%Ay~`wvWmuj+p@E7jB4gOt0`{B+7$(9w#8oO`!4Xh~ zUzQQmo472!LE#MxAV!%UHEJemB*004(_)t4C8U#o)HK8bJ(1=|Gtw}?4L2JaV?N;) zLt~#OjhzCYP8vHEK0ra?t-9~+ps~}KoAkMVW^Z~D6FV&$;hF4nr^6;VkdXAYPcarf zonVK-0Sk<2Ok7MiPzi8al4Xm4hL@3gFvc+@@xmaaY|Tg+r-4k7p+DvsUQWmP76t#wgth;@5 zs4+c(xWUJ`{pS85aYlCzq8->7-NUTYj5E6BFSfo;_;Mgw!NM8c8XhTORv|nwg^Meq zaL(%-vvmw3NWk5d%+bv2Qkmo#9X;YRI$}W8%pJzk*oH&0aTyfiqD{5Qn+#+KM7?&_V5+tp&smV94qY*c^TVIXWsxl zhUi^{nfxPHL@;-q*FGl9P<}5Br@wNPg~c~!T(+h-d-wJAH9iO&}CAGv3h)22h z{CFZYdO@nsoZm``LN`N=HFiuSjNFsVy&0Jj?D5ncR|j@uP0#f9doc^4DgK>JA>A2k zM*6iU5sb~qeHwo1OA_Jq)9_QA7UPB2A?xsZ{OF!v-z&ZncuQ=gJ#M5uvyt|=kv8&5 zT^s2rL+nwQrP-(uT-ho2K`*fQ>&eh}vSAys;Mrnio=Tc+tPlf;V9p>%(cjP|`#eHe#(WCkeS@l@Xo?Y*YM^ zFON*>#SJ!}R@&*ljALa-=q6*75Ndrw|xXRGYTedY`5Y`ruy9AuXN!2|FMQ^~M@8>2l zPw%>V;+=RvlO%wFh9_^uH3h8MSiTa(DkqC;F`-}0z~(wfyFStFwjm|Dp+Ntx@FQLi z-81I&f|ta@n+&i#)>u>ay1n$=H_uDwRs)ri_47rTEBI&oX!}ZpVAXMj7`p1%N_(b# zEsTCDNgd(ES^JrAR@1@ffv@_4#uv~(8$V+wJq#G#;iQAlAwVOIL%U9<7~BDmDqv*R zx?K4$#1FuTVeZ=3qRqFW&CTLug`NiEoJOvP`9=;d6QAe!dN_O)vZ-xNZ+{T}^)ubE z1&zfo1q|zsh5*07klw~1wB?sEg2@|4dAw?IKOF6=k=AMPch}WUixnFmX*?UrRl|Y4 zh=juI)X1`&7E$ARvUJ4>^^cK}OhxTyG1O_%ST;F)q0njj*^-1v5_GMtBL@9gpkRP& z8_*>SpMu=slqS0d&KlobdyKf{L|i9)AJHl_aVVPQnbK!Mz$JjL;W@sd8lhmmt%alo&Y zyn=6(6F^U(=q3OXd2)F4hxFVyfczz`!) zV_5o=(|!)hbdX6QZ3#h%fV~rWXg9^|LAOPzIGh&gq$n#|%z#bi!x%>T&^)k#8B!Z)j^24J!rHWW?jFP>`pp_1G?q@C1UliL(~`Un z#fI8V?+)LCN{J#c`;t7aACrYld>gOJLrJ=1XEy=dzduv6Lw?dJxRiB@Qocfk;YKZr zH>o||DOQ}Q-(Z{gK`KF~z*Qf@QKn$UX%Q^gYRK@!{kgUgqW4pcyoTl+xHlrb|ag;+~V)DLK5 zwl;ogN=B4DHJnu@{mZB^kurQa)GBp^luU^A@94rh&}#w%8J2Dw1UpDkBPeAl#c~Yg z9&sPWPzXh1UmFcRc}od8_!O}Ok^TckO5xogsC@^o#LeP^??6foaaEsGPveZ2*@dr? zGKMtvZ)Cv!-D~N5P

nXUf?~#|%&`)U3oLixn&vu7)@z4*H5vbJu9|SoFKprXV!~ zMLLm6ikb=YLE{PnsEIV5$S)>iQX#ur0a{XIRK}`2JH=^aYQQTE_(fclgHxc8+l|Yi zLT++5W-JW6Q2N8`QN~;Q1IO1kz_p%CHvZUBI~JIMBr#ZkS3r7aoey<#3^ieQq7B9t z*^wtP;vhkk3*$=}rD}l;ReC>*AJx8=fa6QY?S`=si-zL(uisPdQA z^IB&NCqJVNP;p5Zw-An)ri!b38kv_zwJPqejBFko!%Ijv5%GwiYd2D?hixNG42?9* zd|9huD0#e*v8sD1^Yz*ScW@GW>_KQ%d*rp%V71VwG?SFH44jm2{9S}M0??>Srz zh+ic?OQ;T}CWabzp;c6JPLhL0kk-Z6NB-@PVh!Jvc4hNp+SkE39+UPRj#X9W!B-(3 zo)$rzXjL`Jj%;p`NGN%%qlAVArN2R=T0EF#v1$7>N8HYlEb{(~hcUG99K)CmNe*MP z>~0@(5KB4V{ji%{$TzE*YbS3@wDF5wIlaI0h-Kp;gfL$ zGTOLGJlAx3-cn=CwVj^sZS=%hXfpYLc;YB6>A74yab}nFTq~aIIz6Yv6S@qOF<&B{ z8#_I3MQ0qj@9?B&;HJ4Un|>{-?8wSYRL#DFB1zSAlHR!X&Qp5K-ZqQ}pG70=WJ1MN zHihCuhn8Lj}6unkh(D*MF{EVMO2bGYma7%oEL9PMA z`;by$&!mnA>`+jIdycgD%k^fSk7oC0rh?y~@;%b8d+}PsR3BetNarSt!7-o;sPd#E zE5mz?pXQg5{m$1BKU*DnF5Gop*^&*)BDMJ)77tFJ-$Q#IHVcCQOT3`kI}L^ytm%TU zGYZ`qNfLaE?sFKP#-7Z0N+Lc31~4?*w;62{qrnR`Fy0dJ1o2M9Q;+`iV1Fi%7_#2I z1^D1!bYA^NQb@Y}dK#Jb8))DyvD?Hcz_$NZ{nYU$ab((W7L!j2x8Fi*i@)`})9h4&qxqDYJuMKjoOZ9qrf2;cU*d z7dZAXdk>Oh4IUjh(~Xzo@oRV`(%XN)bMF51Hk#oBID8gaDLEBSo<^6Z+HaM_{KcR_ zm^Z;>g*Q!Ba+9mT)%gn0Jo+2X>75qi#XFX_!_~3wU+=Vt+xQ+|T=obLCoXg=*ADx~ zd$HDnFf7(u(jqOvll*eeLtwDPAuLmNQAJ1n2yK8dq|>Ui5%e+n7OhTKMw$aJ^fgoI zgs@BdOI)miSax*Q!4P|@=d@T&{UunE8`oX}9}eTOSkY2vi5JDv(^om%i?cF01V09; zb6F_e*G8A+IOyXEno1bCuqx(f&P- z-L=&r=TIdzL#~!+O*%Dn8Gmed0YbG2Bz1N*6G}tU@2_uk_b?$Y+eejdqOy3ptewkv zL51^C3LaFrYZ$dSV{4`kY2ePrHmcoKeJ_pyHNUn0FjtDwevZ{J&hl*R0hpawo zOHk2Mn@GzQsA#QC_;CR$I&2euTz^heqiw>E%g<@*w@vu{`y1f*G-$+4`29Ei&g=Bc z6FzFrm3YI3_e6e-v9gGQ#Q?+2F1V1Mc&H%H3Kpv!e4V!TdlO4@g1;toyEO->l*|k4_fZE!}E=s|UC7vXa(;~{lQ?QG|GCy!pV*7*e zQGcgJJ7#?vSdyJYRpsk7%neVZ3llzb;EW`pa<>ST3aRHye_+#?Dp0&oRRHjj$lJWq zZGnU0dcbtNFIA+P5_4CTN*!`Y$|WuRqn2DBO@b-q%?g>6y$Ed&rM#F^2v=}F6f$7R z9kmTQSm>cx97A?u6R(jF-{j_CbB0#XIMDz}Y2$$`dPXcXCLEdFa>pMQ(J)Gr`63`S z_V%1g5ULj?2Jxh$ky%fOQwgIl`8>OXlaIKRtW{2{3#lk2Bgz~Ga{HDHeQ)I zRj1xi=-zytJB6h|?#)k+qB5S}o2aipPL=*VAdQkW9+QfPRO(N~>EOWY9CmEyFmfl!cZerpzn&M5m9s+> z0ms;}y6T$7A>g0;VNmaIPTJ+OOL9wyi^rCDTL|WV?6^_(&-5|B#IIip4iJIUFEZ^p zvfS*yvuCNLFPsEEP6uM){S#=6cU%Sg0S6Dii|>JnC*tX+V!Z&tV9LlZ$rqlKz^>jL zU};(PA6;apqB59)%3eOw_$Bl%F;jKjz(tpV=x`CnM!gI1WTMK`&*@cTxQc}jQw4mO zYUFY|1J&)Kx!%Vl)EP8g>fa{I-v#yhFY&TSJrC-zagYwbrhfrgWFW4b?gni3dK5zE zHQFyC1cuE`aN;%EFA;rzYqWy2(`hoQ8bNxu3@OT?md2)^AcJ2+MI6zk+(l$m*F-T% zD1X?w&gqfxW<+wDjEXJz^yn5o^BwQwyM=g8lTJJ#@?`?+MN&^LhoY$_Z?Q=vK5G(T zT0}UPo*Fvgr8ZM1_*P&r08%)fBeH3dIywP5W?U|(aq*mvzyeGS`)D?WX)@|dTUMVy z3x=jQ0?YnbKR6Y$ej)=s58EU2Fv19pFGQeNWr}TLtxc4xU9i1~r64?J1mqR!u~^Y+ z7nXaJSs%vH{^N0}i;2q1W)joJ=M)7)xCKX~P0jqk}4NAmHva_K+Tm6_M{tYdV z)1+!7`q~o|BJ$MU7Zx&cT(f6MdG%7OL2nrJT%8rh{^yEQe;G)k+jcYh@K#q5kFUtY zSSE|z|L*wRaliIRT;IMC)wlEjD#XuNA(mZiD+KD`caXlL^Go3!_|`IZQn=G7HQQFk zSl;_C(VL9q*2f`$j^(!OpjaOt2c3!i(Xl%CBubpUr5DI2b&+Sqa9MbpE>_XZlhx9y zK)H3A%p7_uI_Rkm11YGI`XpC)F`Qb7RT2UbCh;e`a}t`H+m-dkuWHGzw2uxfv8`un zelK**NyzMze7r^#s8F(R&8KN+-IQ`B(~ z(=0EKW&C((o$|P7rTG?n2fpqEUt=W*MRL+)w(4sj!jJ(@lfN!DT`+(3w*y{WHKfq~ z`Wz)aA7e#Z#@N>|UsGFbo%d3U!3*nE+$JA{b}!K;qXQr-ZmV65CsdS66bNIa!#De;nS~#2Xl$%!intc8#Le2nhoD^pS@!P$i(Yd z--UXj!$*hNmF`}9`{T)EGJ`Ir)s@lYZz3ZG+44LWWh~DA97opwnoN4JHrGigvXqcL zW(`Erp)Tc8_x~oY-)A7t{G+w>AOsp!onO%7h4<5O>raMMMR&qq0>DO`u5s*M{v_H1 z?eTsEfasN%x))Ve#}x!(06pJQnNU{wFZyJxkGGqf=LwJ=!V7wddne3k!>Jy zIH&R2xCb?H5!uEC8I4;panY)Ej4L-P{11{ITLV!tv(m|j1UXIqawj9NRt?c^ zNoinCz>~|!IoDFmR~NZ{+Mn}$Y{c4S>;A+;no$^aM%t5cS=g0K;XmwwL}57=fnHF{U6}cn_f>|*&n9mVKdr7 z%D5-?2Cn-023rK(34>mDnz1mxebL)2bYZ%FHqs;T_Ysh$=pUI4H~bTwboggFUieq> zSx#!L7~KB)Q;ah=iV)2}`ra^Knc*p0WBH6Uu6f|95_4g;!ur`hu5$0ITZ%!K3@5xku^s zTGw>Mlprqv*()8Th@&O^F5!&t4$#s==q#1$Ivk9A0&A~%$s*tjZL zH+a@OVTtJWk$+#_XEANm0oq0_O#4{7i<_zO|Izf^)DY6!gmNnJ#B~8@2yn8tmUlBg zkjq+s1@Z6zI~3_JZbG2m+O@l zM+&(wJM;e#qu86pKYL{18MuQKk2Y&^SklDH)w$qoq|M3m-VC0x_E#68@9F{{J8C@~ zF=J3~Udk<6{#?%;O=QY1udj_?Y52(TNLnIE#6{}8xneIi<_dxIfF7yli_&0Ob+0$u zu6)O8ki;~47cth(WPw=9WTA~o#u>lfyqE0DyFqVkD;wp!z%7jahA#E(wNkX#1s)JS z1Vy+I3o-tB(Q-w}Mb*X5>tx4&m_fe?MH3!JE_S_iWnjymT`qRbq$@r0HBghuRo7cw z!(8eJjs~!nb7e#NU(7knT!_S%G9 z$iI&w{~-}s&9xbVI!5-eyD}Y|iFEpL>-Xp(4os@3MZ-==c-v|zHem{es3$N?&XR2u z$JC6~rVNLW?J)Oy#})5tp8vM&PyZD$J`UkUe4%N5ZlF}zn795}YJ4QXcG z`4~^9%cqU5E8`c3nb4DPCr*IPH*L(zM4IBaZo=ODp} z?{Z=yG1#zY4n!{L)V*-|SCZ;CTrBOHeGKA{)ek1PQMv{AQnhmMHNziOtIxI+pazIq zbqWx*>J%Wl&?!K)r&E9^B41^WwMP>dMQjaFTga1h$ZuxfD1!k^Bc6wv*J4zAVm+r@ zt!FoMkzJU}X*%i9qa%qWcHN1>g+7CV*Me-`8pd4fUWhBJ2NtH)G=?!udT8B=9h?tj z>?{g1jN0TcMv&&^b@dl>SQFF$=~vNalK|#}A_8HSv0)_V;9}`a5JaodoTKjesk2Z{ zZ$aLrh0pslEYLNk3*@(masF%v>CI3L*!gOY_&P8wJ>pcvRt-E`vkm6a0E{{E3zH>6 zQOhU^VI<8DOzI0k6o>0I9%{q|@Ee~Wyn&up(LjijLZ&r=OmXop>|G)?jx&goP116j z6qG=8b8R>8a$qLKPNAU1LsKP(t?=}4DLA)Szp?nMRCAUpG1z<3P1rs@4QlF3Y*15e z<{Q);>v1pQe`?u4CX<*fgws_mWn4d7o5LyRTCKp(%${AD@>ZO8o*eEfuDaL9k>CuD zK_f8R8uXyr>2si~`YswdXohMttmNE*A@YG{LPZz5|Fm-EibY$_|Ez+7FYe7*0$1$- z>T}!(V^P!(UXL>LM`l!&6YZ<0Hs-oy(O>g<8R)!~^*UDjJVz263ea|G+q&E$-cYjk zxw-NP)>>=2G}yiB3mhC1l;qJmV+et=STDDyt?o_$sxXJqjH3-N;$8h)Efvm#(o z5ZqQ$;})*WnyawB&a%D&yq*({>(K_i3i*6IZO|f!2#rq<9WPu+C%6c4_tnt2^>fit ziUh(g#&DW+j8MXEf|~p_7tv*cH-RGCkzN3~H@FzQ84wq@$hsx@)V4I8x)S-|pVJh^ zgszUPr;((Ek~S(B9W27VG77SHx5=di}mw>w8GLN=Z`-HP1KO!C_{N* zbBJ=tq+W#f5RWTI?O_4bZF|V?y|jmv%S@iQQG57S2uRw)42BDopH(_)pG|iICh9r~ z3gBf&zC(`%cvuw?^M=kzN1yq&{k5RYX z&&NwUI+A`bf`ZGLF*&~&#D3_pBWtE<@a1B@8XLy1AR#=-;+L^jl^*|1k?T~PY@Q5u zkp}L|fKR_q#}{&esvUfgVQ@b{7j(VUa^#nHvla7h0r#FdnK>s@uFXBV0B2V>>ZNRM zxi;|t&;=hxcj{un=S>A}cXchMd*+;6B98bgJ(frzE1^y<26c)0lI|^jW-2>1vK>5q z2;0cq;KR&Vp>azjAzY--jR99MG_Cn=2?js5Z=Iq3Q3hogcByw*8DolnjLcRCovd4% zo5348oa#ZByOLMEWo5OaJbqsWmu8H}0zjo_{M$tgkBgEX^xKuaAg|y)xzZ+F8aKF= zzXS4H;%{jR(|McrXxooHwiv+a+5+ZB{kh;u*bGwHZ;E zY@D1q9;Zu!YZ1hwufeT&;Q}OUg0zu%az~YL~zSqCAb~*u?|8S+*d>SIKkHd#P&bpk1j-S!jC$(J>(%$WN*wL z`T%f89C751Olbo8aYGK1G8Z_?H-*8Pd_4kiIJZBzkr+Iz3+O`60?^mta4@MB305rl zSjLB;ui&3HEZo+1;jeHOTDGsdpmeSp1j}Z*=uSFA6<@ zp*JQ%am=fyaZ?-AAH_sVf=i&Rd>0~~i6144<%2TH0m@LJ?7wv=6nKV|z6}?ju)H5YuR8FIcB9iq;5s!6B>EIgh0LcuK_Y(l{r0Q4* zaw8#8Uf!O_%f}OW3BJa>ppFpTm0x9?bwuhG=O6FEei@5P zbkRu^=J;Z)Rk5~r(7SV3$`Vb~%B}x5@J3OE#(C=NXu^hA&MJtSS z@C}d%S5PFU18hZ-ghMbAZp(NsA(v~Ca=8PfC>LV8^4m!+3~iCi4RK}tdn^*Sel*zM zfx=!AJ^@jn%K^DO0k21rLKI3hln~3`I}-d|o8<3K^7j_Sb|uaPbdA(p`IQ~=v958< zqq;`&`2%o4!vd-sBLpK@4BCW~QrNxzKOigB8|Q{iD}E?Y$d|Y<25i$jV&LLhe&3nk z_mfF}?>6!zw%g=y@VMGyVoDX@M^pu1U9cTJF!(viUKg2OV?cs%@m?GoidA&bP^=c$ z3)jG9PKZU9Xml-{`1BLs+IGKU0@^0f*5F@U#I5bIw2@K=yq4A(m2w;u!Pf*GFMJ{$ z@O2o|L3#|O%V4?;rz_!cd-Se^Y_Cho_BZ4a?Gfp?@*A~BBTQNR2M`9L^NjNOS43hyiSNp9r+hNBC7-Z}q1pe($a`X4 z+e>q@7O|mON7C`aqu{VrA+)tsAp&(uFk;@{o#6e`3Et~?^jq2&#&eteW&1+9uKez` zeIaVxzA%DmU&Qti+4l!&Qa6>@_{fth@BFsbFv1l8MW-4Zyo~-*{3RWC8E5( z7cv0PPT{vJxh*Xl?V(@jU78^=Rlz!HX-maQ}IO_awC$@1u;#1!v2B!EB7fh+l_ zpNJz@qd2X?%((Jfe~cj5B+QqqOtKdpMLCjhSAK09@PJYiMXU^CbWcKB^~Fmf2`>q# zmtzbk^&iSoiua4BFG0^98;s~NU`OiE2qfgzd8*EpXCPfU3G3Fg0?Fey2=If-LUK?JQloMRwMQJ-wf}15 zZH&y*rP6h3$LWSoBbOKl1j`YU4)_2d)bw~lkrxStEB<>hufY)y+t%}6XO)^NF88#X zmNqyDB*LdF_s<|5k^ON-_Issq9z<$S!z5`4V<8Da8qYL*kv#bm`fvrlfBmFpv8`h? zSAOHR&WyESM>15@M#oy4Xu&XnQA~=`b*B$R7Do)3X^IS6ih~_6P-ad*xKk7dQwbOw zRg0^Nja|r_BZo{?aAHhSE}onp*~z*KF(yP13_lU0Jb!GR6WoB87z-IF-M6W9f+O)W z^+#|xFjN17{QgmX|0KVE#_u#+FANADS!y4X>ba25MwR6CWIiKx{hQ?(sm4v>4mQ5C zfc_&3IhKW7EP@_8)05@UQz-DTKNZ-~>p$oT} ziVZTy36=myE4cO$&t@iX!faQZ$=_qYVz>+mOKb05o5}O*Kk!A#o&s0J ze=5Eb+Cgtof+NXx#1#}smiSgXYVS@~Q=tUOGI}IgK{wh#*~~Ijr*~fnN!W>j7ZQpV zPFzgyZbBHRcc!Sv2h)NK^;FR(ka7} zgrW?^58ial_W|(MZa1z~yRoNnJ*rT`TEs0L2=VL3%I|UboqiQ_kWl-LOpL2ax~vhL zhwzXm8UFCljlJIT;L6&pMVmH1|cGeH!(dlqTO z&}|BRDWQlXp>T;zzl9S`Gyf&fpI%o7UL_&Vg zhU1Om^4_r=ZbIJgg?5^txTk2s}%ciMaw z`}_41MlzGqc_E0531U}3lSwHM9@0`zBbxa>2QV=BaynkvfJ2EBN|#`|0zVN=p%=Ud zOtA7!n;>!H+b?z)u()vvzXD(+bFK-cmvqIyGwx9jnYD8mn0}2`H^NsE9c`?adeJ@rR5DY*h>m+AI1fs1oIbZ# z$abucumIj-WM?Rx2UJp^o1j2%f&vYQ{@^OcmwxPR$Oq@_;;erq(jAVAABcReT-S!( z5#madh46*%wZjN+YzgiK17S2;_^sz@uuiYRXOCN5?0YTIE{UR{g1-tZqD;=GX%!ZYuv;9t!1VR*sKns1AJG@YPH+9fl?idYP8^f6E@1 zYe);z1E-ZH3XQKvF&CyQ+!L|%eGw@yoFOUb<7-F*1ts$x|3yKAAOq=2V7|yr7}lbk zWjrr@t%-ht!6cY5te@fNflE~V8$gvwT_|z9@OAqO10BIZ-D8|-oI=Z!I6X3r&5N*t zo5}2q(JD85J*lTXj1p?Wacm&oKy1XaE)n9`Utw&se8^I`kixM`EN^V5^(MxJg40c? zC#Vw@2lu14ES^9l)#7X8iW$CHsD^K042=Ec=D*;pD)Qj#>NX`5bcq~u`z?lEYw~Iy zSPK7>^xW{RaMC#LPcL>gXk`Qi`k8-*CpwLb;DrudY(lL|Fv}TrerOijx|#<{%ImA% z5En9oACLiBe4eMlo)-t+$v~Q4+{6RgZzC;j_O2iVWiKEwBkns4D${xlX`zXEORSse z@a={-aW6Nrt>1 zr>%thA?X|Mi>)NV@a2~aGK}ZTuQh@+ZoJ-0it&03@r@z$cSo*pDotVjb9@)9hV}R{ zTP|iUAzlEJsAaKi8!@!*o4)f6;E{i(K2pVmj3_SWudinmZCdoP*wsCa+geleqJTd$ zKw{{gfU!X@<}rAGOCV;u??YLJmx5E&|98Wo0O_SGewD>j!QVL|$6rKp^riHifMqx_ ztgn=6WMg0Y^EOK5GhKi< zXDL{bT{^X!g2NvgB`^2|QcE|sTxRkZ`~m)jY1zutcrBm`@MK-3F&6>+#s?YYBM~6e z_#6RWi~!ljcM15Z1wcFB10JY+?-kbzcZ%zW?-Mr@?h-c}z8|jenoFV3$_xtku1NpsEet(GH;6Y@B1;Rw>GEurrlr9sc%S7pFqSXIuk>V9BH?z0v z{zU2hRtkr4rEreC5V(H*cLF6PEyb6cQVdv@3Oe1Cb#Q+KdX``RUx`T86l<+t|BZmi zrGlU&4+)4FQP2T?{b2!-X8~alqB^%xq{Tj#oT`olWY6k|aHIoxWyO4x6N^I@>^IZ4 z-%M1$v3xL=aZNX2s?h;kP%uF$RL#`yk`JKAh}IE=5+*1aJ({2tbeW*! zyl)wNa~`)cBn;8*S*~bpJB2OC9s2Pfgr#h$|?_I`M7U&9!7*C?V^N9+jP-o3dLw%OD!1V;M2Ls|(!l z3j_@|9s()RS2u+#lvAr5^no1t?#WmCIYcE|{YAKGjZdf6a7rAZrOEO8@dnc_j6i5I zN3TT=!vk}6!Q~K$yfcX^FRcPDwj6963b5?uT!D08R%<$WH!U``Qm^JPXC5as{(G%U zj@%z;;_&VnTvWxIX4ZJPBWv^z_z@adDvQ$wFaBi200ukv!~8;Hz0pH4q{IJ4zF=U) z7creicriR3jto$qiqZ}aqeig5;M~y$j=kM*3K;!)bHjgsTJB9BmOIe%a(6mT#n;mq ziaJfE;n$xaYQto)=aF}EK-^;69SJu>IXF$Giqj03hQ`OmXPt(P0taH67@pBW1g$}g zJ|DqUq?)BedG4J~9DkCrrL4WwGfkW#S}-PViB2qanj#@d1QSSd3OR0fUFp^?Uv>V5C5*(6@@L20IOnSM=9v6>n^*GfbgX`i6u7zH5y=UkzMBd7Ws1VLGn&QH(RmAltjz|yq>v`kB z8XV@PK1g1tuff;(EjfTz@KUQnBx6y{M7HaPLW5S|Fl{2+c-qY5rl8cT+ib!Iw;*Z; z_2vYd1n%=yj-JArx{@5FKt4{lK7tf^l`w^a`(pJXlY256?XHa8qwA!;l^TA8qJTY& z3jVXhQ*27azT#PlWLtuI?a!~*Zt7UzeVBH)wj+7yMrqvV@VliU8R)7mCQ5d4jtk;( zEtdl7Z}!tV&Pj)5SFh;*x~PxzZFKJH+swX2_mGb;u;& z1_slaHl|6F_3g?8&%%14n#M&l)aDW) zk;Zu8`7T_~1^uVLLkoQ8Ki2{eHr{K*B68tQJBMb7LsU#ou) zV4{fyemZpm=zfIlq_q2<>2G>mj5@yuJ;O<%dH`1rIfh?E&Tw?ANw@xC3>P@*;sxIT zLU=P^v>3%5oKACI7PKk^f08e0a`8()NAHXEp>Aedh zjGhJlj%xsQr%z?84%&>3c^zxGeu?a)U3YppwrYj9Fz|3(hz-}R9}h~g?*#flKyc!V zx4ujQsLtOp^1cnIR`pXRsdfa-0KFJSmbdW#2eBf4u&hV%4ITYB;-*HlD z`V7$0J1rz&!G{p=OgygB9`GCnoX{cQy$s+JJ?$}m$pF6769*%bGP)HL0OAdAgYcZ`hw!+M7kmv5Vod*NIf~+|gf{t$ z5?W&lkf_{2q+qYkM{cIS#6avy6<~CMxq{>I?Cp$t6e}pdrZ-cksGsT7uTim{0sWLG zhi1QqP%YW7M_=Dyj6OW4Ir~j~%`l*GIbS_G3^~z)7T7mC!0sdLh*rF=?)k9ggspAG zdu|8V8H7Ex74Omxuqza^ZZ~iG&@~ig$ho*rkL$r4{cB9bgZ1 zfb}ezANQyZu&sn$!56Wf4Q*!EV~_X(@F!X75jGQMYEqE8NFkVLbK<&#_n7 zjlEJbu~!Np_KNIduZTVNio|2DWIFar-o(rHJ${=kKf|V>L7n$2{lQD4cAu?%j8;%I zR)b7sX#$OHe~i|pXsj98%1{E0Jwc4tt!S()xytGU8vBJ9tuL*3tU$f#$}usbw0lnK zR@0Bk+ii|cTlx*Y<~AbTSOhviK+IAES}Gv2AAv>$L{=lvS^<%*2y}vg`08B*;={`$ z^5|Uz;xl+7^5$Ix;wyL~GxtNuBGB6ja_W!qK^5eMb8PIJVMD@TJ`^u`<^9pd3>$Qv z;2nFhQ-1=$+C0g&r6E7~F*>69-TYx#a2Cq25kEj{V!V$PLt-4{2e(%-{?S)n4f>J?>xcNl6>#)SrXqY_!*SWSW%;j5yQ( zFUq6damu2T3ggsQpzh#8rRjsg{fMjd7D&;%O0VP?pAh7$H^R+`6YI*zv65yTQ_+!yX44Dt(q`56e#!<7kttUwt<}@?y1Ljzw&tj8H`Pl z?~&Gt?heP}rQ-BWzao*7=UNMQdWwea_lb=(#63aX`bLCIzowP!4~Pikm2UlF8bv2K z!7OlwVL|;KC5tPG+i2fuXp0Nlbd~n&TWSB0h_J5dy7jk_xY$m8kT>T{`5v_{Wn;(? zyn&Ieu=>LvAri{_$E0DQu0qrMf+Ew5%FIh#q{TXpN+N$&OyNC3Uk3K!-TcOl;&5~t z?H?(jQI3n6KLeE7N;iG(PhnY)J}JF}?r{B9jDZTc%|z3~ zK=+^zV}LG;pc!ZYlP)4yra|hr0U2{m!Mh-w^k|=^r}WJ{6K0N>3h2ByqC>-$jOne1 z-jy)r@p62A1REVt)DpruFw$x{M!!7CxqXXFe zNWl<4T8bKS_fs!}OgILG2~F|JTuGs^2rTKG1TL=_RO!k_q{3`WQF05&ZDakzs21KL|6Q5AxFvPw%B?=I8iTMZ6DRI)N#` zm6gU*AUgS$HLu2FWjx1qGFxELAgUy3NV|I*()$?o(G7nASo+)#QxOkO@1V=q?B2~o zsP`iX-APzRMfJ2jI)p-vv*;dV*2o2~}ghAxTBn*!>#OX_DQG&l=FESGWq_8ymK?Hdaa9rr64RaQl8(d8?PFsGOqTs2rK zXV&vN$G!|6WF0RwJ`&-^>fMzsVj683EdL{Z+802 z&^ei3PxpBXJ-%$o&?CQv+}a%W$vkjxy731VEvKEC6BTyw5#}alpf3yhQVO^VS?c_X z>hhf1i%Q??tH0M5^ct>6^UN16LPN(Tp2_j=Ca5tcZaDVRwO&uJm&&zVi=4ymrCvV~ zj8r*k?cwzvQ37r=Tsw>tGy5u@`5AO^i5o~H)rpjB-b{$*M^ozSs^Xv%BlLC^clCDR zdbh4#R6s&-;pY91Qwm7dh~bWe81fWDo)t;z)q5dxQVW} zprvKlEXa#*P`crdT7BhZ(QNDg8v?sp^pbj`87!}#emnGs%PrKLI&X8U^oV8`oZw?n zgVBYVqG{JF$86klQsndHwz+}Hfep*17H{5@465K5g|<<6y$^beFaNlAA z_5>eWlJssnyv?mNrKK~Bp}A9rstbxWY;YFo6!G4~@=DJvJ4u~eJrR=QU(}2K4nYoX zP7Hs`kK_X2uiF6+wFCYa0QRD!fyS6^>5D;I`T#fl4WOncJUVI6fm4!}8>%M@wB+gK z4c5l8ptqP2BPlZKu18tRNoA&GURc0UT1i^Fxl^F)PLsJPub2K_y84lPi5wD46OKGZ zCfuh$)0%MC@f*A|54XTIb{ON$v%_dgNdPYTNAJreJ2lXh9X!v0SjZN|W2!Msx5a2E zaj%y^{W)W~nTx=L-1t2jQ;A1ZNQ@RRQ`81RY&j_mNhu0c93w=@vF7JX|Ni_uPEKh# z?wiraD;UYnZdDbC)<{_O$mE!sQ8(r^%F#{i2jWtyonWxp}zQJ37V2Q7kPO zeX^9O+8;g)La3aBYu1K4El1NPS&X(nT8q9l&i%Z#=mW75UE|XME!y2_MS9(bkr$To z<6o2(RJ4k+lxnU|^@aV!LiKZ6X)4gpaxbDnXl3~5$OK?sGj`EpK zErVWNqhriR5Z{b3*XS6tJ10ZTypz-6e3}_!l)el^GO<>aRk{U~9|YB{P&X zuup?;k|=E_>N@*24J%?FP<4PPnc5tODt=8+YkHfk zhh|O6#7`ZKx`BFZ;^Bc4umVF-1!IOHR%om|qi3VF!iz=ttTisa%8p*8(rGk4RdWi& zz5*8%;{jj}^0Wl-rjzPw@FlXZ!e-m2()B%%fnF5Fyn2Z@Pcx#T)qzoqp zI%%p1HKG%bnufV+S(#Hb8$b9K7{;*fCgfMAA+UdvT4xb-jJG_}J@PEVL)Jy7Xl)ZH znN-=k@m@j|J7-QP;xSwSthvVf;x+|bb*mNXv`Lh=o|B=i39Vj2ok`A zxEnr-^so^07#vOw(yRT}Ay+5*ErLfDR(ixn#o6%Gwc1xLC?Ix!h%=Gc&Kx>$`U|G- z+SJl_6>Q(-14p|qj%_PL`m$3?5nHe_AxZgouTfD#!5+n2CM8|c zxjC?9hK*qJC6o`UKZuruioVaunhSmq!a_#6GW&38mID>TUkZvF0> zc!FjMlo6n%i@|bWKGmUvW5thLU2FDf?NCXt&r_C6N?}ufW6^D*=bVS zYyz}J*w1jEWKzDY{!WucB=Y5l$d?}wKZ7yidL~ljCoc{7nUsqv0+f`9)fF1d7Dmui zEwnP}x8R{Y)&9;;9R(YwCn{%{O|rHpD=M4{Ao(Ld2ws)9(Q~WM^-9>7t@I7U>0ur^ zkn&Q#QC|FDc~vaOw6v6G-kdu(g(!pO!_!NUYi_++xicyh)tD@Dui2aSb2!6Cv=;fuE}; zO6w%L2~AXw{!$;0`>2#5HMh8CF=EGcwMC&tpP{@M<0uAIIM~6)y~!-0X|23wTW#6a zK8Q)0D7L$7^~aVv({BWxbO8!7UBj%zCJtwp>#r3rsiRdsOFYsXOM;S=p$tQb89758EbuMSCmBy zL@NxP*S`8Yo%_9jc|x0mIpa!Ctk+t+w$hj!aA(Ocr4C7P9885jmrA20>Py-CMIj~S zhV2L%1jJt;qL2E9(BKssiApS85kHL0{}RJb=OmY)^5OoE^WsD~xMcHRWkq5^SU`-a z?O8OV$HY8TOHGvR|G`L8?eFR!CI#_q%hNF!ETAFuvC2Tc**^N-_Ymp z*MXCfQ<E#&tW?*Z-g{ys6YolN*&a3HeTidtTZV$Oev4(G-@h~ z@JO%yG*9)~poMYJ!nUC9IH?0>D3YvDkzcz)h0zwhI@-Dek}#W#WKoI| zQn~u6*TFWRjba4Xam|VNwfSpKEQisjLkvH>DWWZ2bAc7MER$(cZpyHZ^h{-RE-UUbL7)FAJk)%OT(4d0$%57p@ zh)mC0FrzHEU?y54x)$Y`Ag+lK#Z0DX#w~UWg5sHONJ<1Xf_eby86m3v7!)Z^S!H@a zo{6mtY~D5m#e8i0)LHVTY^7&2{Ewxk@Z>9=pDn|O0ZSu|yQ0d3IfB6e$FF6Mz)5lc z693Fbsa3!>pR4&fDDk7(dI({rwhqVNS`kOu0Z(WL1nq!B+X2;fz-T*QtR1kDfOPOE zG)4M~bSi^7@n5L#X0qWdvl%NRlj1YWfxo(BJO{%wo>vbh7z&MdUK_PKX783{%b*_w z^bMip-0bFJ9_{|E6HQxkgrkj#?%tC4c&G-5y1*dO`$NVwcx(zd1@W~waXjCGa22eI zEnESB(y?;@P39d>vEoS(e8>QnAJ9NL7{K~0XS5}fxIEA7e!{jTSZRoOVsl9kcal;C z;>}&+!>pKW?t-3hu66*10p5|}&XWWUSjCG#T$^kdoDp$~^|&a+X;L6ykwp!MKGr(w zDK>m)ZQ)8eOTHK!Al;;VYm|90W`=-hDw(K{Q08fB2aOD+81#V%ghY;_bg6o_g#)*7 zYDv}&TdSur0f)gTkuZ5rR_%60os7fI7S8K?<3B}zunf%T<3F%W z2{#(gFg1eb{tTsD3#A+v?#v?Jr91%8ya+7g3T;Ps?0XY6r0ywJ&I^Dlr8`j1CUre-EFd4m`tnIgNL41-@>AT zu90R`RCxAAymPhWnk``$V-d*~m9Sw{S*QQ_M(J|0f*nH|(CcAEO4PvPa+G~^RK+v? zeWQYSqKH{C)kl^<>b=Me*ky&PjhK&Q{cjVMscojRCnb~+$OgVvovoUi<1qO^iS0*5 zc8^o9KYqKHUiW$^P_Zu;i7c)xBC?!_4ArF%+XPjN`x9BdS`m@ut;q72a_`Nvs_g2` zgHpa1_BQ1({zDd^NTkOr_DLHdE4VT@_vjBwGMfcB|d5E=`_S`#zbhHm|450 zT=V5Kt7cod$>)#up`V57AoH3rqPB}0(K=~Vb;f_BbA)ye3>vj9Xp{$4VKYYFEv<1v z&&DGj<~Ovh?N^)f`Nh@m#Tdd*#e0;cO)-s5+`gDS%0Zs3gGi3Y09YP6VVrkIxS2zy&SUSpXrli)NTsNd{cr&^DUew;q3RxK(zCjO`3s%Ip*)c{S?~e z=~ZZT^Db`ZZhQkOQXhwRMYYRslzx#iK8)E{7cg`iz{kpis;6K9XJwGCtX{bWhul^k zRehS@_)Z-4aKG`x*cbSXpT)jGzwxWsheUoC`Ly>fO%4Qq;8+~{Kc_#!{y_K$d~6-B zxT6jGR9>OS7RoON(xZGaa+E3oFOW(jpW)vcq|n zwFqn3>Np+)!Xx5uHQOfI0Z#;gmKMp_`t&NuSN0T?!ovVI7UVOM%T{P?n2y=^s2)^d zg~@J5PGZp;?S(XhOPG{@f#lRAlQAZfHe*aDs6ba~9TKP`%|kzFtwU^og#Jp1?E1&8 z3&Aqti(X{ptPkg;xv|>p_~L51?q|f#ZCq)9O<;$Co&n_nEG+c8st4k*2PUI&9>#~0 zpA)jX{LJ{Tl(v_`8>~9--aH(fjU9){w;@)m;2{1AQ+Rg`LC7{`YRx$DV6@0scuutz z@&hA4ys4^)9m5n~31ZGUg8?z)i4OR3zYEw&AwRHogF$crLzgESG+!82FH8i)GJp}F z=$zPED(o}z$enm14&+DCa#6ZbaY}ydqMgWH<`OM4JP*${NbcasSC=TiB0Jzj1C3ir!|t*?iY%qWC&6I_ z5*La~R~7e&|FViJ`SnvxRO#!{ZsEx;An}|ST}+5R4~`iao}mLnZUvV;R9y}}PZD&q^H#?Tm8?3vsSh8J&s`t8xMrQP2JRt`p40?HO zlFH3Bp8oJy1XUOLZp-nuNHvZ@s7A*dY4}E+6UV5k`hp{%+Tl})8oUg1&*9M$OMeL# zg!+>prxiHWv;x?}<q$^W#kkV_0X=uRi9q$%W&3S1Lyj%K%k$c+HhZb zt`FuM@q079*&I*7^ybRBNqlVG2s;hqvA#?%R=37)LWO=h@ZdZ{oNeJVje8Rez5SFV5{kej{GL&&QKOh=97B>u~& zm5nz`ba3;YU4CT|gKUk)(FZ{0Do0Nh;2P-A%^*3&LlG=Ec`kz=vXNly6q61{Hd3y( z+BBRC;o?Y;G)@idTtwrCtb_BT<2raUuB?L-)2xF_q~tpIY6wKuX`50@nk;Qu zq2-6H(DDQ6G8j!f473Rr5a;-hL0n_c9-#=1#<#26N&~B<{D2MJO2aH*-wIl83gzvZBtY z6oy)_oaymvLQz&G%$&rHIxps>%4ie6hxsi|X+ixXvy{2100AWfa}DqWUj0kw^x zk(HvTY+*F63R&B7RxE?AwahdQhCyZm14yY+hheazQ4?#_CKAU%8YU4fUV-(TNkl80 zx3ZToE1dPiAmj{JIOPj>@{H#7>izrCZi_vXPCXw*#=q)HJV+_b#=rGv0k1S!KMBqb zRLCWXRv%*@A_~f?uc*h3F{LijzIiQlA1e-cSrFKzZQQ=XFF!aE%+<&FBO0V_x{e9& zGx6MLsZtM{ikQ&5sK8@AG97`m>r9H>fF{MSL{g?~Ro4@RO}j(&3&kUfWDU1@s1i!X zk}8Ilrky7t!(9y$AyIbVj}4wwK`8?-bqxyuf7-aq(G_D0iOHx{ufB#=M(OGIzPmxZ z!a57^t}2YH;w~4KMb84* z439`(wiF?s;N4kcZsnK}Y_%y=vuyA)rLDMKZ`;)9BkV)csY8Hw9`GtX+_v23_;X#Z zpDk_9z!1qU+gd{JU78%u7gzl#-3h3 z89A48cmKf;waK;Zcz!$J1?_;70HFO$8b+5c1utA1-N!O0>DpybNu#Nd`OBb$$IGBG zmR$xV1m&p#QX%4H(Bn{AGDIbFb{P~0_2^X_|4(SEoF=o~;h-MDDl=c!w9x#}pd`5C zny3WKUlSGSHYqspNpYE0JF%=t=)7e`lH;w)1k$pEs%WQ}92Vna`I#&%<;(NhfM zNP8dZN|IJxC4Z7gr&W7~#_Fo^sMJRUar+hm}x9l7b~6)2i^r(akXyGBCbp?bEPog6Hty8QZ8r ziHbBq7)xVV*C2;ku$CoRZ2!x~M9uaIA$lkDG!{Zl)Mf2mB8nJ%%r^wEx11O`6(xmT z32~GZqRhZRFzRQ-$vJjLO$ZYQ)lHS$TUrC%R%$tsT8`Pv)#l)fGiX-rRB9bEsKtan z!cUzD-TBPFMR%Ynu-9Y77?|`a3$ELl3{xp~iJr6|mJ|^>WKfO%K+X0l1?Y|_iy;J^ zk;I@wzSSRl1WKi__VieHruI%4x-+Di$conN^02odRv)|bMri)9gJjqKrCoI%*r)11 zXx$=_J}%`sxfUJ5smc8+P^-jqZB?^3y)PkR(QYMHoV?l`_bIaEdcA?kUXdgh@_KPi zfl;f(0-ogk_6~VBTD6z{;YA>C`-ju`jZ>9yw0}6g9Wd1n*a!go2a>Ti3+FsOOWl=Q znXn|>(_|dQkI@5^cWkNPE)1O@tMk|$dpqVzd%MM-YVGYLM0z{Q z)lYF<>~qMo#=&V*#6;ggd07(zqc7+Z2WEuMTk0YZbGCPBTwD{ZM&md(F_4%w6EV%( zl|3mtdauR98`_$hSsS7Vk+NYx1!@{EVOqLWJUNj(L5ScCb2Ul$1QE?Spdw{_xb5yGo@xPpN&%3%oNVhY|?{*gVW_FAGnuz)Z$;YM0$EAhhwyxqf=AybDdsZ%$tW=Nd z^(sei!5ps#&b=%coPr);%6Dp0ccyYQdCUq&M;nf^wt$5zjN{DA{%7nV_<`{cI>qc| zPAPX8<(^J){3m;HgL9zSZ)T$b z3;-~Y1S|$%aSV{OTj^oWI!VuMr{}iQbKB{8sSV)ozm0BIhFsi{GqcH5!Kb1Rf()*+pAhjeg=t~G8Ks!AU3475X$PI_@0RCVKIYjH&oN%J`9{?c994!C6oDo zPImlTz)bJ5kyqfR3eV1VU^$qX!AYgz9=EvafswaV4|H6-yJ!$-cL@eYF+fQdSKSNM zlrnx=BzsJzV<;|{Ly*^h%1fTUIPySLr`;lIo>MY4+%3^JV0{DhEw;YJtfN<=mn~kx zSi5lrYUvWj*qt7LCb_`gonDNl*9GQoyo6qSoAfcyc8iy8It6JMUkSdEQ%UDihFnHE zm%4yT7sLzOM^*->q2|xBaget0IOLsuf$0VC&M-no2Vp1jqo^~g9_L;UMxV|556Z#W zT#;k2BEiT3$&hF)4h4$U5yz}G60h%rc}tNU13ZK#lWg0nL}v~=iN1TN9F`+pNXNu7lrnjpoN<# z(6{H2)sWsQE~a=1Tvt!jBIB-_mjvQ1V*H}Z2Zf6$oy!emXHh%p$UiiT45)A~uN;%F zj#@n{1RD$px-bnJ}oO$(_A^ zk_+^c!S+Pb7*b>|$Nef>*eMoQ-@f(;bkOWqHbWdYL*~Yofr30!X=Zl1nYo=^zU}F< ztUkW2K3P^D-&UV2tB-H1PnOk3tTg22I^UXD)^KDUcT*1{Z(owK-iDH6Sr2Jh)623x z3+}wKCeUdT1#6qOpy@5OdW(^y6uUIfUp6{0C@LT%cJ4(5Okk`6mS!m-@vf{<7-4Q_ zIeYnFcXgHFbtlR>fy=}1E!a?u%X6hI-MP}LZ^4oqK*^q5!s7U`8o$?TE8T1_mIav* zLo!y$EC5Aa>&;$nm&mb?^0O#@U+tlu@hiC}bhyu}3_;cKz2@zfR;vs_+g6@3_Fo=m zu?C=PEgBCK(CC`wAlI9}92Vym?#V2!zHMz4$_727zTMQSZR(s9h*C*t4LuC%Q-fRnb%Od@&6C$75A$^Sxp)d!_DPr54{YcU=1}8sCustfRv2eT zO|ZzpO<^fb{&XV_FFzs#>ImK=^`9WTJRagDww3O^_Q(Wp`uKj-WwnjlrmUJNlY#()%)XrnitltJblzGL$#E@7L@251H>eOq`4C0dk~lkQFk1o@ z$ISZ>9nW$bQ3^-q5?~RBlsIO)5)8YBSF{suIoX6=!6vK~m<|NE5^b2*uix5-Z)wpV zZ(bXgIMsPBI7IyE_kw(Q9zA&K_*Ha0G;8Pc!)X#;e?KsEa`F{~7Q$3kpZk5oB!#^#z{< zC*g%eb((Y(@Opez``po<#@`^{EX4K|Ly9#20;PYzCx9So&;1F>Ax zvp3>HY>jm4)yOyZ1sVN(t}FL_$<

d#jmmVNBG5o2h$mh6hy%tP_j{4?TY!U*1SC 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%0DR9L<4+$uPm7a7qVtFFB;u}6Z7jgKP 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

>PX87j~|OP6mZWr;=Z0 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ⅅg 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%0AG0uT<3Rgyg9y2 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

kcr>c#nm3DN=1i>b-*+D2MmV^*^+ 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 z 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 z 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Μ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

;SUz*%!a08wZi3BZ|d_7GJG4#;R?l} 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 literal 0 HcmV?d00001 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 31cbc70681e54cada3fd1fdddf94c98762c9d53a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONaL0002Z;t6vs<_eFe4(3D4GU>m8NwyFe#j$2c zn~GenMDh5Hsd~BD;59-SGp6v>R;q6t1LEBHW;??VwyxSKhd#hUTc+$^6A+w~B1>7;_s3CR&^;l)%u=>MBB zD(z7hH_PN;$;TPSJ#$7N^^9CQU^~1#1=0L3h`L=3!r~71p)NQdlgIK3b1`Pf@G12@ znM8Cthy|ZT9e%}KzZYXPhYElsv*mn@d;V9@-_9|}zHH~5x;Rbtb-~yQFmdW7KNzd4 zr|;se3DP-wgCj#`GU*jCN6pn^^ft=T7$PBeFC7j@NjV-1*-{H4f$6U1gSl?l#!V{? zLG=kWfurgtLGA*$gOgI|e}<(wB$4Wz(x8AqZZHEl5iE3VgZ;Z3&q2I=1mgwp$dxWo zNipqOzh+#XHA|AOdUYpn8EMDJd$M@(g(P^zs^@9-pXGg!`~r0uk4}&22o7uC;f={> zRur>a-G1CFpA{B4DDN=>=;~-g0=+b~)!(MQo=DQS1&-Vp>W~b(OO`-Hsr+?$Evv|0 z!XWLKe$Z_3uP~L5g(Y}$vg;p4-P==0Qn(PM-WBr^$So^==5V&1pJq#x!?*X`ML$p2 zd&1wt#oN(xm{9Y?8~kkIZBJ$en{aupu$Z*83XsMTA%q$aWmByS7*exK@p8nzPKstW ipdz8<^dUhz_G*8hn!L|54jMjIa21sQfL_B_4IXZ>A|FBk 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) { - // "

HjhL=4+By!p)xIv&ZzujBqB%HP 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�PY1d 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 abVB$CfB#JlOq9cOQO}}z_F((}Y2Y8N3=3ZX literal 0 HcmV?d00001 diff --git a/MimeKit.dll b/MimeKit.dll new file mode 100644 index 0000000000000000000000000000000000000000..bd0148fa7c33555fc8bb666f7a17d12dcbccbad2 GIT binary patch literal 998912 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%0D| literal 0 HcmV?d00001 diff --git a/MailKit.dll b/MailKit.dll new file mode 100644 index 0000000000000000000000000000000000000000..19de4004226298530a53e46da4dd194e994a8be1 GIT binary patch literal 819712 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

tlp{5nZHu0Z!kr*myqN)ie#i5_ZwYc zjG{0cFh-Fy8D{z<4Jb>JPt}2(q?Ff(oFpmY80SWHD1BmqawPeUSMyQ)AT%f~@;9^{ zvuIc+6DtF`K~i_~9iLBKhqoU3{fAxMg#0@QvA3&Fz@HAoZ{&b(bJa-_WH+G5EhLnPXBiI*fpkr#h8tz}RW-;rWdZ(>UDL zg-8k*f0;GbZ*(x3-#7w|h~oEyj9_zNY}94`uRZIxmVQHf)>3-UJ=zensy(m6DgokW zRk^ajDIb&um^Cp)HHDT8K|>4eW%e6MstWX&mq0H`z56T0DoOP)rDU;yw^g$4+4KXi zfUcX8Ri*5=k#As7aExe5$oAn{9uwbif4 zCczuydni?h(SZ8#1u#O8lnGAo>fSp^^@q6tNo{QWbQQE((yI|_&4e8eM-*?vHf?kdJkF;)t9)gELkAVn*F=@WiUrSI6?AM zZe;l{!+ntJpXJL+$(IRFr8nvyggXHG2ukmW%q;!Wa8F4-UwtZE=YJLMzsp^_gtp+R z&=USZXqN+=w$IHx^&8Kp(xinLd*FN zLNlP3r&3)A-4;pDr}xFFoG+=&@l-9&`!B+6WN>KkmfH;N-=%G&gc0>r7=`{t80q_s zW>Qbj{=@d6=jT*zC!fkm-&3LajY{U2@}k)LjoY1hMp3;PNtAyn;Kgs;kub)uPv19{ z#iymDXNcL5{%IwAnjO+XbCp5nI>o0dK>w^grw;>X+i^OyE>DF~9ldxY49intyo66x z1rEb%N-qxM2kA@PPlZtry(Gc^DJM5P^|TQ_Z6Gno`&0~?;?o_OI0l@P>C+Yv&u5?tpAqn8R2zkN?V9e_{ue28k!i&9=q;#ch{TZ3%H^UbH8 z4#%hL59WlhBGqBMIQEkz_77l&qU#2Y$)nKAE{Q?fQ(=sUr^#yf5ctgql)l|eQu@*U zIy|AeR{g0yVmewpKezaA`k^6GuR1>!#+;|Z@JkQplbMX)cz#y!qVdD4Jh0QD=QLhi z=jjyr)Dd$(2fhU`_j7zIYC5Gigc?ZJChy>syX4fV9N3nbVHI7vHMFw1|5X8NIWA|UstVaG>TC4JdgS?_gmx6942t({GpGVxC^U;olzH|p^Q;EE>oND0Euv>t z$A0D02_w7HO4zD0n|s6X#`sNQlu8{x$f44dHW(q^qx?%z%}mLszSiq_n8Er~L)e+n zk`><8g{V_tYgH$vgs5{UEiOiTs#diq7p;@6Y7?AJ&}(t48Y9x+5|kpV`sfC{Zv@-m zPOn!zhTU_$zYDu0qj}HV19SeLK@U<1Cxot{lyMGAHrUJf6AQe&=54Au0plfe2P0tkV6~`wxPY-VnpTSeqo7FenUeXmeH1_a6!L|7`Hk?# zNlUOJT!(v*%#f$VIzx&`>IzTwyE`xTG*7Uk8^c>IihUF4C$*HKdctn975euiReXGi z%7W6`1WHGe;`*1Qs>f1mOi~}^AloGMX#v_>O3(I=p*9mRI)27Ew6qhK>bMmwc^6S! zU^frm)abZCXb=Pa76smSOAL~sf7bOhNyR|l8IAGFDAKyE)Hgv4OetX8J;-4!gq=X0 z8*r8m)^8*^2C(#AMV79Kw5vO_eL?m%vkh;uvu#r;_NQ=WE`rxW}0(+To4R-O(M z+Y|D%wmf}PY+skB4~10{+r<)7tE4h#4-R*z_&YAPef#j!mXfwtgq0H(BW$j)a=q9# z$6G9A74}4=lRf!qd(k^6QhaZIx?I?8k%oy>Ui2nOiq44CU8D^n)e=cb3=Y-c(00{i zX_>I8!kP%XEUZm+wpb-nX_1DD6fM$mkz)I@ZC#NbN=_CPc3Rl$!ir0o7nE{)B;~PP z@;O%YvWVVE(VHcDr=(_oE;VPe=rt9+9a3M~i^U;{)iNoMZj!t6#GnQBwm)T-o36_os+b8UIVYh@;m$a3UwEZURim*e%z7V!T*nDAzL!m3GJN=sZKBn*p$aY6J>iryAstA+KF5of>T z%S*x*3cDxyS6`S%^!f>lU&=W-Y6(lfi}Z#_<6q;a4?D2bL)a-{b;aKkMRJOCN-SO$sjfWTDB*6GT6e1|$8W1h-fGM)h%{KFbt3siS~-|4P8ckW6t-4a zTVbPxCBRM$%_6JH(|aOi7wM2lzl&|g!MqO8_dSr8ZpYZ)05}OlGxKfdc=qb-!Sm;A zFh{4SxkakpikV*|&p5U%EmA{~>|*<-ShO~!;Cj%i9F^e=z*@bYX%bBmtzsw!mu7Gn ztM76;&h+ASN^tJI9nR{KMR!>5lN9?U+xBS9vw!_dd$j%-wuDZZC8^Zja2gU~kfa8H zMCTX_|6oyi?>0ppZb+e(P*uwEn&f^NmUfy?lW-@2Rt_%;gIDiK>P1hj?^#qXj6tEx za6=48FAA-(@%g7zg2DW=s4}qfDhw7D)zU_*ZHo$kKV9ZY>JRb!O>SDhdDVB*LR4|E z@T$2jNe@n$eGg{>(aWs8bZRXr-3~@WbIs&cRS=7tO z!e_G-10_v*72)hrV|=D8KaSlcO zW>Fo`qIqkJ!o6=b557Uw`x|f~N~BXD(b>Fh4f)*Wq&?)tqUM^C-#EUAbLi$wlHe-~ zc&c;FqSoyo$*TTX zVEAP>V0de0O@-du^?XfO$|lmJlXP+*UCYRPof4^OPJUV+ z`XSQ00Xvl>-LB8lf!(Y(vpBPlAf}|JdqR>XEM)d>PL9j3yO@=d(6Wo9zmOr@DiV90 za?RlWD|wm(XD>-FcV~XuM&evdp0;#xoqQ`ROZOVHRJ1Tlo@^}L9m~D^O7KFpeV$1A z))Hb)C?k@75DOz4v$e7VnOF4mT}@=s6H1GuTP--WoKQx@Lce4^oi<`C#d9$j%ZPmf zV;M<*6lCd2LzbdSv*b@83HHMtuoT>(avtaXrms72p4ucXuNLH(etVt$T@WeQrpu*q z`A*%;(uEByEf8iGz2U;HiQXr|vWnhhVZETGP?_%(b{Bd$Vz)%PB+}^t{PbIqT;e%e zq#oiqW+hwPky!ae?}m5|?t$v^pcs6=gQa+3{h^HL=`1K2l6IG3$q6Mx?B~}xcjGK9 zy`Gt+GiC_}jIV8MG19?Ocj#lu%Nk)rgl!ay-tx4Foh{DE(^>K~QJxlXvfen+%O`s8 z2)ia0jtqZ4?Vh7@%+!KJh02&eXV92;62z|Ai>ucpHd7O z8RurQ)JWJ+kzO|?i(3CPeB%hV@TJcO{JrF>aQ;ev5ouBH&4ZZ&KCLKHZ;>2lcy8A4 zG)u=sIsxxB@QtbZNu=BmQ~gd3=0fIN#j6&;IsFkBhMr65e1Tu?fw>e(Zv)e_42#lp zDV-beeO)(}2AeEYeF?dtpN6Vr^W=A^>NA4MGE|KkPQ5^=dbd4G=U{};GsaMLcQnmF z;iejqj=##R6?i7SxguriWWje-q003&y|)TgJ-%Tn3OwsLC#mCZu1zy-{ElFUS%<8u z|8jbVYgKz+=F`@vU~Q$}zFO6*@a>Wg7rrVpQv|b0Ges7)$c#PYOHo=mz`DSUpG6H7 zX*j&;A}`BeT+p!!7{OK6`JLQbd?wN^Q%X|1AP=Z5=sCKUeCp8@>iK;t6#7%`IZ4&{ z9PVg@5`-^G71SJ*8bNM+2$EGzgB8n6l!EVtg6~rAK#iD(PdUy|vr|LVVqgIyDg&e+ zN;P1tf>uG&11n2Yh0T%qRbNwr?{J|7=s5e0uBMc%=8UE|C#!{~xM-|!kmlJ77Zn+!%b4l_mFMCxV@KSFsU6$jf_*}NJ>({H#G-fmP@v^%{dpnz0vQ$ z;cPs-y&s7FJwukwy zZe>ZTay71NE01x_S!34c^tLvs5pB&H(b!0X($a6-8yh7)W+_ReN-&e5r<q1$n zXDrKW_ODGAs#Xqw^9-2Jd;$Xc&4F;%8Cd$YePMWOPOJA;u%|#$${Ako4~BM0EBaN? z3+O(=s$BEvq=Z#>BSfzjdcPAi%XA&+Ir_sf#X3eG1&#G1)j2%OTh(}&p^!AcF+cT{3sFxnE^j~|OVaamh8MTIzpurA!OVzK&hPi(UXAD4rByv& zv#l!F0=|LPZC$5huiH@;lunzY+CSHO#Tnv8wzK)0`+( z%1>vl;A>+T8)>v3*PZi}dzJL#>R?kZN%7euui9?T+|svheKtU!bfPhUq~}|FlCr@# zqw7oh+azCu{TzJ?DR{4BirSxtzB)=)d7+QcWuA<8nvygYT6|teQIa~(ld%LSUPI{C(UAen%RZMg45nVo2LVxUCCwP2Yb| z4kbRFcw(MvE^|H^Z-zl}?4iA#Ib)1Ev3M;7`loj#ivESX?>gUqW& z|6(yqa0D!E^tcbVq*Y}pm=0ZTZ(yvr+`P&Kqfjr5Rc_ef(|HPK__mP*YZ6#5=u(B5 zsyWjS7_Wqaf1C+sk^R3GFd7gW+>oV*@hmMV#Zsrf94nsd!I#U>Qs@LC#|rM>Fx#L- z-5Spp7on9=+H8$^)uG>9P(9Fl+$3c+_d(#B{*G)h^Dk;;7S-bqPFvslY*FS7mM#os z&&ACV`~OeM|I@U=JpLc02yXr|*Ovj~lanyihNa#qENz9lLFM~y z0hTt2G`l`ApDMb8zO|&gX!=6EqWsgdf3F(rq_;O-l?eNZG}BKW#dH1_)q`}UFZCeX z1TNJD9cZkD@5mZ)xqT!xXQ|o$!OenFOLRW#xWF14_R)3C4)#M{RWgy=+l=q%cIaR$ z3${F;3O7>;{gBxwl3p6NqhQ~8uW*PO3=D3mewV&dv#7Fg;{^48`YtYAV=1rR!@T-m zm6k=;R_WVY>R_06Vf_BC{-r;sIttE2>(R=qLj4vs8q*tOjp2JM&HP3W7^5m+?jF0v z?<=;56m0v+$=vcPoaEkz??14}jhe74F@(z3t1f(PQ4^jCcfdXRcHOJagER$GlnAYR z2I|d$orLmhbe!J<+qw93Xj2aNthpY9d83?J)V&_n5`9{odKG;G60KpELGP&3<~O#A zQ~<_b-9G$AHB<5`TUPik5PNTq72MANtx@~)sxq(_Nm45KqA+aI?`|^l3hrl+91o^a zr)|+wX@mVCNn62;RIhyMpcy~-x>clBX8hpB1c_-de!)0@XtpS?x^MP`UKPwM`$s(f zJ|Aa`S_3<&x|aA<>5Y^&pGwF}@4BvySTR=JW%&kMJVaL*8oAIBgVE4T?q za_!UiIVU;)eCqiyyed*gI<^hg9Ix`hYD}->(x;dGK^>Y*Dfg;AP}isg^|<;GmIv=M zzzsgIFGW4(9#irg$6&3dnNL-M+C;WBU#2xSd^s-C;GgM?y5E>5?-uvKJXZHtUgd1b z68B+VmA@-9{*In*@6w@pRqKTowF=|M-{pJNiH$U0gVA1Ep1zHQEOcDfp#@6;qtovc z`+zaYl+uTx+YH&Bg&i-Fcs%#22h*7a(-Dk)Fhx41pZ-TNWzVoms6sabgi4BFUj_0? ze~}w1Gnr6z1im-WIT@n{E%U04BJ~t$T@mWXz3O|D(aj@thY;USLnWyD z`=B1}7wa3*=w3AnO{bi`6Yc?<%{s4A6o1`Mcev=fk)rtNi`YN4pjd5}vLq>33cq7nMttBk!F`1af%yF18i^MGe?tPMVMG}k# z)IxRZCW-aD>ML`n$g6t5YFqCa!LJIznv?d7l4bWO$bzjx!AWw;=V0FRJlm^s-=I8z zy9k$auGzstzw4l~+>iOE@9m*j=~5uZu}`nZZP21Phb-9BXp8hxiJ6?r(dO*htMWv0 zO!bON&lJ3>YX)vJm-?~9X|t=c?U;3dok}Y6g#Ijzs=>X~i)u+SjJ0G@1KJBoMa>op z_u#_blFl`_C3h}MYhmY!SY>&-7i@|BF^#1mu+K&8ygANL%JsPnuPXL{b{D*Ala%VO z=6uboT1fe38$@$BugWRq+f>RtSc1V)oesTnuny@FBG{%=)WvGt-t?_aS}*wY#V!7y zS)`xQ>i6-7v81^kz*kXFmO3sL)$zOqe**<(8St*?IF_$I6{hbjNPo-MTUY6;GM{qD z%r`hg4z5z~o8#6${bd%fa(uw^>|igdSKIXES}@K*JzW#&X;8Af2{%7r?Dg5GODMgF z1=FkVozZuYSkxBSzt?#WHx-JMA~T9UW*O0}SSM2aDpF0^GC(IZo|Y&n@UXIb9>@?OZT^w}L< zZZJ=S^?*yuqI5l=JP77mFvm+o@YCRzqIy;IbpFu{`Po|B4ORq{qQA=*iwc(h2ZMR+ z;aUtmm)Xkv##d%~y{g(7Zh0%d=TrvM7EEt2mHMV6I(=v9!DXIlFG~vExKoNs$o|1e zsaNkyy+0^YIjIM=L|P27*K-xvK@+L-0?xnrV>z_!FoIENzH!WgdH;8L_2N1dw73Ri z8+l$cgQQ^o1#K-V_zQWQYcJCC;!TCxAADmBZx6|HFf=`5)c0>d?T1>YCBKmy>I>aq zkqmP<`Vu`4jPayr&(5U~Y`^*zw@26?yod#FCl9tut`U&WGSUUhJQ%;{`_5n<1k;3abk+7pc5^P%~T6|MO#ceWxVLCXVe%Bx`8`ulVQW3THe*|w5dX@1x@Bc|K5 zb-m89@eH`pKC?_GsvH|g^)nDSt51be-aFz)Mj6L9CH^qAad!|yMQ=G@ii1!zB!_mRA+ zVs#45tC~YkO+8Ivcz5T-RF*O&m|Y*^w$M`ci-U0q#*b5Jkv`|eHS98!2hFAW!RaC` zy?D%9Uz+pO2WuP}U8*@*`W8-I5qprArFY7)DT1Unc|pA)d8_1v3w z?*48M4{E>Eu2(%kV%QoH3O44>XfuQvqdW^~rzfGK$u&wSJC zl`v<+z^>4|7=AHt7?(M>9*g3&ZgBnUQ=8{gPuUr@7 z6`l9+TcNOjKoXY`z44^|U4HxEH@=72h;GwwM|d3yH-$i5)9LW3i!%4l zzmf8=zOFLH#SNb6sa|1_ zq*oXuS)qlJ^#9m<6L6oa?_YeqKc8dDEHjmlbIK6aNe+qzAyPPoW6GG2p%Iy?kC`Hl zDHWA@j!=dqQ&LeXAr&eip%kT*?rZI}*Lr^{x&7|%d+&Xo|NWom@jNfDz4zK{PiwC| zy!S`F!@&KT2kRGl;s0LyC(K$jGi$LNd5A61vZYeKg33*V1(wieu)zN}Oa5=mn;!qa z(=v(r{Z}nDUXeCSt!t}PQk$Jhp?$9Ddt!_V`nHTwL*JG$TI}00wBF%<3Ely+88%$! zrM4GK7;2BX96PSu(gEE+MN#jhz;irmrAMqBpnYQ;DO%!tNh-yPDfd;f_8cu+;!3lo z*WiC8-}g`lGFG)VQmr;ptu|6=6xQ*g^Prm)eVd_Z_+5&YcM?>|{azE>4cwpnTgzat z3MU9+Pjh0|Uh1l_-wVvsc|xoy%sqsY=J(+H5sFq^XlCIw%N?#_xPUEIZ9Uv zc!8^j+>%PAuDDsq8o&AP$8quI z4%XRJYcf543VsQ%>C)rpJi?ob7=vN26U1Xs5RXAYJZlJIyDNz8t{}F%jKnLDHVRjU z+A@LdEXs`7)Rirq!Bo?Le)W5O;R;CV<*$7soav(XYa7iNp=iZG)$gmpiiY}gU3i<5 zm3!J!EV2}rr0BPne7+z(mQSGs-S(io1}o)|l7~Gpv`FD9aFsu`4Cp^AFVWV8{rQ0R zkW)#28#+c`pCR|%af~|D#m!jMea!9BiT}d;5p5lJsjW4__Gp5UC#O8s4e@Ba8!1w$ z@*L@PskGa#opEOj{aNa29L|6Q@!6et0Mp)9#A`p{!r$9D6xS8*`K)}X6`!Vcb$#g6 z#(;VP;avO{87ZlB7w#@_zNvJ?G*Ekw-8v&kukm>sCiiY+ynW%+`vj4T@O z@I{KAEU9SJ<%+`EXy&aMPM|!B(Po?*JmTc>`1tdxAScN%0ghE_nwAa?UG3yHN@k6B32 zR?I?-5=ZDQh$mzJV#E_v2k`_g!fYjI1=^fZK!@LaDCQ%#Y%e@J2Naj&g{4zyvAzuy+?FPII^f11kjXfUpA<dH(&H_>MNf|(#W{qu$EbDMGQE8$ZpxXx&;7B6{@vPU#H)EvCL{hj>WS+LzdTz` zYwbS2JCh#YuuQqX`#$OMUo8dyQmNGVFM*yL+}=&?-`mX<)q|HNHt`y?5hMHpNhz_S zG1}(sRg8GQlYO%oHA8r51Gt(njSBNef(b9tAXlX&oSXT%N zO#8CXS-6L?EBdOaqIH(yE=w`rQWVUg@viZ<9dBb;iXIg-6wgffJf>KRcPzyZmf{mj z@u2rD@XJ)bXQ#(wH>i|>7OG} zR*>0B_O9jj``>Lgd~biUf-`G>p9WT;oqAf^aYD(rsAY!z>m7QPml3Z6-=aEta(evB zd(_j~{0Y%eXTw5Yp6B1c>T(MAUu389a-4Dt%8Rl|z4Ys$y8M7wTBK~(!JCuc+gW%> zen~Wy${BYcpN1|dBDb30i=sVvG^SD>%sn#l!k#$RTabXt`J0sZHBUp~t){$lRTsUn zg&u)#E?6tvPN=HABwV$PtFG&}>S$FtIeP}afS^L2!(Jrx6B}Fd|B!yt!&pB-te;qF z)=v=YCv)hl-tMNySC~~fVcZX%!(A6Y+b6E+8sNUIw584!0kOx;?GaGDe9|%jjm0Wj zxawtPMT$OP_ak)8^l#Uu>G2ch4R^M+%P&~th%S8k&)!#hyvoZ{n%1-hB&5zkz{o_LfMFQWQJ0JljX<6s&+C^(^rV$=;UW)+sz+ z)+I_)JVL+6PAYRTyJr-0Cj-J|M{g4JgWvO&-`^6B@2LuUaD$@oONL#(uJGlRFK?>* z-8oKylNxBnyAY|CZzB2#Yb$9>ZRsRhjy)7ki*LTM3gjBcSA*aL)%4>Hzc*&POk>PL zIbS{ZKtC1#`Ind={5E+m8Cy|0gnHpth~n2?6+MXk7vUbcNzuU!MFa0rG`*7`_+Y4K zDaRP!6DzqEWG+2k((9bq*DAd(H6GA->$_ney2UuIQ?8AG-nM$yHV-A^>r|9TdMB4i z5SK_0mq-xjE3=j6i=J)%!F%Q(%(S_?g^lEVHpf@6IsP4s*VX3ui572y#XDi~KDKyT zFQUEJ64qW2YcGhk7sT3&9a8O6NwrTU)jpL}`&3fxv6Jt$N+s1Erzl=4?A&>+aMot= zRQps??Ndp$Po?P=Z(DJxU95Fe6b-2;D1|nT(ys@KtM@nqBvRSodF5H6WYqzle z{;iY?_k;hAJRT|1!+4|!;*s*tz3zWsZagj}-*@1x{~Has?*C2y<5K=pPfU;J%p;-c z@k=gK^p5Yv>G7Gq7bmvfKDS{rk{kTvand?w5dQyGQuN z2F%2hoD{vs6Z$3Lz5u*%%tvA;Yv$4YNwt_VTXCq&&eBFYho%zyb+R{i1@;dF6)LOf zsT=ecDZ}y09*WGcyfZ6^cV-2t2Zi#Q53D{z;C`iz^9bj`aF$Gu&p`c2J%?WSEx5zS z9NwKlDS0kSB|bYAP5JCt5PLj=`0QBF1Jx7_LH`r(+h&RuwHAc0Dn3wdn)kV|FYuh? zi&HHtM;G(uAH|7@?8IcCrCBm==* z7T+MNHtf!fmahh7SMAU7Qt8y|dQV~19fH#6mX7)^NwS6Jw9vWbG_=2kwzAMOw`k}G z7CPTT8+Fjo)oV51_u(~H$C@sUYL(E?sh0LhOS_|~hSs*w>K1wt=1X~B<%CH-ZjyiT zlGCW1FIyUov2skb6pMX|RC*idE}~W6s7k)p@-6Q3#csUCyYotoH_GDOYw^Cbc%L?s zcxhDGba~cvIpEW#Q5UPLtzN%0YHc;N(Bge=_0rV1B32u}TiUE8CEqN>zd6od`ryW; zNBoVVls7z|$wKGQo1S&n;_sp1SB7Hk;h{Yrwa2l zg?_z)Q%3AbzkqxN%>qiYUo}m#uLhIo^rP~1O%kmgsA&HXMf&X*e$NKGWtX`9683xr zJ%$-uP#Nsb2>K4SAxQ7oB-!XWzXp6OM^fl0OS12iljs-I?^Dz2+rgUKe^smWc=-kL zl4pASH|yDP^c;RCD?MJ($WD%PU06)ou;aN%=#owLSR+6|0Vu*HQyX%E}+ULh3|n1_ep6*-Hm(8 zxQ>{oCEoitDB?L;I9_A(sJ(E$X|xu7a{dao=xxx$1f`?R8O5w@_yW&s5F^z9-`D1r zUx1C#?--YSQ!z0``2FR$9CD8_F|_xcQu4I({BAwgc{rJpUXmvhUlBX~ieT@MOO!@4 zU~>f3=&R_bUV?CXjlF{9PW#0bBDBh(y&r15^i7J;8P`Tbw&5-XLh-9;h%|&@vbhVJ#jtk&^8yZF!cMtNq+AgAaF6#Z`govUc1Y2QG7Gc zzgmcQAdyzeF|?MV7d)3(2MTW6i%MnM7ambQZUEB&td(L4!f{|!ky1kX&Sm}OoYz46vtKv zbfv4IyD!$dIzCKlBe6u+ShwGhK3r-F<3)S8uzKJ#Q zq2U%<*|_P3j;_->Er+k;M9TZdU1VGlL$6!t8ACT)=*8I06DgN@luYIB6q}PwdJ`c^ zRer}8F{nSeNq!|z_#2F=R1zl_yuM7fc_!Juu1>bGieFwd+u5aU zKQ#^BMbD8DUlA`R2t-nh2!~XcyTJqQuSbk`qN*y3s;{ABNqS6E9{f$)mX@H`y4gHGp^Nffa>{x#Z zdd^U*hRUTDQWWM<(dU*%$F}IG=#p2_>sSj(?;P7$(TXaHvSOE5LT|!;n4rBl0}@oc zt)i}2c?p*dx(J&2hN8}f>KnQV_SDNmIRw4wQIy)E#(7ST(v!HIAn5s<6lp(*(#I{- z+V(&^ZV$YhcMS62jmS9SUW{)6e0fu8yf1GmW%%-@(g0uHRN7udbV;S9=m*k9hYh6` zS1xSd5x#x#_1UfJbKQ4Dq{OHt_I-G4!CPu1Rv_kVEYEo{8j(xRB|{XBbM(o=nj4P~ zwg;Hy7U*~M^T$daI3GQLx652;=186W_`8V~uH<3bHb^0!bZJAE_htH9cP)z(>c|HJl zeNhR$-z1+HtNK-X0-8Htxxhf_5oz@LG?9`@Z+QJ;H2YDp2l936KWnwye}x!Of2`?5 za#*5Jmtw`V|M9%fRuo?ep=S$9fu@40^;WKiakY%=^@4=P=vzM@;fot@xv)O6l7Ec7 z5z*yy)THWL^IvrUyV&3|;)T{r zsCcTv<%vlsr1X>)#J2^D6Wfw=leO*+*EjeEwjf?(3*y_ZjO6YhBdi||>KB&lG0s1B z+>1baUA*2Ht86hbxuHFHWgVuCx)r_1(>$W?g{wd35u$8}lmX zn-TAsSJ8dQP`K?_6$xsFJ#In0N-8>H@g`v|lh8Go%LL`Z+%0Gm<~&9*dIn#-9t6U= zudUW!=R7)kUaWxg1e6jl$R83eT%pD2>R!5AF%kVm+V`r<6n#D%UsgEDJ<-13IF$8c zJsJ4=OX)ed8Hbb669`SLgFn6^S3?=`*C3heDkIJ-K0%G~C4->bjVpa)CXcbLS_X`aj^OK@h`4t^5rD#qWMQv{o zlvv7}9+rAROXE8cuAz+ha_nyi>TBEs@R5XDFjCRYGZZzm(A!PkIkc*z*qle>9lcD^ z1JFuBuZ32E&Z5@|>e*b;uXiarc#oo>yP}GLqDQg|!t7=JAZ%$K-7FdBU06%INnPW6pnM=qZMx8V%hD9(O`^S z9|7>_1!f_dnRrVtyud=*5VBsX+*Vhwecid}+rZ!P~ z!1GKVgvMyt12Xr-C<&II=f-G)Z>uwL;s|I4tvGE)d~ZQTgW&@UH@l&tRcK$~9)jm4 zXt$-<^|5j*;d@Ky2G|HezwRWD9>H?Lho)-i<<}}&lSk3iA64F5SOG~#|U+0;~tg_6r=pU zU#0v{`c?ikO>xgg_3zpGlyy?n-+(9mqK=O%Fbj$Qa=o9AqSOn$y&_UX>D~eQ#=^ru z*MbYEnU5Dx3`kxpK#tNyefWTAH=KTG+32ez1Ql)q{d^mN=0wLEW~Re zv6!rZcrw+2GU6Y?CW`ghhPgt}Qor|_5wGQKL`M9*9vUwn`U}U4Q6_xHTb#UiU>)lK z;p+k1nULK0HNWb_T-ZjTWjKS`UTo8ha_S#YUiFpdU>#cD$s3k6LX8|6Vn|ou8Sy(_ ziqIrj2JGs>ixl)uXS|my94xSCz^&B`QvQ7kZMT55#--L;K}w>Zvji5iozpM0jvHWU z_{BEyu6cyweFX{BhSg~mO?IuBa)a$0E%#whz0vO|CyoEXWnp5|W% z>F45wtskanfLT}K<^Nk+yl~K0`%oi8<89u`K12K8k?D$d8d_lJ>&6=Tk)fUFFVc5! znyI088Y*Mx(0U2QZ-An%9&_c;c8k#*Uw;Apy#;Y24;Dn`t@F)ve10w?AWZub`hnbK z*F3@=5m2{1IuG_R#Jyw)v2cV# zL2hZZeZH(>GUBuLY3(lEEcs@{mtL!Nf16p0kIY*1w^qHx+l`F)>P{-9oS|nu!Z(2E z_u?sh0Q)Run_!;cukaLvo9%6JM!fPC8Rr@CRF4ApN`}~>`LL6+N4D0l?(hoe4H4%% zM(He8no{25o3)P@L?4%49PY2ZcAm5!;EN2_B}((LS`-hZ4EoS?XMJ|UYvxe=+U6&U z+P6}aYKZ4miC6ziMf)C7^vD6N=QgJljfT%EDd--p`@_aHZLXnj8hR*0xpyt!ZN`;^ z?<4Zs8>(b!Pa3M)LgUpll)I&J7g@Z?SkH*Oo2>QgqK`8t>s3MhjtBG4;l7L*OQcYf5p*LQ*D zIj#ZZEp~`s3YJpd+h4e_l-Q-ino06i4KCas*mXwh%b3v=8S##&dy%60NxLm`8r&Px zTGJFUdf=eW&b%YX^3vn0R>_PgWm}1~G540#>kesJY3YrQD~8c6FEeTjO6Xhv5E|BV z*dD@3iT++2=PNI6KAPPffi{y~G6mi>OF^hdDYiq8Ut{wY1YfF{XQW@Aif_T~7IU|n z1o7%q5Wj^k=&NgSBf!Pu{U+h~b)pX(cis0AFY%TNzXB?|=XtRsybJNehOI&3f(WfpCjfdF^dt?V3 zr!NWz_F_5KkZm~#uX!D9rEOqot6AEqmUgD4En#V|F!Y;Axy`tX3utbi zS=r`VysDO4vgLNtLR(sBEerj?LfgNglFRw_NTWwD)>@ljXqlm1hIXKrNNqI093W^Z zdb^;7J2Z6tU=96!iiTdAPebdKQk3l(4Sm*hE^Kwsn0vPCe@!=vJe>8OkhC$%3;&dF z{>1Dyn)GM(Ba%ryiexGbD0oE2lhSH=e74J2op>N>A8(9@EKm7J1}w+#2X7Hf+N{^7C80foj(}kHek^ z;>BoCroO4o-Zg8FHv+yRS^@p=D<@atUI$Q&y3B^Pagy1aWBuTt!oKkAml3av*;LRC zhMHSw&fOX>I!EJehK-OE<@+fY_kKx6{3QGPjr@8-!&qVIcW#OIl(b=v%< zUB-QEe$(T(N*>AX)Q$5^c6<88T!|*fBXp@l#j8dr*`brL?NXEc8@|$Ccz;85{wA-Y zOw_oSw@KSlXP0D~agyn)Bsnoorm+5k{3>N*ZbjRT+uJ}r(3cBnXbifD1{WPrbjaf6 zM}HAr!k*1%7|g|pXPmMq8-BSL=LUXMjL}}Q!=q$+0aC7Vbx;_&Nga$|jqf~NTHQIu zuc>17B)T;ITHA6Eb_hg+j1~B8KqsYOa)jQ&7>v>n*awzS9t#rM?m?}!vWIo9o#fjC z=a((SPsCYob44Ga-*X$q=*!vSAK-+;;%V)s$FpHKnf<0T%5#>eDpI7;%6su@heKOJ z)Kt0b^A!CK+a&RJ_EMC2RMC8Rr4qW!(mp;xxwB{w4$X+Kxm??55bO|_JR^Q{U#+zY zhJNvPAT#3MW@^06hBB}MlC z!xh!SIASiDc9#mMCQ>9*GqgEd(PYvS?PThVc;fq{e5Lkp8di5KIcB{$J$@(l&7_{6 ztQMi$ATNzd{Xh=IsP0EeAIvxe`*|D5YPgGZXvwGKW4~H=uOa6I){M zko%<6L54#*=l%EUAk#VG|{XxpDi79SZAD=T)hD?vqmYf|{VE*-AytCPqmu zq3o1%tzpFa`5a#p`L=|$^mT=kA48fO-c87_JBD12|75*{c~pHsZy0EKu_NyLOB%KJ zUR8{mz%uZ?p!4|xQ9CP&y0tTV8EJGiYEpczCEhaN_ef!5h3jxa(FTk+;T9UYJVVot z-m0N1f6^3l4OPHckhFb%QdAK0f^h9n&ptPkmm4#LaG&KLVLEoa)_VJQMjk>9wKshY=1Y$Zr;S^Z3xO8em3@|z}U6t57W))?n$ z)Z9>KLwm8V;82Vd-!d^;2=A~xIP5~hyO9?BVwe6#f%+iupRu>Zb7S}t7Jf_k7knRz zeC557dmyDreETHzRX(=%_yAs?=zQEzSu15SW_tHr%%gR{w^Sr; zZ~R*K)0M#oK;MY**^ zb1&6+rM}d%Eh!|PenvbeW~QJp`<=VIFB)nfdvk-K0dCnAlX^~%_r-Z0_gH-ER#VcZ zxVMmapFfSh#jPtTdEX?hgR1cAg}bt`qIOjj%_yR1b-JR47J5w>+EznHSv<1PJ8@<$ zdE~S_23zRcmi8G-o6kbOvCy|I^kxgqX_CLS(2f@RiKU%yp`|SJR}0;1q3IS{!z7=z z(1$FvjfIx55{H!ep~AKF z2){aL{XY9|dNQS672Z9^t&$Nd+3JmZUPb^;22RTkICcXz>z=v#8!@ac@!RPx#lCU! zRS#Ao-m{F+EbGsqWcAHrG`WzD{RffTT(@Jx)>iiA!+gJfgS^MB`CfQ;6}whLw4a7n zuavc0?uOd0)(+Kp<;*&M*G5B+H&C=FQ{&aitK6-YV*Vo<8tyhOfL|iqh-$s`2g{3@)nN#HP9=EJN zSTcM1;VSii)=R?HTAxpKeyo6^l>Ca?8`mnP+_lCvGVTjY@qo$u%}^(cx6`=sCMAcV zIu>t^aea+jWNEuuXkpvI9c}ThH8jOi95J-U(!OWtv}vEuP$`o<)Y4vRDef@zgQ49+ zwC8+vi`G@eMvC?nQdGydlg7Pm51s=0l4w{mTa z>o-J0zaOQj);dA>60?+y5A-J6{pBwJGM%kkIZ@+{f>*%(FNq#}HK3;vnnWW$l-<=N zYK_-mWu>k*F-qau{-RAKs4Qz zJV!y?Z2`(pyD!o`&y~vHY@s}aeIuLZ`=~&ZogDDon1;$MttH( zJth0QjZLF^H)y;$qck4hN0;)Rz)LHF_%6GkDmJSvx>C6Z8Y`MqMbY6Rf->SU%#z$j z@|BuM*|%QfWo}S(gP|51mAl;|+_UViQfk}!?bqB|yW{;>NTbK`UXbKb3qFIOtFKV> z()TL)B9qeNQcY3UTI%9gU@$$uVzq60+QEa$M(&E&z zsD^IqtMYzpsOT}QFhu8i))V*Q%TwV#y-(3di#KVoa$BuV!`V5UC7&6ieJypB*5swO z?zv~RN?B~J(AZEGk22y(I0uuqe0jX4NHIIU`z5WlGq$2H(M#jqJ5vz8|LCC_d}^ci z1ZGlyPHO7);A8#(9kk?xj^C# z8m4H^dJP?bQ+NrTZzz(i+!k1Q3GHVo)*G5rk}D|*5+=k<9St?(-r^o@DSHS?h{d-FC%;hui>e)9D|jB3K(%d9pMwo_0?%r1hy ziz({;prkmzF8rPX&rtz2`Bv6K=hteFkC#R_`FMCwI#X&gMxWfEJLQ{Un`E|EuK=eM zxQ8Mff33~4WQ?X`zg@V|6&3wjRZ!wiG=IDK3Q}NZ9;WqI(C2~kYOB-xVMV!R;D!6p z^Z6EdzX|It>3>rUg>&u4@GT@X^wIZiRc@4_Qa>pd?m=B?^UNTV{4Ls-<;7?h=7lCp z8HK0oyvr^pFzs6EX-l|NuLVL(8%Cl%m@qMd~2Hexjg7Jru30r)cp{f@0JdmU@kA)p^*HdBdUi zaf5iXL&xF&y$zHR|E0g~-2|Aec=etUzY$Ud#g8lc@N=}I3w;yTmQjqpfGug`>TgG7 zJ)hwlJyP=Gm#QxJLTBy=sq}hoUEAM-F~hg6636tK2}FIIyd}7WF1vJtifb#Burr|D zwvIozG@$ofiS$eaH?g2Sd&p61ReUdwu^{#!Y*qHDMSK48^dl#Q&r>8{K2H(!IbI`v zdC8x(5696P`So;e`8d@yA6Au|BA~|b3AvPM)WfXVQ1i4Z8CS^W>DO$Y{>h`nRi)nN zN~10Cw4@)*wK?EU^PnE8qCIDG5k;fS)7n*DxwQ=y9W$P|dwU+8qh7Ny{ixN!W*f=(8!BVuB+QwifJb@ithzN1dRw{M)+!p=M}3+twu?PGQ$3IAlQmwQ;VPx@2t~!-R@5K9g=p11uG~+SqWfUw z#u;j2tv}s%>3*};FH}`iw18J7l9Ssj+G{DI6_hJ)V{4lCi!j}}-&_M2B{DONPdsgzi^$t8E*#`+j zpM&KWZV}Rc?&9$Y%$E-72~5mRU~talON1Ne-mAwc6h;br1^BvklAK4yXzDWQ#dyI7 zbDo@A6+&9kY9dxPe48qb(kEy+Uc$bLl)U~it*a6ix(YM9guYoxQI+o$9ktPzZ;NtQ zTH0?Pn z_AuHP_pnz4bljoOo)28V3TQV*E%&fAN?oLqtK6iTews(olb>igN|?r%R2^Md<&Eiun^fp6_ zEnXv&_o{_3aizy0s-dA@KF43#{`+fN|%34rMRNf@7eoy6nYF^MHLwPM; zX^XegLPr|KEVC9D@Dw3XTRlwF$kTFbZY&B}dtv7*36d^Y$NB4vc_@hr1=Wol|3bq&2& zMx{(QosXFp^oh-@=c;JDTWnt4WIcPVosvCfp^a=_EoD02{Gw`*!*nicJO1NM=Y6Kt z3PZ0O8f%)qY5G;PeD|34M@{D?rfFZ(w6mqC>NUlg=1cO%Ww=)_H$G%#eG-;IP~!(> z4oEz6KiG{pG*0X7k*zGl&RS3~0Uz?|~43+)T^n?p-r34>LfViCN}tPV{>zWmZ~ z8tvJseXT}JwLd;X(N(q+IMVO0rP2QN8amka|CSock*u|`-F5=wmf|f#OAJl)-WTpz zPx$*fxwEa4H(=Jt=d>@{UdzEPEhW;H#yT0hzua?h_Jq(vE=4$pUgKv!yit3Z*8O2< zFLgi0xNW%4C7kw>#2)cHLZ!x+8R8VhU2gflQpveGs0NSRpec4fAEC<-4|Dz{!eJi? z=sVYAKQAEjK7QlX5Z~UB+$w#fedr?JU()CYTPH8-qHWo)gQ8cB8)RIWpEPvIEJY1w zD7xNI?VifjHg27*7p^vLj&Y9+)D&3^4a=k4FkllXec6n;y=?-H~BmrVRxvLJqcTu}JUp018#RKAncRia11w*)wkRJs%8NI@^g ztBxp#puC>LX=!a)wIzN(sJg($dR?%R@U@FK&`OCq+Q zk;0JXA@A35$(^vj6vjr$BOC^kEew6ahAb!oXdk2=K~ zjPLVj#P#jyjJUoXoe|f!qZ4(15H&JUU2u%J6w)#Ebp`I}9s=7pV*#9;Vzr zL;NBn$BR+87p`9k!7mvfp_;C|oxjjt67IKGhgTpyHXAIGypXLem=Ui%MsvI8bw#yT zE4tRB^XL2caqZ9ZFk^EbY4i=|>D!=78g<_YUk3=krCUnc5-Tjs$#N1Bo+&ZnF`d{a z^-W6bqhbBVDX`Z*L|1M4l?&d$U8OHjhwo0`jZ%u|rms08UyRE;oGuA#Q}pL_$DRFD zzf=xG&!@pw5Y^Ro2+L_2=Sai-Kv5k7$c~sTa+6w$01zR}$lCp(XK`6g+ z9s1c~8N@@@dos9BjZ(IDwHtCZu>KXFx(4E?c zF5JGFB1lN_h4q}UY)@Fv`PF((tsjZnJMHzR4)zY-(%Rr}lB7@G2g}drANbvHc;WZB z6rolWr?~}m{TK4(X+Zo^A^#dcKwp)V{cPO&M*r*Q((1{StZ`(lhUc8W4Ofdac!tfU6=)oHl-GTM5 z%<-Y7e?Aiyc^5hpj!^~d4$3KBibnzcQ%{ta+)I9@HY}eZEpK9*^ErYlQye<%`+bZmPKglzt`zonQC>l}85jC%JBCJRhKt9y9mHqY0MBEt z@-Nt*++nuH#*)iL*ozxs7&TSxpY>iVbgf~%8q@2k2V)I9}whvo^Ut<-zh`b!Zw z+s5vo^kP0g67(`Ol^xehu;a>WwKTf-9kufP4edibnGFgTQxr8c79Nj;{ zGFxruh1HXIW8P5oT{%T_Mk_j3SW%a=Dy4+22cI9Op--A$H^=$X63 z-U~Y^+-mszg4&r@jm;Ab+cLbtzT+nygNLugiLq;iFkXl*DJ6Me_rv`AumxwK&878U z#FwI8n5_oOY6t!hJQPlw5kIgP zqogpS%8L{ck0!_6YTOBnHvz3Bp=B&|frTE#xsZgGHYxX5ylocmV&n2yymaHr&sQlO z4K*-yi=pr5YPQmivw&Ul?scQ4DTP2Qqr`*DH=Geq!ETgrgU||shGEtalmll8f{wkZp<`gVgd4m;QMXStv>a@wguaTkm7u#% zD_0|@rg#>6C=&Y7MCBS-=%G`ZVhwt+#5?z;a)rw(w+!bC61vjg{m6(P>8;%I6h*h9 z-*Y_N^1&C8-2c+30lw}Mk9*6#A~_BAR#1$#V}6raDD?iDJ*;PK@1oY^4#u0-i#MYM z#nQZJ=s}9WZK3}{q?eoQNOZa>-|N7kU z%Y243-+(k<{AQ5P7r$)n^Tj@u&o@T=a`Dg_dDZg znK!X^$%t3X9r38)TFf(bVaQ^mqsHYWj!=VqZ*JRs3oKb$^$8a9z_a4 zJs?HU@2v#kn=Z^B;y;IIXG{2skc>CMP+nP^hVw@btWBk*&n=aB;kx_$=zMj>xv?&Z z*6cmIE3~?T_Lm84Pf;&;dlvJr>|k6EBpgc->sYg;qHu4RL*=^;4i&_qg48OfQMfx; z?tPsXzO)%^%SFn~DzE6B28y0dQH$95%K!`qY+ zowU%N#w8Wg&<&Pbc|!vX?Kf1{&@+bCSzUE+qVkGb?Y?cbk+-mh4#s}5Xf?ZmatAD4 zd8?NqRvUlZuPI)&6elf34NFnpQdDoF?e-ehB%E8~K4IY$-6srhc5S^_bg2|H?1mAK z-d-vAaVpU}4wZg^=zWKNXhF2op=VnXed17=yNUKW)TAoWR}R%_rlGxR!WKL3y(~n> z9ojuqr4$*dsITRFOApoHnj49Ja4Bx+O7ycs#d2thY27v6WQ2+?<%SXc=Hjh;+_Xwk zt*W$urC7~ndo)G0O0A(%ZhTVFi4hul7Q3dR^F`2^=b1{uFrchX%1h`?IUSlgT2s_V zdGk50+!W<@S69iyTWg)}X|D2W##PfYrd5w@M1`D`flsTH0T_eD9ryG!)#_|}qRSk& z8}%aXRtB_@gAtP6b^P1Ks$jjqr@B#%Tt zklr~OZ7Ez4XeB!D%Bg7!+@hg>fUD$cBPZHidR-IrI!GcK3bjN6{;pAO`_K;R>iF%ecZw;L#?O}`n;od`TGV*>Ztx{TDt=fMxji{!R z_cK~ww66_L^1Y;!q8DM|q!n6C(RimYSBSkDFig3u`){c_)@ zlCMK6h^8Nzm zOzR+Mp`ll!hD6FA(6qIaTn*)wy3bQy$HyBOmlE$r>m_mIE3^1(Huk5ZA4vNWYNNfA zx3Z+#$*IU!q~v}^(Vd1~fPOL>?|=o7dfA5ZO1r&+5hYs9Kub$))IpCBdDWiKI=B^< zRpK4QXq3<;XjS3*LO(%;P~Ll-elNArcw4$_D-haO%63_G&225Lnb?;e<^e1~sMK>a z)Rp8$=zrauP+$!YD2gkD5an-R;Mq)DvOk82}SFyr8mHGNez`pOH0~D zs0~T+KJt(mB0E}3v>yO#ChajNMQgG#I7#tlHPv)AG?02OiSZ^Q`B+PhcOz;@Y+Xat zpX56Wr4%Xs(ZeK<5=bHTY9CrlG??B^TYm-GNb2fdj0H)1*jnpB)TD5YAz9jJDSD#x z-3h3B8E?H%O0k&FqK3p{i)dBc^7vwihL*N^{sZkHQqDlKPKnTwwBfYy~I%bWd63Vkoaj+3~#g$=PTvsomw6!9>%=Ikm1jA)ZK? z1`Q>WE+F@PGC~*&9R$J^zBSl8MJz7D~pmarfQ7^)kUaqLdGm6^6?n-FcOBB_ND9Uq{ zpv2tD!HSM?xs5>kdIY;GXp!dqfTE!{DLRp%h;^2{>-jEya_TqLLwQb4lCY&|+xHT%uMp zrxQ(eTHOT6g1Q@biJ_|v-DxP7p$>U8?b#O`5kS&WP3);vxa_Za%Kl(Ma9HRLYk1{gQeP;S({q`1Yn2_7ZZ+dTFc~pV)FPE0FlD400`n&00eQ574$X6w4nT^{Z|;>!aW=)T7w=TTsPC@a?|A+ z(_n^a@D98TNwL&Q^p$a)tTq;>X}rrU?d?{lRV?k(u%aSw{Y#2A!CEkv*lw>aB%0&e z?O8)h4UIQ6*U*E8h8Sw&QH&bk6kdKy=ws~6@|i1s`*tVMLgay8FvXrFf58}aFGBL3 zdDOiKDM)WRC8kg=MyD(38SeBf`W^FCMfJV3J-c;giheJ1SrUB<#BaJM(No{ctd>Mg z#^4OnaaSI~uNnZw=o=po_n>m=J>)iZM2dtih4vEK1y%_++5QyDuf@mcvIF}5Zmr#- zX<|L|eg0QmJ>T+N#G`4LrDz3&D>weO_zex2p~1m>`xvN{et?PG+1uvlOLc#u08-7e!|y0j+j%A{wukLf&FZmI_ zqX;Q@@f}}DUhL52|FXOzarVjI#YE|RCI7FNx0Wwal)_RrzZCb-T-`Uj1}DTQIo^_o z_I&atU-0l=Bz~>oOT7`5wX)c3{1(9oNgL2CY(UG`iZ@;@Qt&SOE6NQqGz{MqO1ybp z^<{-dSV8jd*x|P-p2SJFv+YkjprJ?Y?s+sFUqHHe=L=o?M8x$l{A%7Q^#z8s(E0qc zt~$@8zC$$GmGZ*zwtR+DX=nc?H<8xAuw>lVL#;J+wf6i5)Qv-W!(QfE`4!Fm3-H=1 z`bI{4*i1!Lo)DBmJa@C*O`-d7!-`RQynR;bb?Nauc@*_Du7q)ej4Ny0Q^r*`?nUEn zGj6eQO^sV)TzBK%GOn+2=hLs$Wvc1+D-@ki=ZY3@uf;oGIj*;OCoJCi%66N@J8SXI zSKgKuFLxQ$?|gO8+2R$qc;~B^#TM^Qi+8@dT4V8AS-gK+e{-%^O`o_)(WwkYqp*@= zE0rGqxAnZ-oH8gpjnY@47ud?2tZF$t>-DUE= z@$xFs8$S^BU4#AC{~!G?)Ingsl^57AYRk;YGhKRo(=$ZFFsr4<+hPSS=uOl%qkx|2 zj$h1$l@I9sdAh=7?oq^uQ4QS2D7u0}uf0#^GQ3gQO}&ve;KfI8H$Z;t7wfHUI=>Yg z8_-}U<$6Q68oI3?-ZVxiZdc-Pd$dE{d?@lXRMS!%#r&4(3aGrHu&x?m5B?yuN1Ht){UD&RcZ=7O z9{=JNMZIb&%HLD{k}cWww@07(SbQJ6#cw`q7Vonr(F3nYA4;;{N=_pFwfgzcKKxbo zC}qXB=KPDx@=NGtT-j2tmQ(3OPo@P*zQENVmt5|u-Sv$2&hS?UIdlQ)AVz-=#clff z+OwC`Q!nkQaRDuHl3Ss#tpNIe!!{-E$?z{btVYSPXO8cM7{zFMCVrC;{rUfm+W60u zGCe-zDIGJ#9}pBu{`aBH^JA1a|LR~VMKiq*cYb;QUrF)bskQ%oY5z~u?te%7|EzwA zmf-;U^Ezi4o?I63h<|H_xx{!6ToceH7q5$ ziZ9Kw@;oupF~f^qgMD}qV8%Hnv*<1`Tah1Zx??8O>9&}$Bg`D}%soT!k_Apm@cSMy zB&NyqxM#9bRnKg~o%C&xlY=@rCX-g}#a(SM7tw0Rgt_OY4GGL+$SXJP7AE^~?8o4s zpYqav&s>LZg@rkskcvx@m%ejMSc<%qvovd%Ng3q>SFXI2+cPa*AkP$Z%pjUjO==^r zJ1-{^4d-WP$(@o}&IBTS=dc&0$VcNmGjkeV-2zj9);eYj&8iyELx^3F%BFCtL6i?I z!5S8%ah@rApEB()mpDaxDznxzYjIv8sg61(lOD$RAly0y>7-|##65pu&Uhx}CuQgg zk$l}il~dg_c^`#;gK`z5rk<&belo8J?w`^r&&v*=ohlY(6tkzBNrFjFngJkMNgaSC21aZ+(xIsm+s zx;kbHecnOp@G|P*nJ)0tYNHM>qh~#H3T-+LOlf-FGdFd{eixV&deJlA@EKT?=yIAN zO!liF=PmU4vNYQ>$00|Ud7jyeRKhIuObz&z-LrA3C7yW&YY$3VPAc`N!Ey#s!xuHL^`5DbH$vH7V|~tgMn^#!mAp|>=}4(UBR!M3mT28=L{+F1 z*5537ImQL|jcQcEGaWE4n7N+%dFCaI3r=+d@%#JL<&i{W2AI?zOzU+|3y%c)PDnhMj$m!dv3#GVzmmyUw^ z)YvmR3K~#`XLJ-aq86UfQIJjpg|Si4lpgntj)JB%*fTl`n$j@O=qPAPqdcRdpec>< zjE;h)G}bZUywi-PIc5l*7%6ShjOKWz-YBtS&1i{dt~O?+XR3qYc5g=OJ@XsnFq1)N z9TUz(&8Y}>ZEy?A&AFGMo}1H^juCyX0MmjRB{0{3X+?_?m^3i0X-fiA1I%4?N|@~H zOUb;`o>E$HUcbubp+Fi&NyZ`J%>0wz}!Q{TXCx4wBcmn znOlU({@a#-9(HlMIOb3=7yBjIU?K0JxKDNHE}Ve7I7?fzoGsJ`dEtnjI@8z6{3Pww zg?@0%p@36yoGx_HT`qT5^N&MLSGqKT;aa_ysyjx?H5^PgIv{a~%f+SWhTjciIfF=R zqZ=)COsI1YymjUBBGtJEHO1YIM4fxmC}q6P_t9j>ggW2HN$|HNH~VbtOWg1N&y3bs z$h=)Tpi#{d;}rbvuS|=NF&Cf|VXCv=W420Uvc&N2$fUn9&;5zngWfU&%mb#+aGY`Q z++<9a5=V&UCdSF~8GKx}BoAsT$1FyikQsLcyjAo}E-n9U$+8gp8p{zUWCrZUx9f=W zutY(eFLUDcal{FkEq`NHRu5?XpW-m{7MNbN)YZI<%0iIZn@YB0;e+Ti+)UYlIDM#x zV}?+l_Bgu)^9bc?&v7QxVeIql1JjopdFGsF`q9LMIEN6YKkac$XnzLK0mlq7KYRck z@u~8!3Frh;4WQBY5dJdh=@?#;12d4eCNRNTqQ|LdXO1(7id-E~E-*uAoM#GQ)ld-3 zP|Ds#;(U2kK*b!B>X_kF0rN#EFvI9(&-~aTLS?`Vr$z~JIMoPh)RpB7CysL!n32>^ z7%Rmn8swPJ`i-LTj*)tw0ksD+mb!XI`}0`pEli;O zc`WvAU8+F)^Eg_Rz;J&aN5#5}WF5JgbcJVh#W#>OuEK1zy69*>r(CUOz8|+J7iKp+$}|W1rK7DnU}=+ z#5~j7m}?xfIT#J*5hrJvFqBaVr^;Z)(@vji`^*Rpam*plsFfH`r-ZTjVmzfd(~23y z+ujKj_slVzG;{q-pheCoZ3)!=OrSRtm{G`UA{}td=3vO>u%cilQD0}KGHLF^__ZuB zlWDJGq}NRcGljlSVCI8)mMS>IHJnuE=V-Gq*>ik{Uy?$c=V+T}7QufOW~XDqdGI+Z z*~`_cm1`=scZ}GCS0HC9?ePrT1eP<6s`lnogNSXy>yGK@n9afM6>)D zTJ&W(o2`${r-6>y5@_qprxl*jzA>LRCNSJL7SLxR$NI)Xdaxf$-jYLY!XkRiGim`A z(UYDjoGNqoBAVuz`e2Sg@*>*om_hV1UP9$rZxPk)?<5DWqIFKVIG=f@KnaPnn11t2 zbG$vxR$?(#9U!UHN-U<@!sJjZv6$}kj9Q7s)XAq(E3ue*31j1HG0jruCz-cjq7|Oe z@%0kj@wnvP{uA6{e2wtD#}vnG&e64>taF#pF~`W*`3dD(LfPV+N|@ilETwB4Gl=rT zt7I#>oR&%)y33eVjtSdzIc@cEv`v@u0KngH^F^0a0nco~cmC{IEvFRETwPhbo#k|$ zW44f%YdIAh;^bH#TTYiGF!>O;oGLhG5M73nW`CV%1-0-@2AI5HUZEYH=?|s|m{%z_ zlqGKtw)DlzVU8*1nBjC7ObVElRMj*4E6BK5MRgP6a9*pa>oBJwaU32OYbet(;TS}7 zxWCO=)Y862{5=upeiok9m5}oql^xE^mOy>2wUp`^^~cuI^^Q3dsQ0y&>iRh9eZ5W( zIVPN`*HM4RNGk`(V;zl>R5riAL6aRbfv&nWLf1l{H)x(`GV|a>56lLtK7uvOq~VzL zZUwWEa*t$2BxeQMM1>p^mVFbY{Ef*shErwICzu&;hn!7xwPPkw+e7$GS}<=?d(ZU7 z%-9;t7Fy+*y_moF6~V2v%`?dzBGlb6rJrKSp+4KG{NI=w!caw=7qiaW>2}XdcnkYT zkh7ioc;>!&M7)-HkA`|?H`WF0le|w)dnSf;0W&*jfoE3xR3FeP&+Nr~%3k+}>=dzv zbn!5}nG5t0b@fc~Yw=s-$bBb0l)#Jz^D&J~V4eoEnC%mfv$=6)2BX;`jGqSh>xQ_3-7@mo~q-~A~y_BzL z-~`ECt=~Z!<(SYy9;B&0j;;p}(vbv)*MkRX=tL*SEdSTELK$!Q577q4ggyKa?H9)S z^C9}#GaIlzeG4T$L>EnB$;0Vg*zoOO4pV8*XrDhql|7?9{2Qw48SUXm=`PP`@BNn6 zduA+dKYj$s$7qjd+I)y_C&3)2gPz$kF`)fmPS7t2%n>jrDbHllkVnDyU`|od1m+Bw z({xP&6J3XIH0g{m*5}XAAHvvtafU8=S|sauKSPy0qoex_-Rhak;T3Ri`Gqn()5qw(&uIN*3qDC; zDk0TH!8ZvE*I4c#`i#ioR5v0{zM!aQxDM-rxi~1Dz%&9=FsPcqv<6c+SnQZVR2rVl z-C&9YpL>RF%e{`t@~q3t>bZE3OBk!?;z4;~tREH+YI;WdSn=RK&uGn;2nKpadtJ$3 ztY@^>T@uXjjM~*pgQcEPyLwr$*)v+JrGg62N&Q>~Yr*|67ToNaqp)Ypln$CYW)RiD z+em#-(&S*dkE3mo5^POixGl;AdlDFKi}Jw_2@JPI#UR^M$^8wq&SPM%35q5#!@*Pv z`g%q!e_Aj&fnm#EC76=Hu;s57RG!9?L(6}CP|q=;<*&h$JpQ(1(fzuRxjtWn#v%6` zgGQ3dTJWZzMFLX@>2C@;Brsc(uxB20PhfcVtQm}UjO@2ef#jM&!RI;maOdsTAp48L z=sskvAfID~XRkC2_mkHW)e4GwrZ$)tAg5Lk^URvV_}Un8Y6s;!vm4B8#Hk%z>zPhI zPMzR-&kXi)>IAoXM(3v6g8H7h+gr%nf~KC)*{W`EmuIqin^`xw$1^(X)eCxhCb#z- z>IHp-p=0^+G9xs+Js6t6v?uKR2TyyZDfV)gBhDSc9M9AS!*lnY!E(i3x0D<*ncvDJ~LS7a8I~-@R(ylotp>a5*W^_dC+zyr`lq>-z|d9jv1c4 zz(LvDX%Rf^nTlZABX)~mkY~;`liih;!6?t<&X6;ymcd-l495zQSN*MmC7ziK#+@<* zt2}eP@0qQG^`2?yduHq4ZO`oZ73+rAaPAQ7^32y@zC|hS3J!YaL^D~(wF!=SCPxN- zD*`&V2~K;4doQor+6KRShI=o2ux*2^vk(<4vb$T#o=m&o63@Jfym9mgGskQR&q0DCbGcS0(0-hZyeG}Bnam_ES|1<$JB52{5OgfD{&sJbY;+m?Y$}=A)m|#W)d>cknac_Ad$Vgy# zb^K(|DS=_hql3O)&f_z%#s@Pd*qRWBQ#}>za?E6!g%hY=h%+|$$}_zC{V14m!D-L% z4mmTKLH4Dh;U4U73MN zFgt>^p82ItKn=j`3=SqR8DMq=xmFVXCKJbL3uaGnT>{e?%--Ox1m-?4p9MobGamOR z`h(dQtV&>pfcYXgmcWb#b1*2iitslXr$GTt0&_Uv->=}_Qn@pJ86b*RrGvgc&RfUO zD-q{durz^L0Omx%?-H>bj?)&*_d%)E!sNvIl-uivpoeEtP(RBM=TyMoese00vj)uR zpz0c7-tc+-96XS~a9+Oz^AZ@&>uhkqGhgF;hkMKKK|$CGmdp(IjdMX=$Amkje*}#j z6YgIA5p?yb&OHZj8L9pV9!Ox`1w)awo~ijsKs&)iB3nK4JnC>Cn50No%s!m^y`Ato zAz-padO0S{J$q!JVfv2y1y-1b}Wui9>fV5-W4h6m}V{&EC3j8!)wn${LV zZ;{A)&wPY0W4=b&i$#ukrrZ30V#up_w0i(w>O#X(5q_D0^J?IkSY(oC zrl8j~N2=11X$edlFv$`2i#XLujP6cgQX;E7bKEmmMBepG^?Rj$%0_sF#i=TJrhMe2 zXNF-$>jpU$BIg_vwnfEAR*VIVWxJDHF_Pk#$&{^})N{qi1CE(MPb2sHA?K>di$2cX zQzO&|%rz0VE@Zw zFyc6`w8#L@oB+dlRf&vDU^rFP2wN)L$gtgvYLORxoc9X{Gz6)tMbQNQ5uqURa#0aQTV^!pU?aAJ;(p&@%TOd{!j1E=X~zx^8K7M+Ut>a z0j_IAs;wJH+*IxL?LypC?e(;Bkt%WY_WEim+U590KZb_ZUN77vVzjFhl2RdM(@42* zu)V&7WhuV(QHZ0r*IRr>F~wT#LHq&^xk1ksGF5B$C$4KCo%Nhb&KWWq|9X>N%d!mn zGik5mO?vib8Pk|kW$6!Ykuv0ZxqJ2&eYQvN6OYziU&9icA@6)|!^cfzs#R0uuDl+4 zCQJ1n_>CIn>8X!mY0(?I@VnsYf!<=PO!Y^RtZ8q34$I|Oks4sN?5!XALdJZ6)>w*s z`s&BOloH2pF4Tto^`T!$`R#5csZ~|j1x__s&t!R>C0oz@M#lVx-C0zgd-aYxq>MrP&`$4r^^9sMSKxgwlA-#V zAEbl^qh65v^hP);jWIt)!iOl~F#Q^jltG5;9a*+tifawX1N!oxWU2;OjkiM{*3)aG z96KWWeY9T461iIDJX+7*BV%U2gLe~=YOFqUuau%|u!9RSPR~3bM2y}8kO_L36s;wW z^($nO-tC}F^$uE&Qcc!O8%Kx&{%-sE;( zq-W6-LU~?B2`Qgf^&T-P*WwP5WQjgeh`YADrWXovOZb|;LP);$5z5mPwRlah!M!x) zd@JVHjgS?3x4Kdu#7OT6S*3T!sz>Wa@BJ8Ykac>lkbG_JIyrMU>fP&6jC=HQy}uCm z=;iteAtf696&-rdrCk5>e^UACi=&JFdKAYh*INj2^VzI_BBWUR;SW67y;##W>$De; zYSF#Or{yEn7X9P}QW{{5BKcfTzlfw*n}&64CS1#7aTc|YdZ%88 zMeU>Bsn=&w`>1#7DJ*Iq^-euiil%mPSL^;%adb`X{r*8uVo`g)f7C-PYRC9)J<6i? z{O{3ISk&7Dd-Mz;?wbO8^?zB?YUQ5oy?T>N<KfIF&5Qg2lQkqi55Gk*I`jDc37{^ zqFU^Tp2DJ9tX6N;NY+%f*fG5`i)yiB`b{jVHID23S=4(;$MwgB6lv!@jyI~&8prjg zgt#sCn?Bnk)M6*}B_4SLa#C;8n93tl(a1clXL*E1<{AAnAqNxfd{+OOMYZ!;eFuwb z=d*gX5ci#tv-(*XlXyeqtp2x*3Dm(nr4{?EZe1qYC(sZ=|8`c72s!Ae{yL}EWl{Zg zPETP`{dG=nEb~dc-SD?Q>T=ogstq;c86gJ~V_q|6v#1`_jrlTF;LT^{yE%rjl%*ry ziKe%83}Y3iQnSf0)(CNX$8UTt#O)p1*x?cC9mm-35$c^JBt5{U;#EsP~s(0eXMj>wR#Eo4-^0h*&b#%QEH(KECh@M8Mc^WrHv8cCx;>G}6 z$0H`ur*UJLltiD#jgc&>Pvgd8Lfk%0HlAQneVS}cXHk8cY|N69=+pCzViq+A&o}0= zs2;3uEEZCc=)nfYJ1nXP8yIV(BxZL5V-wysq=+7~lUqq8~1Txw*n&@}+%+|cOHLT7U+53|r2{}bfX(3r(S z*L9RnW8+1S(6KHvUiHWZ$Q8yr9;t*}X;iV$6&mG}X8gjUuJoE20qmlqGSihF<TxQy`gJfaW>GVxgF*X9=vZo|bTHC{xbJq| zV2qP7iM6hiF_}fpluV<5MXh5u8P5oD?{l+^7i3K0T##kVVNt71mNB12)ild^U5H!L zEMpamIuB+UA95;H(=1~hi>hgsv57_1G|Sj3C9x)SHNIg{YeF}J{_Y;yM^iPu&Dbxb zA~D~47)jSsOofl?o{fLI!>A`Eu>$lmt`g$5MsK5~5O-DYZFG{U67AE+=)t1er;jm+ zMYT^KV>pYddmrOb7FG8?##9zn_dZ6El*C(^eT^k7s#W_MD_K-)^fk&@RBQA%zGP8X zDFck%Eb3f2$T-5H#>ODyEQ?wphZq60ow}BwHq1886LQc|HND$7pGCbhd$-ZRlZs~3 z-NvOt+_m>^qY;Zb`aQ;F|1Z^jMl+Al72|zIdrqZlG0YgmqG~$Km?6dYCGM@M^uvv} zJo!+0Mi}pTgq|plFh21J{kHXhu}O&g+t!0dm5>r`+4J&Q+=Ip$mhhw436Irgq)|Ve zj#;AJxff5zArBdCSz6%f!C}b5MwUl@gFIr4VM#$gXCaRnOIb$j#r4QW{LW~cX1V)4 z{89{=XlU2TW0BN_JYh6p>5bo;E`j75xkBW4z8o^um?FfTxzmkBEUFEs8&xc-oo5uJmeATi)W2-9-+Q?&Y0qnCv>dc z#vCCPfjwbkiO!CNtGCX}VW_blATxP8B$P~!i#&(vAIiHor9*@uy;P;GLkIX`<_l+2?A?Pi= zr!9H?v&y(#h&y^$8HGX)`ly7(NVUqivV&}m)3{S?7QqgGBU8vhA03@yJ~r}ikTLC+ z;MppUzRp-6e%OVAnDxfFjxy$vk@$rdve9VS$xZbRq|)dqM3!e22JnbA#R`kW}Fn__UUiN^<6|h+OdW{trVw_Q$}wgZl9hu zv@D8od+;x#g5>~ufUfz^8CzMXHK;Gn85ebxskU*9&rD^Zno^9s&=pm}x``N7!n)>GQJxY__3{N~>Mc|+DHBkO z3(a&E)q@wAGd)5v7n=(_LVcQQ)_R2c^iuOLA#Q6lGIiXIczd~#84==*xy2*2L%WeV zK#D`>3wocZu{l(TyVI$$Ia!E%4ba$}DWpVeo}_D4C{JUvilu&Ce4`(7nR$k#BVwkm z*R;z`v)g}5c)3|$2zG|x8f6z!U2f*Etj2T11CSxuoccehp5|0>9Q`w#shgY6vZ(#@&CNn7f!lCqq^}AzHx~%;=Dbvh>_OV&+T7g4 z`Ka==Fu(T1&~;r4^N>dUfY+$kk6HeWtHXl3r_7*)bn=4p-zeIQ$-mFe#x$`d$u zquh2oy_qvMvU->(5juxd=^#a zPUd{hN7b~m`7(>DX=igWi|Vh=<`Ndw7oE-JEH@3pvmMl{v$=+4CiZ4XX>q$O^LJBx z+N&a_1Iu0rwMM3yBSh}%G|M&ZX7dS-QM0LwIg6ziy=j7&F6M_U^yMy^ab3(ZDLyq% zyO>FLh@<<|JndqhCnZ2Dq)c^{kbI}hGL_E=A?`}j#T?D~s2=QMj%QIl*cCOQe=021 zgRZC^yhX)?RS$ME^F%&w4|Y>A7R@;7!EWYEK9=giL`-6=b~m4sF#*+s-Bmte)q}36 z9&|+3n^^nM&*yqxI(w z^HUbpKE2E@r8ug6`j|g(jB1TO<{^$zqqmQFibd75kE!<({qCrm_A#R@s-}I+6c$y} zKIWA|$~9GsK4xnn?nvls-XK$nwfAeRY<NS5?oO=?XO}IBVnKt(eJ4k zNv_Zy7&_KKvpXMaBfg|@Q4qh{t5k`RFxc!ZV-h1_a3V&v&tS73=c7i{VDm1P26xF_ z4R<9{sWu#9-p4u9NYE|pgf~aB&`6;2WOFJx5-8>#bF7GwBOzXn{Q%}PnJO_7hMLc_ zsP?(fTr4Fq67DzO;TUx$y5IbSMYYfU=4MW%#zv0$EsLsqj=7IT)how5E=2Y_l{v@! zM~K_+!%cs0xss^!&Tuo#qRu-b%sMP;Bs`$nFfkHbQ8mp~F^Q2dQb}SYBqVXRaz)j2 zq51DB~WDnBY4WrDqGA7Z351UV&ybG~cO~eGUWqBTNCgnY|9?Sh4^S;@Hh1!|k(I_>K-z`&pSB8C7$Y+&#hDFV$ zRi<^1Td(s(OdXG04Ee}BCq*m7v6@3ZHLZJP&Qnnyk}@;G@-5ae`fuyadMx*09V6Lb zrn20Nb&O=Ac_m9*%zK(&<>s|4AK`wQ-kscJwqv3GYK3D}n2qlfM;CpO3E6Cplj7X` zqr7X|Vm`&v6La}i#B4ENWO=HteCDvlT*xvQF}I7D4_N4pIU3GFF8Tg(es)S9`)Y$YYJW^OUFSXA$9G5c_;hd;vKVGzg4 z7UJHmZ!x#tPsehv0k)Xig}7_N7IT+JXr^p24|s&0^M7t07vkppg<1Ll<&1Z8&|mlB z=wFy;a;2nPjr|*tFU_S7k`#;YiqJmlugtA14UpdutSwdMX_mz&@Cyu5Z8MFLG8KIt zWgO&d^I{hIf(pqu=JhPJV&_A)o4tj&_1a+$@(9&yhnee*d zxryGmbmeul+;b?8EA&>&OOV|r?V9!W;BK=_NQLix>|uCS#B3i$G5FdlWI5y~bLQy( z9(|u#D5SziE5Ij+*=J@y^52*PW{!{&?FID3XNWmq&hW%kK@OU43aJRBU|ds+9Wqz4 zTn(Wd51E^#_`dEZYkJuHmSrD=uD%YN$Ay$@hq2G$d*u9!*qT!J)|%@*LT9w2W|c?w;OM`a=N=PB59D4h z->^PzI^$${)b}%wo0F9AX!*^YB_;9AtKZC%9_fzy{ASLcAaky{Kwe#(Fqa6)7hmZ) zgrlD@Ygn@J+s$#v?`F#5B2}W7PntOsNy>$sLCh&L^$8MpjGZx?{ExKo2+e{s=Jg(- z+5M;4*&|j3c7mBhr8pO2b*~Hg*US~-K7;+&JS)UqTmDUq@JmO^{n)Oo-z(Q%%|a3I zk2?!op;>Stj-^?~WLbChRRP@!v8d6jTPavQX>HkdwOnmXYmgAPH7slB|HvaAq1Ldh zsUD%$@LLN!(ir7&EVDqA#~FWCUekuGYlRdf+9zbSVc9WPUJ-??PAngs!BbM?6S8h$ z`TCSkyH3a*EcE^|eU~p}4U{6*jkY4DSW04EM6KmQ+`fofX)|PN>_yI5NENj@&XjTj z(hCx|MzK6pPuB)P&a*tqY%&`83&wI}%7es|8EFJ$U;J zQs2sDNybyGsgMTNESAz&aHR*iz=}OZ`QWSCpZT=sAt_e6kYeq5ygfY^a*;)UA&2^O z^&fcq405rBS4TyvAWFCra;Y^+NU_!xcj})&8d>Sj$W%vDaJGjuv0~3k`SwmcC5EI~ zy`PgZ_6=Rz2Wer|FO)JF>qjl*TC0+!J+5TVK(4c970H;<*yXO{?Tpq|*7H&>!?jHu z($?x-ETu9{*HR%jSSx0e6yeKu_?;2b$*L4mtWnM_A)T#RFUV9kBj@&zo2-lyDf@=| zv@Vb?)*6=h2%pvma;w$uMH#cOKhC+39#%OEeI@Kc$nDmCA?_^bWz`CC*UVm4;T+1R z!k2_m`5R%kQ{3%%faO`pAlBt%QEIe$k&L;wWhH=fb-dpkdfAEmT@ej ztey*Hs<{vOw4V_(+R9_;-9*=pL&jLgS^D7jroSMMT7`>bs(Vl#rxN$D)_#^AEEBD? z#WLn~lqZImC#;DqB`kSXIm;`%FxwC_#Y)Faqt@66xfC+ZTFNpDW0s`A%6wJEj9i6P z4KmYO&N7(gDGRS2i+u7?o+}VD%i8~%l=4@xs}b^?Rr!XL_mGO(`FX4VQj%#}9)#L? zwzXYIzE;Wklvo*W$yAGx4;_83mB(@#Lix5-^BsyQ&=%pGOLr4Xt;~0&jK=7t zvHGU<{t9`lP6Ke?grmP@RSGH4W+8_D+cGO-rA##h(i$<#tf-df@+dK&qA2Y?gn^Wz73lxsU>FJYR3DvbG9wzsS1E8uY#_;fv^#n~~2d zYq=2jd#4{*3rb}SU6*x3%!gKVm6V6B(zV`@kE|?~6BuIyA)i<^EcwVe8?wgg{Q;$N z&pT_auB%Dpnot-2w$|$H5xUY_XKfc!5oqweyaxEx+Qo7WgzoGq?gXIPcJ1K)H1d%iR#V3n;hRNb!BwO~=f$W+fj=$X}KE03ksLHsRV zkwslcZLyXMDfg+X?k(1?H6owDJKQ^;Tl-lyaqoO?9c8%_=SsTf-)fy=c@RSB zw_4g-S)PIhvV>n)Gg(?R@`>jFUt056X0Uu^EobSDR8)&9>m!zl38|10*u^ERvcBY) zzqy1})=rjuwCY&YeVg?=O9{90HY>hPmN1+9>uW2OWeoS%*VYP_S5stPd}EcdtbtHp zd}F15D)Z^jG25+7meCxu-CE7^4SI*}^>$d5EI&gipB+~JGLcW92hRA^s^41KEJJYJ zL0@_L)_Ra-&?xy7^jm8z%L5P^tKV8ru-w{K9&4xdG|RG=<&%n?Rx!(?$oVroT-s^9 zs`BAcx6@j|k_Vv@?zGmiTsa<3a&SddZB?+OLmo#vS6fv=Zq;dfT1Kv(Ks&B7b$I=zZ2s zk6_`~_FFYlw9?l2wg$?x-}+06vmZjOalndfq|{Tj^X}2L`G`4SUCR>8(6uFygH}%= z11&c#}@8CX*xzgSx_kl`LmK;+?v^oKL%3>yH1+5HB<1i$QkwZ& z)JU;jkuBE3Uw@C3-b-XmOaC;M7|!q1hOPW(SlYfWV>0|{du6J+OQp2+FJdWuOQ!1J zuVLx73|Aj0Pe*_DKAEaDmoU>mg=Hs~u#0~Q%LA9ooNw`0uzZf1E=4}w{kvE;qYbI1 zJ^Tyy%bfGkKJP;A@Yg>e<#zJ(N`6P&)j=NfryP@#*A`dzD9?C*DT~%b%H#f%EM`O8u_4tYe~aTX z)nn)3?ie!J@0=i!BeM-8-#_Yi61Nsl`p0?X4#Yg^FZIYk$kYDNNjKFn$g}=Or8xEQ z1w#6&Tao_>mRldhU4oDTmT$xI_lqL`vn)Lq$$Rr6e~FNM?Jk^CvZ<{8tsK(@@{)s| z_U{tnUL!s4f8mr{9{R4)^ZqwIG72Ru_J8D&36K~3o27`q=|)cmUi5Dl;$GLi=>JCv zUmwo#hfdS6+}&bx{B?wsXwz^7F%3D-@n7YU7a%YBJ9=aZ-@(&@+;(1zxl7|FO5>2 zf|U6u3dtAW&^`xQ?_VmUSX1B7-iU3TH~`v5eM7t4ze9-IKAZeM3US+KlmDPcsC_E@ z$2>yq^O^sQN2q-&{f>{0#q;8GJbU+tbRpVyoZo32Zu2)_`D&INtK0lnc%%+eeeG`{ zWV(3PMEhI5@gL`yCtKlJ9AdWn9Yf|!(ipPCe<916LAsWTa(wG=CdHvW6_n~*|4kmD zR6G6sS@t0nUE5UqM+hm>+P{GPl8CAHj}s!#=IM~{{5xf;#8dQL{$D(D17ddh&k4a7 z*Sq1#31qiF+oCd;`~2N~+U<}U|1=>5+KwG~LIc_FUyXZmw4t{9EnT|{a?t-BO9^&o z{cYm>?sxF(0>#|=o|HrWCPMPHYXW#;hE#|B$7PID2TymYUWfg9NT!;L{3xHp{>ChI zTi{tGQvKpDWEq2)(U2qlSz(!K@bf-x9HiEtfp_ES=x?Gtd61+26)b&Go|%wi{@%D& zqnNdbq5H2B{w&-xknFn{-!VhX@BTR~|J)?6Gf(*!NlEN#IOSi#F}@D?d)r8L%3q55 z&~?ikjJP)-XZ**76lr-Ia0dzb$KMmrvv90L?-=$-DdLxiO^7k<87!lo^l6_9DPkG? zloZpR&r;b7?*<~qvR@NYqTMu9e&xw;mwV&~#Mt&$Ty1-g9p*MqC+wXg1JdTxNmrK!R;yobx zwX2O?coiKzU#nh(>kY)TvCCLGFP75Qu419TFEa}0mEm8GkjNT|jQIOIa?$wcp#2j`lg0@6j6ce$$QinijHsR&>_24-nJIJ}xEkjj7Id z<~1^=brf&4Af~f@PDruVZ>p|sf!u7TVC#RFR>&9*N1s|)46|uy;X=i5(e7i@V87*i$vWA+CK?#$HqYWkVj~2+-aZi2#ue?_F0cm%w4wC zigI>mZnhm0;vOs8ULvGK>oZ2zen%~`?NX1NgWPR*#(gQx<+LoHX5gDk_t?EX5{3-5 zD_C|SpSqA?_I8h447uNKggai!r*BODHIN*;osc5@?GE`RvEg=(ltgQcu<-$_|I}iH z?FcE+vT%<@fBR#EeTNihTytH!499xFzKdgi{L!a16_V?bmXKU~5XOzSJP+DqgcND| zJYDP}7-=tIDa2iA8>D*3j^Fa%@{F=mgt)Cb%3g50%vqIilzmW2qJ$6ICphMjr**9} za(>vxXRkzgNNC5xXgjCRe@pm?J(=bAC-IlpkZO$mj7R!H9<`UUtQ(DQKSIXYpL=8& zWW2pwh&whOw-0j67_0!J5c9bGyGO=DCffMKmMCF1V)7xA>|~Y;aL4c@4cvILTE*Xt*9Zi0N?hD@=Wc;o}fRQqj~e~{0okZJZO9;t*(w|`)H z7x`?16xen8%bc&Dhq#uzrvEV1v?2LFW6ss3ny$`bo+Atm@d7^^W- zy=JfR$Z*K(cJ}?0bBR_L_Yk8XZ`h+eG9I$jo|Qu}#o91ji{?Szv=?}Ub_Bg;`-jUC z_CgHFGW!Z4ZW}JQn|p-XaJik~5o*J??M@y+wX}EbTRcLk-nDyqgi@`r2YZB4y=UL& z5lZ!*J<=nT>V12h5VyZp*?B_T)n=95{6X11YjCwV19e|zxARCL+ly5v)WE)p>^XG$R~C`kGut0V~-Hxw%9s*v`45G>+HupLbdqRo-V|##d^C? zh+B*GcE(uQJB_EwF}B_w$1;V-;YRx{AthP^oIz+rZL-(1RN(1RDN0yjf8&u)A(eKG zM=Bv(?BgEU0r}FN@~CVLx|XPcd~MJ6$T7$cd(mSuCI{Co=OESg3XeGW(%|>@uyHb` z7*_?!kX<(ImZsT#c7RX27_!@*#xf9B7EK^E_9B)UH_DN@*IqBgZLxj!RvD9+r~7Pk zy39Fv)~8*AeD>Khg}7(NL-q$!66c-6b~(o!sxQBea@aNtWSN`bvHw>aa2IK3vW!fX za>Sm-vSot&eoL)g&T)U}Q%&#!i1hRm6y8{{|pT9#zAb6?0wyOT%g zYmI-{cX;Fx$XPqrBTqp7whKJ+6hw1od!z(nI62SAqd#*2-n)U=&Nz>}1#z4*kF10Q zoUfjhsouqvC;iQ`Bu%PzW9fju#@AFN=U?+ z=#eT&)GutW$meor`vSKXEg@Gp+DlR{ z%Ew=VgIwukvxFg?Ax)hnEX~@>udOz7R!DKy;tB2@h-v1m=9qWx#54P6`X{EzCyDHBJ+c+z+|dnZc4$jVm}vx-(mdyN+Gwyy1~-9OF7?y+_6(RV!zk zkP6>1{IWk0(%PXn4Y2C?8Y5>)mEq7nekD)iEOWi{mlW-y*>VS08^>8FVuaB5n%X$I z9@($~`+uAjEcH-R%DKI>PKu9&zLwd+`I2MShJ4x-l;=igKgZmR^_716yV0qGt0gMW zOtc|=>+D9Si4b?~z0nyW#BGfmo%=mPtNM-3D38#p-pQHZ5jvw~I#WDCsWP3XJwmB& za$XSPj=C<+A|Y;hx;TecQ<>d$tc!E)M?w}B=XUsoq#UXGIw>Cc0@BY(DU&hkOx@pUDa7ra0Zv4*IDQhN_C&}hDRvXea;GxP^w|h#~z_nInD-;P^ui~bB|D};m!_^Y)3mk;2f8t zZQLNA#pOC_>#5}n67L-4IybQ#_!{4eL8@G*AIqY_y7-1bu9M5si=0DOs8q>62nsnnYtxy~RK`Xz#Xt$5I>WKqu{M>^F)+-GPb9jBb~f$%f5 zhn%`Xei#>8FWPr#ZE3u+TBW?;8g0(j$)^fW8VMG zC;kp^vGWlN{VnzJIM!^ZT!`Bjvz>pGa9_OO#44$TCEChDv<*_d;A9FZ(#Fij{VU`} zXR8qRm!o;kb{Uhns+{M1E5u!G<~h4PLi1~$^Rq{2e$97&^T=Er3s1K-H>oR&mzS!9+B{3@(JHN0jh~ukVcuum|F}KKmUxHb% z2uEM++%Cm=`8xT&-eM<5h&u-tJEJ&8&A7!*3Fo8c*J7tsh&#U)JH0=b$5Qiav9pLp z&9BAINhwaJU2-SbD~`EUrcyKR6=x=knsKi>FR+|`TG!q{Enaor@(6vmdWloU@($`w zf8XkL=bT4qx8WO(^@ThZJrSgtvebzQ$=5!ZrE9ApZ#rpT$rzd`TOe;c86K$;lIf8j zAuF6YQW7(z)LAGc@k>pq^PWeJB2}rg-Xk;;RykjJghs*#PK^+EBz)+!s1hYiybH71 z$k}aQXuXf5RWwnD(H{+P@s`Zf*6C&TmJBbp0n)Y*KE^8*Y0o#Jyi`da6l6ldA_^6wO{bHY1C z-4o?m=hXFxg|pE*r?C|6ZOrZv)H9UYRq8W5)1Vugt087g#Qjuz-GgFA$%Nw25N_agicQ$$Qxg4p=o$VfJ2HE8NF2yOYE8ouh z%(1=`<#Cp*ko(y_bLz4@2N{jt{mjXfB5IM2d_Hq}NlElYrIW)#Zwk<_FqO_*oR9j; z@|DgS7WJ3qE1kV8m3-w;=^PW{wqd37hm1*#jY>!Rp33Zw!%F9LjSy`ze&wWZr&l`v z3c=qrc}LgUqRg9}&>o7BGp-Y4i}R`wx$@IG|An){BlKR|mrlxl(LUnn*@&rf(mXSrbaTEJ@}pDhkq00@ITs%iG1`VC ze47qqW3Q7gq)2-yiam;DB(ph&zC|zwsrEZ3h2(1!QJzVV1J1<5GN0{u;z}_Go%z2= z(WmM{es-2iaYjv%cT$I(*b#~;(ITVpOBP!7kTXa~k(N8mr%gvbhn)|F6gYQ5MkBh` zsTESJU0NvnqSm>-mh#Ehp2?MC?5NX2NP$Lsw&`0IN1ee!@V*1&8RYz{Gm9m=zAog1 zQ|(FhB4SQEX-8$wS7R2u4Ee*UbBv@wBcYo9=`3g2{H;%W1M-(M`#8n8v+1m}RY<<} z09tG%V$L}!zsY=V;#U3J$rj?StN%Ewh2(3Ev8qw;{Nsd9h*Zu<+~beNG5&EfrD&hm z!<*5_`5&jJkf}n}LjH9Y2+7x0BW5GS7r6L$dGyUQbZsld45SH}s*zBPKQQQ|Jl2u6 z@;)~ZSSdy9mZ!Q00%a_;Tb?8-P{s1_QQ1DhKg)Bo4VRS=cfm)XC&*QtGkn;j(gcN8m%)vW`kUD{- zf5@EwF2W9f$oYZPKcyUlBz=Ql(gUR|CsyfN3gn`|uCp@c(?aYyDZ_gjftSxosr#P% zyGxA%t63H{lz%7dvcRmrWz3?tbuA6~ToG{oA<5Tny-xO5(?FJxA}xyX(;6{N1Jx}5 z;OHG8%>r{YOnbD(LC9^8mVxEElvVZd*Zm+FfkQ&@P6UocE#EFsYEVo8T2=nl!uEki zrj*C8QIg5>14aUksP=)^SS-v_DWxo>Xv2GOtoDJ<7Uff-{e)D*Asqreh2(4dFy`rb zOUJ-0Aw@!H-MBGu(l3urF;v3NfhtIG zv_D43zUUIj3s9;et^aTMU$(ZL5lLzS*sA0*4%!Kp^qy#CZ zKX);!hgnohKu*{kM zHWOWS4+vbyG96d2QW^`%7k|HL9&#QKSP-F9?vBKPfu%wU#O~{dkk7zCEH2AKyRR1^ z)t!MQEP4I#OCKaVu$^V$%|2}vPYz#zZjAX50uuG(hsSqcSZ!9 z^QClPc_1)|r4>tVV1bZ)&0%>kuwO`tc9zyLy6mksI;66R8RVS6oUt7i;xU9+IL! z4$CC0s~sWF2llhn&BoJ2NO7RBp-fd&q6;Yrv}+{ghOkfMGdHlvBi)f|ULdWpj2W0J zk2OC~$kK&VEeM?S2&H-{kaw9(buXuSIj~=dThoPsS|KIcmIC~Kic|{&^{=2*_-l;# zRVY}7eXxO9EOeDJ2r;h)N?B;0lDrZ4PDqh9^C4Z!fh-H0VcGR5)B?`;!0U&lDUwzfoUu! zSK{{+$c8|Bfdworkq_1DRG`u$ zRIf9EGc5O@#STHv1=5?zoO`^eYbPPTq){HxcHsA>q_nELIX@%cp(jg6!np`c*L(&#~+ z9_a?TG^w1WVk6!KhFq3Z%QE0Q>>hzMNy=<4bMAtbjlLM(G-)DBI^J2MRB1`eS!hid z32C0R%Olh~*Cf?%A@iAu@{C1HdQz50CP7*!jbr%&Yg7TGZPF5!I%}k~Puj{-i{B!j zLCg(F&NVV;k~xshN&P+YI^>q5_D$-2t<2{Q^ztgC8l1F<<+fwE z?}Q9Z+U}7rAUR2)mNJ!roJmF|^<;S#HT@1UCaIjIKH6{}WPH*|j~szaOlp!Y^P!lN zki4Y+9{C3{Evb;@^ImwD3i4D^rALxb?q`#n>tsF@QwLIrC44ifokysIZzqjm*@>~5hGV^-w4CKTF7ro8`#nNs zUYpdowaj@lQe{9kBn@SuR8))4k`{S{YOyt`#*?ZO^CVLA!2k9pjq*r;$bqCXmS^z(%Ds?Zl1_SLB;?nm^foe|(dgxgkW)z$Ju)5g zXHpeQXcoT20{JH?)>ft>p;W$Lf0khoI+hij?GZXwAh?TVH;(lpBo=JaPUb_h2vR4Q z$I=4%yaj0xEcXbdN(si=%T$*jhE|ExV5UdjLrkM!9?MCtSCilhk5IkRf`?ezBOj{Q zHNiUggDG^5BcXbw2Qyi6u#&8WWCW+NG{ju4fV2rN7UG_*+69+$44s?4!2j9>>)#-A zrgI9(4Z%4qwDRwSbPAdsWemwrkj&sTmKS#Wv|k`ygBRQ=V`w!#3F#h8_sBU&kKjO- zUd?e&iC=kp1#>(Sf%FMZV4?N7K4d^}3Ja~z7YTWaWqC3FlAw_1S-K!+88eUNt*$<; zp@^wuq1Ct)Z*va`*0Q{WoSP%&?qIBwtOc#lts(aYhqAnam>VENgR5E8ihWujoe<3F;>rlbObX^?Ng06u{upFxuufMgFQZkbL!JsQ zVR;-eg^=fhskg`&nm2PH#ldMTGy^E+#b7N9jY^7{A8dE4OhxPbE0Bf3Q7p8Q&}exz zSf{&;A)(RoT5#8GQfQS}j#O_3vwFDl0c3e_D+|qTig_>Ct*4BkIk*wBD!AGspF=(j zrr$1OXzuKQd=ebR@;=)6N64DsG8URm2Ow*MYlXP8XHJd7evEEYDZ2CO7nMKW}uY!j?LhZ9H*r1P0rDn>G;H4~TUhD`qWl^(W zN3cJOngu@uC$gw9|6{Or3ms=sWA(<+))6vBjn$h%Cpp*DQfOK9a_zz=FW4W)W>9unkmJh-YjaSycp^|LB^<=GB-49k`y&l zUJ5mS!WEh+i$eQZ)J%CjG-tAmQ8VSuP>VcQXr?R+?PpOl<=s$fzKl`p{EE<>ENbqo z42>1y&Ykx{c^sqW&U>M>DKZ}tYSmSt&{QdEHhmJ>SFB?+f>TUP=rr+X;vfu3s#L?knkP z#~*fPOHpG!7%pT{V?G)_$)d*mdEr4X$W&^~*9#Z2s4;(GID4*)QDgq1@R*mRs4;(O zIQ3;$Xv{Ybp24CD5V z5q?|v(WO$<2)`q|)FU*)dxw2*${2M9=^wt(BXkBC7`~B3&4RnaU0Ku_J0Kocnpi0Ut_{^SkxJ0Y!gffP04W{2}Wl%i(b z+_1A+ikfjRg>zZdj9VPu&Z1`AYhmXjnM%#LH^XIXU7_)_EL^!kikflnhG%Vbg=XA) z;hE)9)Qnpl9sHERsqwred=ZNpy<5UfSk%be67I&L#^KiRA{I4%z6ft+QKRL{ z@NpJZi?700ZKk8UWv&XR3n|gmh^h+jXHk9eP57QI|4p?cJVHo`rfRw)JjWwc)1Bd; zSyWAb2p?lnHT@xchDFu%hj7a0vVBxde+-XeQ8nEiUcjPi`ct@!Mb)$>d{&5C(>-Bx ztIS!|bWgaHMb&hF_}VZ2ThoK#HbUH*9t@B12-WmZcqfahX>GWMMb)%6T+53AE{18y}XE-d%BkMH@@6`MmPLU-{wDVu##w_Yse}$W;4ZB7d@cmFp8?Me2VeV=D9TH2}l}B3)Q^A?AKaFw)y24?`l6?JQ5>Iph)tf7on;SxQT-G#X^}}$k z1wW9uJBP1}ED=I$3%*>6nCl`R3X!w&ln^ArKNaCnCpo`bMOZAlN;!uZR~~sx7yFS~ zM=C@<6~6h6eVX+ho-IdeSf~wyLjLwhJxH6#g}bPP6~4DPrhVjck5J4FeDreb_m223 z4WtvxCTkCRx#|B%YsgKJjv^m?|L_35lMLw^8O}N1Fjek!>KYlva_I*??KZ^R5~*dG zl7aV|Al)Lff0QMp5p^G=N8|uY`$zHhMM%%cNsl}Vxm~qbct*T5`WlpU8JCG1O}`TC-1NzZBmW z4t@&|InVovj$ZEj0PQ><(m%4A#n(?4wHOeo^vElS85l{gk#(mPayevhWUiFNnt4~` z4JEt+3`z9YYP8tcSmYjzTn=uJJNBFJk~UnaHELXu$M%ROj@Jv ziBt)x(A22AH*!))xu%XaG;-%YN+rkq=g4PRB*!Dwkl~T9gvgwaKt@JtJaQH?I?{SS zd4$IEq{wPcrN&`?q|*T^A$GH1UtApD$D9)BFQiCoj4^f*WO`(q5LweJAWug2 z331!-nTUCi@~O~h4$_Q!Hj*R6ZPlVkou4UYs^j`w9Cvm@pq zkx$_C2l5+Svm+ga$iBEi#0>Jtt&lm9i5?jYc{#GcBM(59M0y{loMkO0LzYKA72@_z zY2>sJw|7b-#xIl#e|Lb#*s4eaAr+45i&c?JrD&=zRz+?U;`YS{k!~KLzW69Ifb&tk zyf$*TkOC*VR9-o(jSQEfy@TrwdNpKi|)~NCo!( z%DKE&tt}SaJJ1zTnUZiazq{m$s%b(jx;LjDbY&KPU8418y~@X;9WNB)3hfdk*^r2# z{i`G!6GFQLDWCF$&{gh#g?8ZlS8}lPN=o>M96xHOZh7Pamdz+3wPAVWGL{&AVUcp3 z6kowTx>k(xlt*r18N%{eWC+V6kXI11CGrSMcb2V@nJf#iYmvT?`(KJhi+RW^?#&^ zkYa7r_1J@lV{MB}7b52ueZ%+b$Q&W^x{g-A?U92*Dl|1(c1C`c;;7NGGjfVWjh3Ae z{V0{Nz)>sP&PbAw3b8|p`h91l1&bO#)sZnQYW!43=CY{q^IfEjMfKPBk@~-i*3eXc z{TTU}MUAnV$N?5L#`Z+&ACsxn7~2!+$fA0APo$7V_42;R<;P_zH4YC%vRKqOJP;{7 zK{5H-ofw%^(*uzuEL~6H?R&_UVcCmclW8Ag zZRAUqPq15_Wj+;2m6EszIUPyom|fV>N^|gZWFkuqWHi#Bj(j1+9hs*i)k5T` zJBXZ5M+(l+v84P4IU6Y!BFEuhkbfd&LMjsb@O{xQSk&zHMZafJv)dQ_S&F9Spf7ro zMU7`))c2>TX<|J4q7I81&%S6(NQu^}kFJ?N;NBrROU5L|vl0EAMU7`OdYDCxXES<& zMU7`Gdc|Mj=$abO{%AUj8qa}f6^k0rq3977HJ-!KUs=?64oB;sl{u^N9F8_-QR6up zUCyFbt$6fD7B#|?qxLzOj~e00(Uw9AoM||lQ!gh++p?(hS8}v7i#l5+N4vAAvsH3* zD2qBX$?_gj58Y@Eyk`iF{fP!nYAbOh*w@9=MFJx-W_L<5U+Nm$T`T=zT0* z67mR3azY+wX_t_xQhdW8bSAnax`?F->V7lol^WeCWV&-Tr%H|f#L_<@KeMz3LuMl)D0#J+B-X=*f2$VP*tyEytDA?3c&XtBPKOQRtT zFP-9iF$MQKlux7Rr7UNDk~ueuUMWSp5bs~en3h7OYPXD*ebFe|k)LQ(OW1Cp%=zkQ9hSBAJuU^rN^GM2l58(Qct`mAkZ)CCm zl-I_6qCHve3CL@SzR@8ptsjumFFKMXy#e-6p{D(#vxHP=YP1ZBp5d4tYp@#%#~KtJ zT1Osz1cc7rcSQ?X*5K*Fi^%7$=(M^NWKLA8 zPg0?&IhYfzW>Ir+c(i>38Ke4iM6|yY-!MG0dKt%hAX@(d5#w8lYwRULeiY*Ndv5fw zN2uQ)iZ;HGQn~&9NVKI8x8KJ_vxHPQY84$9T`j~NhvTA7iY%cT3FD$mQl+SoFh07X zkrXu&#z)6pCPnqdgv6|zw+>$hM$1o#PQILCWG(2cl8;A=J+ch7m>7MJP>k8FoL9W}3#Wu_~u8c0#Jw~&18!oP4eEo8NjB5fS&<6WsYqK8n7_K7_^KtYO8I!mcT^Ft97IJNqkj>FWEoA#Vfpwl_YjonZQreD`@>TRqx)iz#xCJp^ zN1I$Hh1O?EwLPk}meK$*y&yZInL>)R>+puyO^D;;%mc`=7SF9=LSZoE$>?`C* zI6t<_Bip~m`xmh$?PNaHjnU^wl@c4p(ypUSbx~}AM<`XpnAu*YTHao!Y7|Rni4T^k z8ppCdLaDBdX&q#$jnB#wHjSlusheQAoeoy)62rcykCcF!mx#9*)%zGC1}j%g;C#Np@^M%Uiv0jS0Co zrrj)aF2r^CwUA-4Tp=Y|i`R9n9b|ZH981k&yo(2UAXd()4&XPb9*~D(E4s*hJ}JWY zTObd|24%VO5M)wpmq+p-1+ku8WlZsM{IwlOQLN`JQtG0nuR-R;Cf@4G3dl>brQM`- zT#vuI4Otw^>n`PUjLb^NtFiqa*#=n>%e+m-ti;om?;)?n&hH`R^jf@A0(m{w)gyZ# zZ^TBi9Kv1XA;{8LElUPU_#0$dtX)r;&wxi{&Tq%YdW7aLiAKK)Rep@O{9>be z$y7&>&pE`r8(Yq@4RR9u`QM8<17%DV-uJY2W6$9Le|8bK_n&G3G_}2NLeh+BOM^0#d>?B2c#-C&Lcx0)v-k^BQU~8L-xkX zJ(3SO9IN$6G2}!neXu+_rFsQ&CYHxSU(P9oXz?W;*#NQQl`M}U)z^?{yw)RoAm_)g zy^C^|SEqD0ni6j(#9e<<;%P%DErTN}4p2eb8fX4BmENTU~JU+`Kv;s7VuV$eY;53fa zEI#LMSqrrSToZqdCATe}Md1!PJ^n3=S^-+ePqU~MpiR8dJu)A)0<@2}W1$rwh0%XVYSkwye zKs;}ljG+~v8S)tuKY70tn(rBq$Kol&rOO^sc0@!s{D8pA@0nb5^wjQj8QXpN_-59nz_^C>5t18nz_S} zPeHu#L@8?K&WvXXDb~z&SV0lZjl%=+ za!y74z6m)Wj;H3!da0H6NW2M46|RK8K+KW&@+mTgdWX(vzr{}qacglZeolz{?Z;Db zbE-^5cM9JlpHuNBEOb{wU;j80Z{?Bwi1{;~$?_8Jl8!*m#>cQUT`9jA@^5_3G@0{+ zYkcCn5L$8>3temdfmC|(eiqtgO8YMS$wyiKHSu>9Ye>$pjK;gTRcZG*cF2-Sn7*r$*F}h)qF>muz7M-u`5)bYm#eO zE=8Gdf}|%eoh@T%4=0tdb@Eo0>3sAy$-7u+rzMrJZSr42+*!~r`MeioK57=UOKu^g zNK-A}DY?HAo&}l7FLNq&J=P_83yYcsU6ZRh70m+i7G!eU5_v2&3wk7HvZz_mBe~{9 z8KY)FpXAHukhrtp|H!)cI321#4&d`FTf3on=049elY1Lt2~mU+OBCg@q*}KuQN*v% zRx6w2x>)NHq9~%=<+5q9*0o4Wa=%}4FS0J__dVx)zjH?U=l6TPJLk;IIWwPgW}bPT z?GUcYMP3CR!gaWa-x*-v1ndy*gu<=@_MNp(;a&lG9{TX*@IVxH&a*PFhPzD_TW9Ay z``&0ucoYgd=h@q{UBZh{=*{Za!Z|4H3}@d3el1*x!p?A(8$H9_K4mS#? zfp*|3U}G^PT!_N10`~pNq2bcg#j&%ipg+{g2&a7}h4t#k;pBNj*j2#V84-RBg{JjF5{Gzuqt4+#)MCNEsla+1#B!b!;R;Qmf2On%8U=cw?s-Bl*tND zMqyXMM?&Y;I5pgAg%EnXczU=mm;4ZWo-h__%?K;}UtVlAxXWfTGrX3U z$qT&<&lA|Qug}BzT;y5zMR=zuqfo6c!qvWG+Y-;m*h+5>wwxbJ+nN)u!^`CPwY~~J zu=4I&^TH;V{P=I+U5HO%f98cBMEM?M7MBE+y6|g1>?!)!;Y5_iAne_zufugwdUO#_ zd*+85q4Wb`Pw?i4n{$!-ydd0(OSbZLBY3I?+gcF*5G4`bA6p3WO?U(fyK+~8d>fwo zuXYxO7jcoqC`ckm&3w==yL;l<6R% zjE%C*FH;7E%>$xLB`)$v*N1Cxk)yLd+>nbruJz&J--|w+-dH?e%?oF8k?m{@Pv(*z zufn?b5cKoL@Jy5kLD+kP8^iNZDm*XByO!;9w$cma0?3x|q926(0e3)ufouz($Q4o-%52Dm zZ~KRn*9l1m*#`1cxWRfMZQ$A91(2QLt|+&b!?kl1-b)F;8<6`zc7=zcupVpd!S6zZ zGXtVRnS$^#lvdC`4#=MH1r&D9CxGk=m)yX%K2F&>0^Zn!tse~649F`Whr`JzAH;@~ zN1@iy@E|TSqmGA%h%){!@Ei{>;vz@+c({BX+tv_e6&!m5Xy;@&l?yx}2Wbg%HvIBN zRz~*xxo~$b^87g$?#D%(3CU3FTzC))I}={z^5MVQIUgRyMefh}@UefDxfnkGuQC_I z?eg#5pUadRCHlap6+K{EQU-&tYsZ(^>o8xiUm(03ekIUOAE(xh|2sKuCknH{_BRy8I#B`9V8Z!=t!}9?Jl^9^Qhrp4f!HQW*XjWzt%b-%v)w zd-LpV!#m-lD4pPJ5^@$Lu{hPbg0j;kxq(szenIXn*!w%-qMPsTu{+@rT;#ijcf#Yi z$af3xgr^Ge-~9Y1JWGiG=I1}*c_{Q2%0J=d*bcpI_)qwIF7jQ{Cdx_N`on5l6!s||Yp05u#s&UwcL-aWqIW$s#UniUQJZr4U}P@&?Ty?0>WmH>gskb;KPPz)2W7fO^E+lQVsQMPzd#NXSD(f^>b&n z6BpUfoz-JpWIuOS&tffld%v^#hY-J?yQnvW`2E~P4IScJ_um-rqLvWizcJoL)w#%i z?y8m!22bA9XPonc;oZBPf*Nebq+~v;E0dD8u`!DJYcT|5LM2 zD8t`Yx1&&o4^%IsP=>#&Haa3&rVM{yO+ldyPgfI;iZYbpAF7>DD8n<v54iHclNCD8ur3oVqn2ET6}#2f4_69>d4hV+akfACem+l7O%!_Xbb?x5h@a0B)XFGyRZdXrU@gk$32G_|<#U#r zi9-3DrRJhgK2KCHqfkC)t5r^jJ*0e|q_#n!e4eDHp-?_gQs<&jK2K7YofPd*K2K8D z2ZZJGWOb(yp3jRRpQor7sSM`xR5k7tYe$aGR8`|5b7QJnIv^}JrmIhJk@-AJZOujI z#+Pa;7nvJhs)=XCK2tt_r6!ycLizlaIsk=oW3D;{g>qxAx|54M$L6Z-&a-x8Zp>9* z<|6ZXuKJn~KR4#7eT4YAF;D#;3Y`h_)FIdoe&4GNQ7AWlP`jZ}Zmd_A zqfl;aR0~ijH#VtdE{T>YH@2wBD3lx9)HD>zjUUw&D3lvJ)g3658^5Tb%c5nTRj`_yYFlpFihm@905WNz$J zRTRpNeQIeTes1hjA3~wCWuICTYf*0OQ@f#1ZtPb_p-^t@S94G(Hx8)hQ7AVKstJFH zJ*3-0NbWDK{>vD{ly) z+_;|>}kG2xlvZDgF?AcPD@3h+$gWjMxorOplwH?+^C>kMWNiN zpp_1ZZBcGi&}PL5q1>pTEer_Djf&d$LU?Zc0J-sywx7yiZX{^exX95-&_YF6%Q80- zv=RYfxlviG#zp2vHTaQKR!inaO|1s{|md-_FhKTgJ7u5?thZqqb%U@pI!b%@yM3#$#Fq6gm?g)2d@TlpBv} zb-BpgsG~Iw2+NHpv^H3a)*B79wOnLBKc(&FB6H&@?KTSK#xvUU_p#%Wxsjv|K%v}d zuFXZE+;~FL!oC)J+$w*$Z=2Aazz>c`E#mP z>H*e{%$8K``GBx&>8W+$BD1BZ){l$KmR{QXD3mR|w2?ymZ0V&%7G+B>?K}!)%j=p}k{yN2me;k$D3mR|wE-xUEq$~_D3mR4XnRp8Ti(!= zQle$bmN&F2D3mR4Xjf1uTi($A2?)!UH?rH-q|V&v23Fd_S!< z7nv>nw3h?IvSonw0T-Dq1GO<+WVXDg&E_JrPwp=_C; zB|5BSc`uWtrJ_)_WNV{PC|jmz2T>?nrfIiPC|hP|%_5>@%9fd0ItpdW=h|!(%9h#M zAr#7%ue9qZlr3Lt4@X7Ilr7(A?YPJ>U8tpSkv+ChTO26E)+7tHO95fIu}F(?S<5mv z7HOrp$lO@0c_@?{i?zx^{M=Zq)kC4&SgbX}T9g}$wN@x}wk*~;ucKL5*E?5XuKtrZI8#&T^S3gyOfZ6*rk#&T^t3gyOfZBA*? zGUdi{ZD~MQZmiJO3E{c%Bjm<++F>e#Ym$}P4K8wYR%($ltYw)SE47CL!g6Dc){KkH zjUTjRE;2XrwBB4~Zsch-%Zq)c+}NmHt{{YRW208Dq7cfBe62GIlpLb~&xX9etrD+N5D6$pGjon%g6v~ag+9(vtjeS}!3av@@YlSG38wa)M z!=h!%jU!qg6v~a`+9(vtjgwl*%AywK#%XOb3gyOGEeC~iA5Jy=fZbkpjM2YSebWSCTIKYUD3n^|^k^McOP;;u z^f6pyYL(L`a*?T3PM;yfPp$I$93g&cmDj&Pp>wRfz6#r+)GDv9<04b5g1$Wi5Y+Evs+URAt$V6(Z zS4E*jYO6mX#80HQdJ7a7;MvB6FjYz9%3oH(u2Xxyan;s^8m)?T^fjR6WW? z=0>W1;%TwZlp8(una>EJ+~}#FN1@#4rI%_V%200f(v!HzbF7#C6Bn5qz4YB!i=N!} z(hmvobK`aWq!2$hUe_<6(3$YMegoT~+<09NHD&uFbECIjJRmGL-qfR9YHuWy>^uRX|v_OxL$_k=Zg`-_J#6%MASt z3T4X-{hAOzTW09@JjeD&u6AbVst|v*GedV!=!}}7CtxkgmKl073T4Y@dO8Ya%V+u` z6v~#F`Vkab?ab1n&x`$`Z24Soj6&J+x!xOvvgLDqG74qO=lUzHL_3r%P%9uTTfWc- z3*p%ka}2)SsZXXdm@RYkC0yj_%+c3!k=Zgw-x?5>EpzqrTx7N^&~I^(*|JzK*;?!& z>qGXo&tiS=i$W+{mgqa%3ZZOSqDR{ap=?>IH$tIoS*j1@BG0j<`W-GZTbAmv?L|A3 zElc(Lh4|UBOxK0@*|JP`QRqxqraz4BP_``7t8o1~EwrteXQ0Pu-lb(Y@*|J$LM4@cisz*DBmMKYo)ElBu zlI+ksy(G#|lKiaSMxiA6MK9e+l%XWqt#?A9B-yL?MWH0wuYZn0NpeWv&P6845k2W; z*77)olH{nK$3>3vF@3uzE0`>mMFuL;1UMBss39bCF4MLLZAl zNpeD;A;eFT6Z%3FTBV%O*J3SNrJT?=qR{zsLNCBtlq4tgxL4Ue=kqs<*!zAb^+Xg( zl9PHj6iSj)`eYPJlGFNL6iSjax{@MVrX)F|C!kQ0oYC8$P?DU{7j+i3C`r!fYXicP zJ{;KlD~yWRhIh zyK#}LlM<&S){Sz+o z?&gL*UWlJ0H}xq({3N-l&qARy;imo#wnIsBQ(w+SCdn;5Hy|uY?&v$Q79~lH@pR9- z`#IL=#6>1atg#Y>awE zD3lTvjrXt?r9?#|1BK3wibf{ZqLip;aik2xQDj9uIC?zTx?+1jXL}g=?5S|hxAtkC9 z3#bgH#3RNgE^>4pF@E79Q{oZha6ni}BpRW~B*(ZA4L6KeIB8jqNDxiev3GF)pL9y8tH54DB7!4!h!5trkWG%G7%BE#dBLcOvI1IW(#A+eVOM1WwA0QQkHUVHv^q!!V;UE64YK{| zX!IS#TFz45g5MT<4CH0wslh_nRmocJY$S7$*H>2~B_Qk?>}pI62%D+98Q*Xb*I+|v zxto#0B|na?xbDUl6nf{syRk=ze+_mwPNUE@*xk5_wdfk`ZrnnlYp}bayvvS4oImVp z>TV>W&^7p)(H4cS!Pkrw6q>1f7?~*aJEf_{G!*)s($|eUD0H2^VH8gjTc_*vO`{|V zU8ipvwNU7HO5ZdZqR{V@_A|0j=yyu{8@VX-OS1!vohWp@4=@T*=z1SuxbN}(iKkyK z9blA3p5mTojh`5J$#Jl$xCLVcKSv__#mOgGw~P#>ln z$wK_!Do;1obCD@J)c7eNEJcSIm$}Fk9bx>#MW*OjL;HY_yZ`%sV~yTiYraJ;btYtaafH-6O3eAl9#-o{{9hw=p7)e~Rlszz?wuFq@ zYOF+I>s2;aZZqaiV7262`IC{6C4}b6UB*{j8T zX!bl}%tE2r^N6tkg=WuVhB8TPi)POgMimsAJx?0-P-yl%X>>!O+4H3Fi4cGGJZX$V zp}F9sF#&~gl|uOJ`68T$r;R;S24~MR z#(6IC{5fO%Z!$Y}nZ0L>G%hlG&ln#G@w4}=F&c%=gtJB#)}rh^YfR%Jv-fx7tAMcV zyq7hyEM~riLL*qr{2vO9U@>zb3XNbf^Ia4g!D42* z5Pt-VnZI)py}?HCe)F$@un{a}TC-UH$Pv`dhq%ZQbj*5OMY&Ay+jbOwq{89*wVAOo^D<*RGjGFyXX!d;2T#Q2Zm1WJ%C^UPPH}|5@ z>{-E_HdnMvv*$x*E(*<_mCO?;GQsd-a~KYKPci!EY% zC_is%YFa}4&zqW>528?JHZ>El7R{bb&14jsJ(JA$P-yl{GKZtk?Agp*i9)kyb2ATx z?mb(Y6&H&=q}j8TSrdh3&(>xg6q-F-n=hl#?Ah8(MWNaAMKcG5X3w_f85Ej5+nI$Z zG<&u)OE1A5gJ&1)d3ZbX86p1c+0INtq1m&Y`8*2cMmzHb6v~ZuW?K}>jdo_T5P$Y; zXTBB?HhZ==2MFP_XGfSllg;r|24~L>=3FlFtm|MJOW9G#?CoH>Tx9lkFe_pi`a4-2 z%|}ETe?IMK)@>eVi0Zxrg)F6Nsk)T>?00YdyV?qVL|BGb6L`FlWE8mF4Ym$4p`Y24e4aFJ=; z&rIYZ)3~2GoQq83erAp+yQ~OSwQIy7Snbt}nl*VtFvsVeBH12OItC`3#?Qed< zMUH8IGn0!P)BffZA^r#sFlV9A2o5mkVl5iM0p=nuas>Zpt_cVm!GY#xY==fL&DyF&4BU8fj*(6+$C8%3Sn=5E{WzX7V~AG=ihe zWnAP4jy7woXJzCFjyCIZks~*7D6NVm04~J6S*S#%4~%~E26K>FHmSj^o^N|LMx($=1vq^5iK$^wu+W%MYP16 zi$W`+<>q!2S`n=@2W}I!c9a(@qSa;&3ayB8%*NYA8CnsoHB(V&MU-ofLZKDWdNUt| zRzw@kom}LKDBtY%BWpQJNjxu}{BJThqtF^@i&7Utg$n|QQ0RHWakIoPeCz&-=(t%Dg;qqzO%sK3ZNQoLLv!q3pe6))V4q?A%6B=GFNkv+53l?9}t$k*UU3qWcJ=LuXB;vd&exkoA;RiH2#j+jf>3Q zJLY6j#?Rh6rm|P`GiC2TX6fIAQ1<>~Zr&$^vRARn?`I-MP_e${B1cfMmUEG_r(&%Y z;*Vg+%15CQ3|T*7EgHd)wVR6^!5HgEK-dVzS{JY#8o_w$vJig+3+Je;OZe6&2!-V7&DP7de8(t-%3dBlv(djf)&X)tb*mj-X}ba*?yA zWmyN=*5wFVRtqljX}o1EJ1jsKyv_H?YG$5|QiMj3k| z?OL5tX!a~^y^lh(XIU!~g=WukR>}#{4$YnwtPB*IJrk_CC^UOkw$e|ETI|gb_Br(< z)@Br%J*!#K)1nN`p4F{}C^UOMYIQ@Q*|U~41BGVKI@TgCa`voiH9W&wmb2&M)@&4- zJ?mLRONY-S~+(CpdV>V`tIXG<#|g=WuIRsjmlo~^CDC^UPvw*E$;*|W73cR}nA&7Ln> zolt1@Y-28CwNcGe{n%8ho`pD2_Y?X160 zDEZr2H-z}JXFKcOi{dEQdouiaf~5=Lvu7$?1SV>&uIo82i%SC2y2WunN zqS>>9^%IuypC?$mMH&C;Zb$0?)}rj~Xr169v-c(IQb1VtzGB_RcIfGD7pv$ceq3>s z#$Bv?h4^XQ#kwDb(zuIN3Wa*Li>0GbuXeF46zbJ3R#b?e#$BvoTx1$|x5fvArE#jY zjEhX;-qr>#GL8FLd%4Io?q?-lX2&JdxS!RHi%jEw*2+J`m{JN9`&&=^#mdMr?QcEBMb4-Ft!IV!^XUNVMHITa2Us1k7LDlus|y!7rvJ10 z1cZ(0Kx;6zLnD}Gy(h#U!89uag+?&V`WS^qFwGi?LL-=FjX|LiOtZ3t_#>ER-Qglf z@B{0<>#R3~uo3*ws=`H%;4te6E^-7%TCKRq5gcjFJ{@g!LZQ3SOluVi&8HKrJt#DvPPERT(0rP072FXm z(|kJFDnz0A^iwPPk0?X)=?tqV|1IroWk-84pU$*epwN8!xs@AY^G~)y^XY8s1PaZk zUs`c7q72QabFHUPXg-~9C3BJU=>qEv3f+xZ%b+OVq9uPL4uCz+u!)nPgtE@-4$oX`Y^%NI5pRTr^N1^$2wbe<8KcB9)`k>H! zy4o6ywP-$FZ4E)8D{i$l9Ba{hy4qTaLi6bw>mUl(0rO_eUC!(X`Zzih33(=Apq z3eBfmtaKEbPq$ceg!uF67HcjF&8J(eg(#F8TdWl*lp9;DRVb7jTdW)*{(QQ{+8Pix zpKi7G3gPqVn=qelv;LwoIG=8}ipSr5{%p5C;v%zmyETf7%--$RL@Yz|>5tYlQN~Z> zAFbIabZ-1;&Bt~qjeoS3a*=8LleIP=ERBD*wqZMz#=EVbg!pN^+uDsnX}sIoi$cA+ z+d7Owy}H{vhC;o%+d3n}PvhOz3-`(XVNZAWTCW6zrSX0%or_H4!`4_XGL27Iv$@DL zK4IPFBGdSUl~_!ib@X)igq2Z32&M5!Yxey@D2-2ARZ0q>G(Kg0%tfa0DJuts()g@Z zt`w^!-!(dKH9?^?zGx+*P#Ryd>V-uuO5-b53JRt1RVxF9()hZSr;1viB#Sh@VHMSc zP#WK|a!@FZ|F({xP#XVZ71c#8O5>2-5QWmXsNIT-OygKP&S16V-OWAra1^?`iL-B8 zq71#MTg={R3!yZAzz#V~WYU(hkw|RlArFKcB1GVHC>e zsjeMFS;?|0hUjUN`HLnD}MCsh(cBbaROtt^B_ zu!CK*3KKcQcCd$|&^B0!=F<#& zen43LkLBn%6O|pkl8Jtfi+mpG-b8NDGe;sxdGJ7Z6Iv1I}lWh;n(0n?@t{}?zX*|V#1clCx zDRxb4hthb8U5|@Q4Znwd9D2->??S=ShJj;Fsh0=JI-5G^?b(Y;7g?e?C zor*%eI?L`Y#82Z{_8u-Wjc41(0>aYxE4yf2)?+e_=i4e5nZ}Fk1THd-7uo4tWEwBB zmx(fd8ZWZr>WMRo(s;3*P+th8@nUBX$wiLfR(mEF zIf7g5LN0Oyx7t-2iLszN)~$Bt(?Vzjx7k^Zh0q9Yv&%OTLL<1{PUj-$)9vwMnT;z43IInS$cdUx@L7)tqVHGDQAgslZ zvxAGAVMER#E^>yAan7O83>)JV3h`&y7$^QY)}DOhH^wo9_%m#b0KHD@jt zdDdx;*P0!LOk>Tdz(uC9=2XQpG{fpnEm6kLXWglfLg$9=G{$x)pLM4N7n#q7(>@?9 zpKYfbwnO>sIz5E=`RqDxpin-$PCpdtRo5ASLcQub@1Rhxx=xx9Kc8LaEEhS$mUgZM zgynNN$9#eHn9S#goQhmzK38$-agq64#mV9#^SO$%S(Nc-*eXu6tvI77pC57RwG%@5 z{D_m+UI^uLRi|_^6FH_;oiSYGm{xTra*<imf9&=fdU;A76OC^UkPIr~s(1Rrw_qRgvPX~Q}0zFG^R<;WG-?{lN>LFm62nbgbLykeC^vJOVLLR+&79U;V|OYq`7A3Wco`nDlURyNWXOYf(L&QQd^F8HAO2-C2af)&OZo6s3>z zI|`f8*tYsQw^2^w*84kFchL?Tch=6^PD3v8*P;eGM^Nb3q6RtXud!Nk+y^_uxyW7} z?0gp}!&U`@oofMM_1|^kda(Z$s{-~{Cf{{TF7kI^(wy=r^s5wUPIV#vsvymI3We^w z)12qA7CpyKbK0TM8I|UA!&I5g7=*BD6}eg--)KOqmaL8`GJ#+ zLNk84la4|&e!4Rkg=Xq>rvQcSh0~q7y+q6OJ22@^(}1w~bcoYd2%k?shxznFr$3c} z^N0O4*rCp7E^>5+I#amFv>EEm2?$G@Va_HlGHpgUd$`E78S9+qB9mmSv#gKUXG)Sx zXTh67C`mG%LKONPm~oEVSCpY_8RxX&BG0jL&H*kmTgExZu@*hG9_O4F;%Ccv=T9Mi zwv2afq0pHy-nqA**g9p)c;^8wGFv7%PC!_;WIG91i?U^^GoFj==jqNDTx7OPch;lK z!E11)a}tHJ%?e6q&=g6$F~1BJ3>nKKuKvSo#H427~~m2(Y+vL(m4Z-8i-vgHS-3YT%p?ZqKw z9-OP|oB=3r!yOru_0D+|1OBG%-X9cYgH!W=tQ|-slssoX7kO@Mbe4)T{;$<-bdCke zu>9HRlzyAFBWvY5k8+XulkYsmMdr^Y=Xn&$pG{6DA%6aBa{8c9{%mpvV=c;`P0kP$ zI_ow$nOKYRXOokQLiw}VIfFv^v)QrUVMmeg{|?m_Ckch_IJP<&D3m|joCPS9KiixF z6w04%jxtcRO!>3T88S!+<~?B$k@>UFN#c^PymnbpzJ>kS=X64$=L!3r#_x%vphP<0 zq@z$G9dLd`p+q|9{E0$|bkIq7pS6=0dOipKasnLbK_?5P7sxu0!_Ib;oW1ZD?71vU zXSK4Gi7>|A*Hppox zbIM6ZVMlrqP}mv8YMpg91%%c5-8qNCvW1m7=a?U| z{Tas3I#%YqlZe93I#%X_lM)bC=AttQ<$A9ezR#DO@hH#3^?ncR&t+#pKql>izsc@w zMDhL#DgD~xMVAvAb(20KL6=NKVr3nm>}1jG!(Xn z}a2{UBg}UF0#PRJj|aqcQ9E)JYn6E;MkxhlYDZ!xuzk)~Dn2c)raF-@N^;|n zGS`H^ZyqU)(i^32q$)~-x*_HJ4T|!3q+vj8Xt{o56v~J)G0KA=Pe#rKWFypS6j8Fo z*4MrjQl`hjZwN#h287jW5}6s0HS6Km{2~ic9)bK}^`DL8a1m!h0@QjovW?47qLbutu~F_684wV5q_0MDQRqlhBBdvbT1?m|ca7AW zB9D}fa%vk2f)Al5_CTM5=%!ag^u)1sufGeC54W? zuV02nuwNucY&}G0_*;>`P}p(RhJF5@UyF|Y?MRVN`B8+J)Q2*IB3eLLZ>0IP=u8+A zX@g~$u$G71RfhG?hmmf9GOT|-icAg&+t$aCxdCC@8sWD?eKcq<$4C02P|r_@^hcqd&x&NA zP_Ism96_NTn;dyyx;$H0|4fdQL7^l4G?K_g97Rhg^J%1|DB~Z+v`9y+MMp6$@;(Y3 z#f-=h6gr9-kqi_%iq9e+qtH>zjLZlKyLM(p@=)lwW=AdsgpJo%k^5)J<6>9Jf=DDF z>_`_xYNODRE{r_QWrC8?HKeqGG7BTm2c#p&;z$Zg(;gv($?`})E~0i=X+oJLqDwC zd7=KkwC5uCCnxf1Ky)ab6G;uo=Aw%7edMixuoi!a3=RmZ^+RN6Kv=C@x-R@ASx@38 ziIi42Gv@hay8K7(c_YNU zWqk4Xd8=QB&R!{W_HL&#CE9-)!&eu+#Ik)(hnA(#8MTvY#TIrJ<8`;N{PD%#pIv?} z+8-&jKLvgnx|*cW)wJ6$Ls!$DyM$d$zxu+iCYG7|?hl#7@~4QpQ59>wc{ZpT#JXKnh7>zy1+cUv~)`!Py`;s7#5Q!(){BAU8>3 zE0m5=o`H<=#Xl4N_RA!~ak15xln=W7TYAGiEG$q1W$puc502}O-wqoc*3Y3RGzS0j zW2p~|koa5o#lM!N^s5k37Q<1*Qmxpd6+_Bukb6ib+?B8+^(A&*ml!1r!xMr zKR^<@zLD4;Db&wWsGm#vwWyy}U#OpT5`PbUi7i_gQVzo&>e11Gq-ANWM<)h^rLi8J z5fGNf2DQw`i{+t3QbNP?$0mtod3X}aNTIZe`ei7wJzprXrReaS_7b!sh2`OZUGT<+ zUn>pD+<-D=N&I6kN8%s5FaB8g;-807Xe=uD?a)|M^o5