From b91ca908ec0aecf680f1ecdf4f704d60b6d5eaf8 Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 17 Jun 2015 18:54:32 +0200 Subject: [PATCH] Maven Optimizations + Fixes --- .gitignore | 129 +- pom.xml | 33 + snapshots/AuthMe-5.0-SNAPSHOT.jar | Bin 0 -> 414325 bytes src/main/java/com/maxmind/geoip/Country.java | 61 - .../java/com/maxmind/geoip/DatabaseInfo.java | 117 - src/main/java/com/maxmind/geoip/LICENSE | 504 - src/main/java/com/maxmind/geoip/Location.java | 60 - .../java/com/maxmind/geoip/LookupService.java | 1046 -- src/main/java/com/maxmind/geoip/Region.java | 8 - .../java/com/maxmind/geoip/regionName.java | 13400 ---------------- src/main/java/com/maxmind/geoip/timeZone.java | 1403 -- src/main/java/com/sun/mail/auth/MD4.java | 295 - src/main/java/com/sun/mail/auth/Ntlm.java | 361 - .../java/com/sun/mail/handlers/image_gif.java | 123 - .../com/sun/mail/handlers/image_jpeg.java | 58 - .../com/sun/mail/handlers/message_rfc822.java | 130 - .../sun/mail/handlers/multipart_mixed.java | 109 - .../java/com/sun/mail/handlers/text_html.java | 58 - .../com/sun/mail/handlers/text_plain.java | 198 - .../java/com/sun/mail/handlers/text_xml.java | 157 - src/main/java/com/sun/mail/iap/Argument.java | 308 - .../com/sun/mail/iap/BadCommandException.java | 73 - src/main/java/com/sun/mail/iap/ByteArray.java | 132 - .../sun/mail/iap/CommandFailedException.java | 73 - .../com/sun/mail/iap/ConnectionException.java | 79 - src/main/java/com/sun/mail/iap/Literal.java | 62 - .../com/sun/mail/iap/LiteralException.java | 58 - .../com/sun/mail/iap/ParsingException.java | 73 - src/main/java/com/sun/mail/iap/Protocol.java | 496 - .../com/sun/mail/iap/ProtocolException.java | 91 - src/main/java/com/sun/mail/iap/Response.java | 530 - .../com/sun/mail/iap/ResponseHandler.java | 51 - .../com/sun/mail/iap/ResponseInputStream.java | 164 - src/main/java/com/sun/mail/imap/ACL.java | 117 - .../java/com/sun/mail/imap/AppendUID.java | 61 - .../java/com/sun/mail/imap/DefaultFolder.java | 137 - .../java/com/sun/mail/imap/IMAPBodyPart.java | 438 - .../java/com/sun/mail/imap/IMAPFolder.java | 3127 ---- .../com/sun/mail/imap/IMAPInputStream.java | 269 - .../java/com/sun/mail/imap/IMAPMessage.java | 1468 -- .../mail/imap/IMAPMultipartDataSource.java | 86 - .../com/sun/mail/imap/IMAPNestedMessage.java | 151 - .../java/com/sun/mail/imap/IMAPSSLStore.java | 58 - .../java/com/sun/mail/imap/IMAPStore.java | 1934 --- .../java/com/sun/mail/imap/MessageCache.java | 444 - src/main/java/com/sun/mail/imap/Rights.java | 460 - src/main/java/com/sun/mail/imap/SortTerm.java | 104 - src/main/java/com/sun/mail/imap/Utility.java | 186 - src/main/java/com/sun/mail/imap/package.html | 623 - .../imap/protocol/BASE64MailboxDecoder.java | 194 - .../imap/protocol/BASE64MailboxEncoder.java | 260 - .../java/com/sun/mail/imap/protocol/BODY.java | 96 - .../sun/mail/imap/protocol/BODYSTRUCTURE.java | 429 - .../com/sun/mail/imap/protocol/ENVELOPE.java | 216 - .../com/sun/mail/imap/protocol/FLAGS.java | 107 - .../com/sun/mail/imap/protocol/FetchItem.java | 78 - .../sun/mail/imap/protocol/FetchResponse.java | 279 - .../sun/mail/imap/protocol/IMAPProtocol.java | 2467 --- .../sun/mail/imap/protocol/IMAPResponse.java | 142 - .../imap/protocol/IMAPSaslAuthenticator.java | 271 - .../sun/mail/imap/protocol/INTERNALDATE.java | 144 - .../java/com/sun/mail/imap/protocol/Item.java | 54 - .../com/sun/mail/imap/protocol/ListInfo.java | 102 - .../sun/mail/imap/protocol/MailboxInfo.java | 127 - .../sun/mail/imap/protocol/MessageSet.java | 137 - .../sun/mail/imap/protocol/Namespaces.java | 168 - .../sun/mail/imap/protocol/RFC822DATA.java | 78 - .../sun/mail/imap/protocol/RFC822SIZE.java | 66 - .../mail/imap/protocol/SaslAuthenticator.java | 53 - .../mail/imap/protocol/SearchSequence.java | 446 - .../com/sun/mail/imap/protocol/Status.java | 114 - .../java/com/sun/mail/imap/protocol/UID.java | 66 - .../com/sun/mail/imap/protocol/UIDSet.java | 138 - .../java/com/sun/mail/pop3/DefaultFolder.java | 145 - .../java/com/sun/mail/pop3/POP3Folder.java | 599 - .../java/com/sun/mail/pop3/POP3Message.java | 648 - .../java/com/sun/mail/pop3/POP3SSLStore.java | 55 - .../java/com/sun/mail/pop3/POP3Store.java | 420 - src/main/java/com/sun/mail/pop3/Protocol.java | 847 - src/main/java/com/sun/mail/pop3/Status.java | 49 - src/main/java/com/sun/mail/pop3/TempFile.java | 193 - src/main/java/com/sun/mail/pop3/package.html | 607 - .../java/com/sun/mail/smtp/DigestMD5.java | 229 - .../mail/smtp/SMTPAddressFailedException.java | 105 - .../smtp/SMTPAddressSucceededException.java | 104 - .../java/com/sun/mail/smtp/SMTPMessage.java | 342 - .../com/sun/mail/smtp/SMTPOutputStream.java | 114 - .../com/sun/mail/smtp/SMTPSSLTransport.java | 58 - .../sun/mail/smtp/SMTPSaslAuthenticator.java | 228 - .../mail/smtp/SMTPSendFailedException.java | 98 - .../mail/smtp/SMTPSenderFailedException.java | 103 - .../java/com/sun/mail/smtp/SMTPTransport.java | 2383 --- .../com/sun/mail/smtp/SaslAuthenticator.java | 53 - src/main/java/com/sun/mail/smtp/package.html | 732 - .../java/com/sun/mail/util/ASCIIUtility.java | 275 - .../sun/mail/util/BASE64DecoderStream.java | 470 - .../sun/mail/util/BASE64EncoderStream.java | 322 - .../com/sun/mail/util/BEncoderStream.java | 71 - .../com/sun/mail/util/CRLFOutputStream.java | 109 - .../com/sun/mail/util/DecodingException.java | 63 - .../mail/util/FolderClosedIOException.java | 83 - .../com/sun/mail/util/LineInputStream.java | 147 - .../com/sun/mail/util/LineOutputStream.java | 78 - .../com/sun/mail/util/LogOutputStream.java | 145 - .../sun/mail/util/MailConnectException.java | 107 - .../java/com/sun/mail/util/MailLogger.java | 388 - .../sun/mail/util/MailSSLSocketFactory.java | 365 - .../mail/util/MessageRemovedIOException.java | 73 - src/main/java/com/sun/mail/util/MimeUtil.java | 123 - src/main/java/com/sun/mail/util/PropUtil.java | 167 - .../com/sun/mail/util/QDecoderStream.java | 93 - .../com/sun/mail/util/QEncoderStream.java | 119 - .../com/sun/mail/util/QPDecoderStream.java | 217 - .../com/sun/mail/util/QPEncoderStream.java | 212 - .../java/com/sun/mail/util/ReadableMime.java | 63 - .../util/SharedByteArrayOutputStream.java | 64 - .../sun/mail/util/SocketConnectException.java | 117 - .../java/com/sun/mail/util/SocketFetcher.java | 722 - .../com/sun/mail/util/TraceInputStream.java | 162 - .../com/sun/mail/util/TraceOutputStream.java | 158 - .../com/sun/mail/util/UUDecoderStream.java | 354 - .../com/sun/mail/util/UUEncoderStream.java | 216 - .../util/logging/LogManagerProperties.java | 648 - .../sun/mail/util/logging/MailHandler.java | 3477 ---- .../com/sun/mail/util/logging/package.html | 58 - src/main/java/com/sun/mail/util/package.html | 79 - src/main/java/javax/mail/Address.java | 88 - .../mail/AuthenticationFailedException.java | 84 - src/main/java/javax/mail/Authenticator.java | 172 - src/main/java/javax/mail/BodyPart.java | 82 - src/main/java/javax/mail/EncodingAware.java | 79 - src/main/java/javax/mail/EventQueue.java | 158 - src/main/java/javax/mail/FetchProfile.java | 238 - src/main/java/javax/mail/Flags.java | 589 - src/main/java/javax/mail/Folder.java | 1662 -- .../javax/mail/FolderClosedException.java | 105 - .../javax/mail/FolderNotFoundException.java | 124 - src/main/java/javax/mail/Header.java | 94 - .../javax/mail/IllegalWriteException.java | 84 - .../javax/mail/MailSessionDefinition.java | 112 - .../javax/mail/MailSessionDefinitions.java | 58 - src/main/java/javax/mail/Message.java | 717 - src/main/java/javax/mail/MessageAware.java | 58 - src/main/java/javax/mail/MessageContext.java | 122 - .../javax/mail/MessageRemovedException.java | 86 - .../java/javax/mail/MessagingException.java | 174 - .../mail/MethodNotSupportedException.java | 84 - src/main/java/javax/mail/Multipart.java | 277 - .../java/javax/mail/MultipartDataSource.java | 86 - .../javax/mail/NoSuchProviderException.java | 83 - src/main/java/javax/mail/Part.java | 463 - .../javax/mail/PasswordAuthentication.java | 83 - src/main/java/javax/mail/Provider.java | 139 - src/main/java/javax/mail/Quota.java | 129 - src/main/java/javax/mail/QuotaAwareStore.java | 81 - .../javax/mail/ReadOnlyFolderException.java | 104 - .../java/javax/mail/SendFailedException.java | 140 - src/main/java/javax/mail/Service.java | 676 - src/main/java/javax/mail/Session.java | 1281 -- src/main/java/javax/mail/Store.java | 315 - .../java/javax/mail/StoreClosedException.java | 105 - src/main/java/javax/mail/Transport.java | 419 - src/main/java/javax/mail/UIDFolder.java | 192 - src/main/java/javax/mail/URLName.java | 770 - .../javax/mail/event/ConnectionAdapter.java | 55 - .../javax/mail/event/ConnectionEvent.java | 98 - .../javax/mail/event/ConnectionListener.java | 68 - .../java/javax/mail/event/FolderAdapter.java | 55 - .../java/javax/mail/event/FolderEvent.java | 167 - .../java/javax/mail/event/FolderListener.java | 66 - src/main/java/javax/mail/event/MailEvent.java | 63 - .../javax/mail/event/MessageChangedEvent.java | 107 - .../mail/event/MessageChangedListener.java | 59 - .../javax/mail/event/MessageCountAdapter.java | 54 - .../javax/mail/event/MessageCountEvent.java | 154 - .../mail/event/MessageCountListener.java | 61 - .../java/javax/mail/event/StoreEvent.java | 119 - .../java/javax/mail/event/StoreListener.java | 60 - .../javax/mail/event/TransportAdapter.java | 55 - .../java/javax/mail/event/TransportEvent.java | 166 - .../javax/mail/event/TransportListener.java | 76 - src/main/java/javax/mail/event/package.html | 54 - .../javax/mail/internet/AddressException.java | 130 - .../mail/internet/ContentDisposition.java | 192 - .../java/javax/mail/internet/ContentType.java | 285 - .../javax/mail/internet/HeaderTokenizer.java | 489 - .../javax/mail/internet/InternetAddress.java | 1379 -- .../javax/mail/internet/InternetHeaders.java | 609 - .../javax/mail/internet/MailDateFormat.java | 913 -- .../javax/mail/internet/MimeBodyPart.java | 1597 -- .../java/javax/mail/internet/MimeMessage.java | 2228 --- .../javax/mail/internet/MimeMultipart.java | 1016 -- .../java/javax/mail/internet/MimePart.java | 228 - .../mail/internet/MimePartDataSource.java | 168 - .../java/javax/mail/internet/MimeUtility.java | 1652 -- .../java/javax/mail/internet/NewsAddress.java | 212 - .../javax/mail/internet/ParameterList.java | 828 - .../javax/mail/internet/ParseException.java | 70 - .../mail/internet/PreencodedMimeBodyPart.java | 124 - .../mail/internet/SharedInputStream.java | 84 - .../java/javax/mail/internet/UniqueValue.java | 123 - .../java/javax/mail/internet/package.html | 502 - src/main/java/javax/mail/package.html | 312 - .../javax/mail/search/AddressStringTerm.java | 103 - .../java/javax/mail/search/AddressTerm.java | 96 - src/main/java/javax/mail/search/AndTerm.java | 135 - src/main/java/javax/mail/search/BodyTerm.java | 122 - .../javax/mail/search/ComparisonTerm.java | 83 - src/main/java/javax/mail/search/DateTerm.java | 126 - src/main/java/javax/mail/search/FlagTerm.java | 159 - .../javax/mail/search/FromStringTerm.java | 104 - src/main/java/javax/mail/search/FromTerm.java | 96 - .../java/javax/mail/search/HeaderTerm.java | 124 - .../mail/search/IntegerComparisonTerm.java | 113 - .../java/javax/mail/search/MessageIDTerm.java | 103 - .../javax/mail/search/MessageNumberTerm.java | 90 - src/main/java/javax/mail/search/NotTerm.java | 93 - src/main/java/javax/mail/search/OrTerm.java | 135 - .../javax/mail/search/ReceivedDateTerm.java | 96 - .../mail/search/RecipientStringTerm.java | 128 - .../java/javax/mail/search/RecipientTerm.java | 122 - .../javax/mail/search/SearchException.java | 70 - .../java/javax/mail/search/SearchTerm.java | 87 - .../java/javax/mail/search/SentDateTerm.java | 96 - src/main/java/javax/mail/search/SizeTerm.java | 94 - .../java/javax/mail/search/StringTerm.java | 123 - .../java/javax/mail/search/SubjectTerm.java | 97 - src/main/java/javax/mail/search/package.html | 67 - .../javax/mail/util/ByteArrayDataSource.java | 200 - .../mail/util/SharedByteArrayInputStream.java | 115 - .../mail/util/SharedFileInputStream.java | 544 - src/main/java/javax/mail/util/package.html | 54 - 232 files changed, 157 insertions(+), 82601 deletions(-) create mode 100644 snapshots/AuthMe-5.0-SNAPSHOT.jar delete mode 100644 src/main/java/com/maxmind/geoip/Country.java delete mode 100644 src/main/java/com/maxmind/geoip/DatabaseInfo.java delete mode 100644 src/main/java/com/maxmind/geoip/LICENSE delete mode 100644 src/main/java/com/maxmind/geoip/Location.java delete mode 100644 src/main/java/com/maxmind/geoip/LookupService.java delete mode 100644 src/main/java/com/maxmind/geoip/Region.java delete mode 100644 src/main/java/com/maxmind/geoip/regionName.java delete mode 100644 src/main/java/com/maxmind/geoip/timeZone.java delete mode 100644 src/main/java/com/sun/mail/auth/MD4.java delete mode 100644 src/main/java/com/sun/mail/auth/Ntlm.java delete mode 100644 src/main/java/com/sun/mail/handlers/image_gif.java delete mode 100644 src/main/java/com/sun/mail/handlers/image_jpeg.java delete mode 100644 src/main/java/com/sun/mail/handlers/message_rfc822.java delete mode 100644 src/main/java/com/sun/mail/handlers/multipart_mixed.java delete mode 100644 src/main/java/com/sun/mail/handlers/text_html.java delete mode 100644 src/main/java/com/sun/mail/handlers/text_plain.java delete mode 100644 src/main/java/com/sun/mail/handlers/text_xml.java delete mode 100644 src/main/java/com/sun/mail/iap/Argument.java delete mode 100644 src/main/java/com/sun/mail/iap/BadCommandException.java delete mode 100644 src/main/java/com/sun/mail/iap/ByteArray.java delete mode 100644 src/main/java/com/sun/mail/iap/CommandFailedException.java delete mode 100644 src/main/java/com/sun/mail/iap/ConnectionException.java delete mode 100644 src/main/java/com/sun/mail/iap/Literal.java delete mode 100644 src/main/java/com/sun/mail/iap/LiteralException.java delete mode 100644 src/main/java/com/sun/mail/iap/ParsingException.java delete mode 100644 src/main/java/com/sun/mail/iap/Protocol.java delete mode 100644 src/main/java/com/sun/mail/iap/ProtocolException.java delete mode 100644 src/main/java/com/sun/mail/iap/Response.java delete mode 100644 src/main/java/com/sun/mail/iap/ResponseHandler.java delete mode 100644 src/main/java/com/sun/mail/iap/ResponseInputStream.java delete mode 100644 src/main/java/com/sun/mail/imap/ACL.java delete mode 100644 src/main/java/com/sun/mail/imap/AppendUID.java delete mode 100644 src/main/java/com/sun/mail/imap/DefaultFolder.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPBodyPart.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPFolder.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPInputStream.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPMessage.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPNestedMessage.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPSSLStore.java delete mode 100644 src/main/java/com/sun/mail/imap/IMAPStore.java delete mode 100644 src/main/java/com/sun/mail/imap/MessageCache.java delete mode 100644 src/main/java/com/sun/mail/imap/Rights.java delete mode 100644 src/main/java/com/sun/mail/imap/SortTerm.java delete mode 100644 src/main/java/com/sun/mail/imap/Utility.java delete mode 100644 src/main/java/com/sun/mail/imap/package.html delete mode 100644 src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/BODY.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/FLAGS.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/FetchItem.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/FetchResponse.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/Item.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/ListInfo.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/MessageSet.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/Namespaces.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/SearchSequence.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/Status.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/UID.java delete mode 100644 src/main/java/com/sun/mail/imap/protocol/UIDSet.java delete mode 100644 src/main/java/com/sun/mail/pop3/DefaultFolder.java delete mode 100644 src/main/java/com/sun/mail/pop3/POP3Folder.java delete mode 100644 src/main/java/com/sun/mail/pop3/POP3Message.java delete mode 100644 src/main/java/com/sun/mail/pop3/POP3SSLStore.java delete mode 100644 src/main/java/com/sun/mail/pop3/POP3Store.java delete mode 100644 src/main/java/com/sun/mail/pop3/Protocol.java delete mode 100644 src/main/java/com/sun/mail/pop3/Status.java delete mode 100644 src/main/java/com/sun/mail/pop3/TempFile.java delete mode 100644 src/main/java/com/sun/mail/pop3/package.html delete mode 100644 src/main/java/com/sun/mail/smtp/DigestMD5.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPMessage.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPOutputStream.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPSaslAuthenticator.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java delete mode 100644 src/main/java/com/sun/mail/smtp/SMTPTransport.java delete mode 100644 src/main/java/com/sun/mail/smtp/SaslAuthenticator.java delete mode 100644 src/main/java/com/sun/mail/smtp/package.html delete mode 100644 src/main/java/com/sun/mail/util/ASCIIUtility.java delete mode 100644 src/main/java/com/sun/mail/util/BASE64DecoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/BASE64EncoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/BEncoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/CRLFOutputStream.java delete mode 100644 src/main/java/com/sun/mail/util/DecodingException.java delete mode 100644 src/main/java/com/sun/mail/util/FolderClosedIOException.java delete mode 100644 src/main/java/com/sun/mail/util/LineInputStream.java delete mode 100644 src/main/java/com/sun/mail/util/LineOutputStream.java delete mode 100644 src/main/java/com/sun/mail/util/LogOutputStream.java delete mode 100644 src/main/java/com/sun/mail/util/MailConnectException.java delete mode 100644 src/main/java/com/sun/mail/util/MailLogger.java delete mode 100644 src/main/java/com/sun/mail/util/MailSSLSocketFactory.java delete mode 100644 src/main/java/com/sun/mail/util/MessageRemovedIOException.java delete mode 100644 src/main/java/com/sun/mail/util/MimeUtil.java delete mode 100644 src/main/java/com/sun/mail/util/PropUtil.java delete mode 100644 src/main/java/com/sun/mail/util/QDecoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/QEncoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/QPDecoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/QPEncoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/ReadableMime.java delete mode 100644 src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java delete mode 100644 src/main/java/com/sun/mail/util/SocketConnectException.java delete mode 100644 src/main/java/com/sun/mail/util/SocketFetcher.java delete mode 100644 src/main/java/com/sun/mail/util/TraceInputStream.java delete mode 100644 src/main/java/com/sun/mail/util/TraceOutputStream.java delete mode 100644 src/main/java/com/sun/mail/util/UUDecoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/UUEncoderStream.java delete mode 100644 src/main/java/com/sun/mail/util/logging/LogManagerProperties.java delete mode 100644 src/main/java/com/sun/mail/util/logging/MailHandler.java delete mode 100644 src/main/java/com/sun/mail/util/logging/package.html delete mode 100644 src/main/java/com/sun/mail/util/package.html delete mode 100644 src/main/java/javax/mail/Address.java delete mode 100644 src/main/java/javax/mail/AuthenticationFailedException.java delete mode 100644 src/main/java/javax/mail/Authenticator.java delete mode 100644 src/main/java/javax/mail/BodyPart.java delete mode 100644 src/main/java/javax/mail/EncodingAware.java delete mode 100644 src/main/java/javax/mail/EventQueue.java delete mode 100644 src/main/java/javax/mail/FetchProfile.java delete mode 100644 src/main/java/javax/mail/Flags.java delete mode 100644 src/main/java/javax/mail/Folder.java delete mode 100644 src/main/java/javax/mail/FolderClosedException.java delete mode 100644 src/main/java/javax/mail/FolderNotFoundException.java delete mode 100644 src/main/java/javax/mail/Header.java delete mode 100644 src/main/java/javax/mail/IllegalWriteException.java delete mode 100644 src/main/java/javax/mail/MailSessionDefinition.java delete mode 100644 src/main/java/javax/mail/MailSessionDefinitions.java delete mode 100644 src/main/java/javax/mail/Message.java delete mode 100644 src/main/java/javax/mail/MessageAware.java delete mode 100644 src/main/java/javax/mail/MessageContext.java delete mode 100644 src/main/java/javax/mail/MessageRemovedException.java delete mode 100644 src/main/java/javax/mail/MessagingException.java delete mode 100644 src/main/java/javax/mail/MethodNotSupportedException.java delete mode 100644 src/main/java/javax/mail/Multipart.java delete mode 100644 src/main/java/javax/mail/MultipartDataSource.java delete mode 100644 src/main/java/javax/mail/NoSuchProviderException.java delete mode 100644 src/main/java/javax/mail/Part.java delete mode 100644 src/main/java/javax/mail/PasswordAuthentication.java delete mode 100644 src/main/java/javax/mail/Provider.java delete mode 100644 src/main/java/javax/mail/Quota.java delete mode 100644 src/main/java/javax/mail/QuotaAwareStore.java delete mode 100644 src/main/java/javax/mail/ReadOnlyFolderException.java delete mode 100644 src/main/java/javax/mail/SendFailedException.java delete mode 100644 src/main/java/javax/mail/Service.java delete mode 100644 src/main/java/javax/mail/Session.java delete mode 100644 src/main/java/javax/mail/Store.java delete mode 100644 src/main/java/javax/mail/StoreClosedException.java delete mode 100644 src/main/java/javax/mail/Transport.java delete mode 100644 src/main/java/javax/mail/UIDFolder.java delete mode 100644 src/main/java/javax/mail/URLName.java delete mode 100644 src/main/java/javax/mail/event/ConnectionAdapter.java delete mode 100644 src/main/java/javax/mail/event/ConnectionEvent.java delete mode 100644 src/main/java/javax/mail/event/ConnectionListener.java delete mode 100644 src/main/java/javax/mail/event/FolderAdapter.java delete mode 100644 src/main/java/javax/mail/event/FolderEvent.java delete mode 100644 src/main/java/javax/mail/event/FolderListener.java delete mode 100644 src/main/java/javax/mail/event/MailEvent.java delete mode 100644 src/main/java/javax/mail/event/MessageChangedEvent.java delete mode 100644 src/main/java/javax/mail/event/MessageChangedListener.java delete mode 100644 src/main/java/javax/mail/event/MessageCountAdapter.java delete mode 100644 src/main/java/javax/mail/event/MessageCountEvent.java delete mode 100644 src/main/java/javax/mail/event/MessageCountListener.java delete mode 100644 src/main/java/javax/mail/event/StoreEvent.java delete mode 100644 src/main/java/javax/mail/event/StoreListener.java delete mode 100644 src/main/java/javax/mail/event/TransportAdapter.java delete mode 100644 src/main/java/javax/mail/event/TransportEvent.java delete mode 100644 src/main/java/javax/mail/event/TransportListener.java delete mode 100644 src/main/java/javax/mail/event/package.html delete mode 100644 src/main/java/javax/mail/internet/AddressException.java delete mode 100644 src/main/java/javax/mail/internet/ContentDisposition.java delete mode 100644 src/main/java/javax/mail/internet/ContentType.java delete mode 100644 src/main/java/javax/mail/internet/HeaderTokenizer.java delete mode 100644 src/main/java/javax/mail/internet/InternetAddress.java delete mode 100644 src/main/java/javax/mail/internet/InternetHeaders.java delete mode 100644 src/main/java/javax/mail/internet/MailDateFormat.java delete mode 100644 src/main/java/javax/mail/internet/MimeBodyPart.java delete mode 100644 src/main/java/javax/mail/internet/MimeMessage.java delete mode 100644 src/main/java/javax/mail/internet/MimeMultipart.java delete mode 100644 src/main/java/javax/mail/internet/MimePart.java delete mode 100644 src/main/java/javax/mail/internet/MimePartDataSource.java delete mode 100644 src/main/java/javax/mail/internet/MimeUtility.java delete mode 100644 src/main/java/javax/mail/internet/NewsAddress.java delete mode 100644 src/main/java/javax/mail/internet/ParameterList.java delete mode 100644 src/main/java/javax/mail/internet/ParseException.java delete mode 100644 src/main/java/javax/mail/internet/PreencodedMimeBodyPart.java delete mode 100644 src/main/java/javax/mail/internet/SharedInputStream.java delete mode 100644 src/main/java/javax/mail/internet/UniqueValue.java delete mode 100644 src/main/java/javax/mail/internet/package.html delete mode 100644 src/main/java/javax/mail/package.html delete mode 100644 src/main/java/javax/mail/search/AddressStringTerm.java delete mode 100644 src/main/java/javax/mail/search/AddressTerm.java delete mode 100644 src/main/java/javax/mail/search/AndTerm.java delete mode 100644 src/main/java/javax/mail/search/BodyTerm.java delete mode 100644 src/main/java/javax/mail/search/ComparisonTerm.java delete mode 100644 src/main/java/javax/mail/search/DateTerm.java delete mode 100644 src/main/java/javax/mail/search/FlagTerm.java delete mode 100644 src/main/java/javax/mail/search/FromStringTerm.java delete mode 100644 src/main/java/javax/mail/search/FromTerm.java delete mode 100644 src/main/java/javax/mail/search/HeaderTerm.java delete mode 100644 src/main/java/javax/mail/search/IntegerComparisonTerm.java delete mode 100644 src/main/java/javax/mail/search/MessageIDTerm.java delete mode 100644 src/main/java/javax/mail/search/MessageNumberTerm.java delete mode 100644 src/main/java/javax/mail/search/NotTerm.java delete mode 100644 src/main/java/javax/mail/search/OrTerm.java delete mode 100644 src/main/java/javax/mail/search/ReceivedDateTerm.java delete mode 100644 src/main/java/javax/mail/search/RecipientStringTerm.java delete mode 100644 src/main/java/javax/mail/search/RecipientTerm.java delete mode 100644 src/main/java/javax/mail/search/SearchException.java delete mode 100644 src/main/java/javax/mail/search/SearchTerm.java delete mode 100644 src/main/java/javax/mail/search/SentDateTerm.java delete mode 100644 src/main/java/javax/mail/search/SizeTerm.java delete mode 100644 src/main/java/javax/mail/search/StringTerm.java delete mode 100644 src/main/java/javax/mail/search/SubjectTerm.java delete mode 100644 src/main/java/javax/mail/search/package.html delete mode 100644 src/main/java/javax/mail/util/ByteArrayDataSource.java delete mode 100644 src/main/java/javax/mail/util/SharedByteArrayInputStream.java delete mode 100644 src/main/java/javax/mail/util/SharedFileInputStream.java delete mode 100644 src/main/java/javax/mail/util/package.html diff --git a/.gitignore b/.gitignore index 5c3fb3fc..a0348ece 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,125 @@ -/target/ -/bin/ -/.settings/ -.classpath +# Created by https://www.gitignore.io + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +#*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core .project -/target/ \ No newline at end of file + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties + + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml +.nb-gradle/ diff --git a/pom.xml b/pom.xml index c117639e..0ed082f7 100644 --- a/pom.xml +++ b/pom.xml @@ -120,6 +120,13 @@ + + @@ -147,6 +154,32 @@ 1.4.187 + + + com.sun.mail + javax.mail + 1.5.3 + + + javax.mail + mail + 1.5.0-b01 + + + + + + + com.maxmind.geoip + geoip-api + 1.2.14 + + diff --git a/snapshots/AuthMe-5.0-SNAPSHOT.jar b/snapshots/AuthMe-5.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..2aee53fc6d9e3b39f30bf7ce5ca5ffc706ee2c64 GIT binary patch literal 414325 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%0D2KX?-2W6=9E26AEXqm+{rSs5*;F46BKQgQ&m{m? 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

q=i7;Q0n2j#fm7i&Y2eC3K;Mh{dB#b;uOKw;5I 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%0AsRSGYN` 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 - *

  • Country edition -- this is the most common version of the database. It includes - * the name of the country and it's ISO country code given an IP address. - *
  • Region edition -- includes the country information as well as - * what U.S. state or Canadian province the IP address is from if the IP address - * is from the U.S. or Canada. - *
  • City edition -- includes country, region, city, postal code, latitude, and - * longitude information. - *
  • Org edition -- includes netblock owner. - *
  • ISP edition -- ISP information. - * - * - * @see com.maxmind.geoip.LookupService#getDatabaseInfo() - * @author Matt Tucker - */ -public class DatabaseInfo { - - public final static int COUNTRY_EDITION = 1; - public final static int REGION_EDITION_REV0 = 7; - public final static int REGION_EDITION_REV1 = 3; - public final static int CITY_EDITION_REV0 = 6; - public final static int CITY_EDITION_REV1 = 2; - public final static int ORG_EDITION = 5; - public final static int ISP_EDITION = 4; - public final static int PROXY_EDITION = 8; - public final static int ASNUM_EDITION = 9; - public final static int NETSPEED_EDITION = 10; - public final static int COUNTRY_EDITION_V6 = 12; - -private static SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); - - private String info; - - /** - * Creates a new DatabaseInfo object given the database info String. - * @param info - */ - public DatabaseInfo(String info) { - this.info = info; - } - - public int getType() { - if (info == null || info.equals("")) { - return COUNTRY_EDITION; - } - else { - // Get the type code from the database info string and then - // subtract 105 from the value to preserve compatability with - // databases from April 2003 and earlier. - return Integer.parseInt(info.substring(4, 7)) - 105; - } - } - - /** - * Returns true if the database is the premium version. - * - * @return true if the premium version of the database. - */ - public boolean isPremium() { - return info.indexOf("FREE") < 0; - } - - /** - * Returns the date of the database. - * - * @return the date of the database. - */ - public Date getDate() { - for (int i=0; i - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/src/main/java/com/maxmind/geoip/Location.java b/src/main/java/com/maxmind/geoip/Location.java deleted file mode 100644 index 040fcb6b..00000000 --- a/src/main/java/com/maxmind/geoip/Location.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Location.java - * - * Copyright (C) 2004 MaxMind LLC. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Lesser Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package com.maxmind.geoip; - -public class Location { - public String countryCode; - public String countryName; - public String region; - public String city; - public String postalCode; - public float latitude; - public float longitude; - public int dma_code; - public int area_code; - public int metro_code; - - private final static double EARTH_DIAMETER = 2 * 6378.2; - private final static double PI = 3.14159265; - private final static double RAD_CONVERT = PI / 180; - - public double distance (Location loc) { - double delta_lat, delta_lon; - double temp; - - float lat1 = latitude; - float lon1 = longitude; - float lat2 = loc.latitude; - float lon2 = loc.longitude; - - // convert degrees to radians - lat1 *= RAD_CONVERT; - lat2 *= RAD_CONVERT; - - // find the deltas - delta_lat = lat2 - lat1; - delta_lon = (lon2 - lon1) * RAD_CONVERT; - - // Find the great circle distance - temp = Math.pow(Math.sin(delta_lat/2),2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(delta_lon/2),2); - return EARTH_DIAMETER * Math.atan2(Math.sqrt(temp),Math.sqrt(1-temp)); - } -} diff --git a/src/main/java/com/maxmind/geoip/LookupService.java b/src/main/java/com/maxmind/geoip/LookupService.java deleted file mode 100644 index 72a33260..00000000 --- a/src/main/java/com/maxmind/geoip/LookupService.java +++ /dev/null @@ -1,1046 +0,0 @@ -/** - * LookupService.java - * - * Copyright (C) 2003 MaxMind LLC. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package com.maxmind.geoip; - - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.StringTokenizer; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -/** - * Provides a lookup service for information based on an IP address. The location of - * a database file is supplied when creating a lookup service instance. The edition of - * the database determines what information is available about an IP address. See the - * DatabaseInfo class for further details.

    - * - * The following code snippet demonstrates looking up the country that an IP - * address is from: - *

    - * // First, create a LookupService instance with the location of the database.
    - * LookupService lookupService = new LookupService("c:\\geoip.dat");
    - * // Assume we have a String ipAddress (in dot-decimal form).
    - * Country country = lookupService.getCountry(ipAddress);
    - * System.out.println("The country is: " + country.getName());
    - * System.out.println("The country code is: " + country.getCode());
    - * 
    - * - * In general, a single LookupService instance should be created and then reused - * repeatedly.

    - * - * Tip: Those deploying the GeoIP API as part of a web application may find it - * difficult to pass in a File to create the lookup service, as the location of the - * database may vary per deployment or may even be part of the web-application. In this - * case, the database should be added to the classpath of the web-app. For example, by - * putting it into the WEB-INF/classes directory of the web application. The following code - * snippet demonstrates how to create a LookupService using a database that can be found - * on the classpath: - * - *

    - * String fileName = getClass().getResource("/GeoIP.dat").toExternalForm().substring(6);
    - * LookupService lookupService = new LookupService(fileName);
    - * - * @author Matt Tucker (matt@jivesoftware.com) - */ -public class LookupService { - - /** - * Database file. - */ - private RandomAccessFile file = null; - private File databaseFile = null; - - /** - * Information about the database. - */ - private DatabaseInfo databaseInfo = null; - - /** - * The database type. Default is the country edition. - */ - byte databaseType = DatabaseInfo.COUNTRY_EDITION; - - int databaseSegments[]; - int recordLength; - - String licenseKey; - int dnsService = 0; - int dboptions; - byte dbbuffer[]; - byte index_cache[]; - long mtime; - int last_netmask; - private final static int US_OFFSET = 1; - private final static int CANADA_OFFSET = 677; - private final static int WORLD_OFFSET = 1353; - private final static int FIPS_RANGE = 360; - private final static int COUNTRY_BEGIN = 16776960; - private final static int STATE_BEGIN_REV0 = 16700000; - private final static int STATE_BEGIN_REV1 = 16000000; - private final static int STRUCTURE_INFO_MAX_SIZE = 20; - private final static int DATABASE_INFO_MAX_SIZE = 100; - public final static int GEOIP_STANDARD = 0; - public final static int GEOIP_MEMORY_CACHE = 1; - public final static int GEOIP_CHECK_CACHE = 2; - public final static int GEOIP_INDEX_CACHE = 4; - public final static int GEOIP_UNKNOWN_SPEED = 0; - public final static int GEOIP_DIALUP_SPEED = 1; - public final static int GEOIP_CABLEDSL_SPEED = 2; - public final static int GEOIP_CORPORATE_SPEED = 3; - - - private final static int SEGMENT_RECORD_LENGTH = 3; - private final static int STANDARD_RECORD_LENGTH = 3; - private final static int ORG_RECORD_LENGTH = 4; - private final static int MAX_RECORD_LENGTH = 4; - - private final static int MAX_ORG_RECORD_LENGTH = 300; - private final static int FULL_RECORD_LENGTH = 60; - - private final Country UNKNOWN_COUNTRY = new Country("--", "N/A"); - - private static final HashMap hashmapcountryCodetoindex = new HashMap(512); - private static final HashMap hashmapcountryNametoindex = new HashMap(512); - private static final String[] countryCode = { - "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR", - "AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ", - "BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF", - "CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ", - "DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI", - "FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL", - "GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR", - "HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP", - "KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC", - "LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK", - "ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY", - "MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM", - "PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY", - "QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ", - "SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG", - "TH","TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW","TZ","UA","UG", - "UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE", - "YT","RS","ZA","ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE","BL", - "MF"}; - - private static final String[] countryName = { - "N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates", - "Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia", - "Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa", - "Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina", - "Barbados","Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain", - "Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas", - "Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada", - "Cocos (Keeling) Islands","Congo, The Democratic Republic of the", - "Central African Republic","Congo","Switzerland","Cote D'Ivoire", - "Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba", - "Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany", - "Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador", - "Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland", - "Fiji","Falkland Islands (Malvinas)","Micronesia, Federated States of", - "Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom", - "Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia", - "Guinea","Guadeloupe","Equatorial Guinea","Greece", - "South Georgia and the South Sandwich Islands","Guatemala","Guam", - "Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands", - "Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India", - "British Indian Ocean Territory","Iraq","Iran, Islamic Republic of", - "Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia", - "Kiribati","Comoros","Saint Kitts and Nevis", - "Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", - "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon", - "Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", - "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco", - "Moldova, Republic of","Madagascar","Marshall Islands", - "Macedonia","Mali","Myanmar","Mongolia", - "Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat", - "Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique", - "Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", - "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama", - "Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan", - "Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","" + - "Palestinian Territory","Portugal","Palau","Paraguay","Qatar", - "Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia", - "Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena", - "Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino", - "Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador", - "Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad", - "French Southern Territories","Togo","Thailand","Tajikistan","Tokelau", - "Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago", - "Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda", - "United States Minor Outlying Islands","United States","Uruguay","Uzbekistan", - "Holy See (Vatican City State)","Saint Vincent and the Grenadines", - "Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam", - "Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia", - "South Africa","Zambia","Montenegro","Zimbabwe","Anonymous Proxy", - "Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", - "Saint Barthelemy","Saint Martin"}; - - - /* init the hashmap once at startup time */ - static { - int i; - if(countryCode.length!=countryName.length) - throw new AssertionError("countryCode.length!=countryName.length"); - - // distributed service only - for (i = 0; i < countryCode.length ;i++){ - hashmapcountryCodetoindex.put(countryCode[i],Integer.valueOf(i)); - hashmapcountryNametoindex.put(countryName[i],Integer.valueOf(i)); - } - }; - - - /** - * Create a new distributed lookup service using the license key - * - * @param databaseFile String representation of the database file. - * @param licenseKey license key provided by Maxmind to access distributed service - */ - public LookupService(String databaseFile,String licenseKey) throws IOException { - this(new File(databaseFile)); - this.licenseKey = licenseKey; - dnsService = 1; - } - /** - * Create a new distributed lookup service using the license key - * - * @param databaseFile the database file. - * @param licenseKey license key provided by Maxmind to access distributed service - */ - public LookupService(File databaseFile,String licenseKey) throws IOException { - this(databaseFile); - this.licenseKey = licenseKey; - dnsService = 1; - } - /** - * Create a new distributed lookup service using the license key - * - * @param options Resevered for future use - * @param licenseKey license key provided by Maxmind to access distributed service - */ - public LookupService(int options,String licenseKey) throws IOException { - this.licenseKey = licenseKey; - dnsService = 1; - init(); - } - /** - * Create a new lookup service using the specified database file. - * - * @param databaseFile String representation of the database file. - * @throws java.io.IOException if an error occured creating the lookup service - * from the database file. - */ - public LookupService(String databaseFile) throws IOException { - this(new File(databaseFile)); - } - - /** - * Create a new lookup service using the specified database file. - * - * @param databaseFile the database file. - * @throws java.io.IOException if an error occured creating the lookup service - * from the database file. - */ - public LookupService(File databaseFile) throws IOException { - this.databaseFile = databaseFile; - this.file = new RandomAccessFile(databaseFile, "r"); - init(); - } - - /** - * Create a new lookup service using the specified database file. - * - * @param databaseFile String representation of the database file. - * @param options database flags to use when opening the database - * GEOIP_STANDARD read database from disk - * GEOIP_MEMORY_CACHE cache the database in RAM and read it from RAM - * @throws java.io.IOException if an error occured creating the lookup service - * from the database file. - */ - public LookupService(String databaseFile, int options) throws IOException{ - this(new File(databaseFile),options); - } - - /** - * Create a new lookup service using the specified database file. - * - * @param databaseFile the database file. - * @param options database flags to use when opening the database - * GEOIP_STANDARD read database from disk - * GEOIP_MEMORY_CACHE cache the database in RAM and read it from RAM - * @throws java.io.IOException if an error occured creating the lookup service - * from the database file. - */ - public LookupService(File databaseFile, int options) throws IOException{ - this.databaseFile = databaseFile; - this.file = new RandomAccessFile(databaseFile, "r"); - dboptions = options; - init(); - } - /** - * Reads meta-data from the database file. - * - * @throws java.io.IOException if an error occurs reading from the database file. - */ - private void init() throws IOException { - int i, j; - byte [] delim = new byte[3]; - byte [] buf = new byte[SEGMENT_RECORD_LENGTH]; - - if (file == null) { - return; - } - if ((dboptions & GEOIP_CHECK_CACHE) != 0) { - mtime = databaseFile.lastModified(); - } - file.seek(file.length() - 3); - for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) { - file.readFully(delim); - if (delim[0] == -1 && delim[1] == -1 && delim[2] == -1) { - databaseType = file.readByte(); - if (databaseType >= 106) { - // Backward compatibility with databases from April 2003 and earlier - databaseType -= 105; - } - // Determine the database type. - if (databaseType == DatabaseInfo.REGION_EDITION_REV0) { - databaseSegments = new int[1]; - databaseSegments[0] = STATE_BEGIN_REV0; - recordLength = STANDARD_RECORD_LENGTH; - }else if (databaseType == DatabaseInfo.REGION_EDITION_REV1){ - databaseSegments = new int[1]; - databaseSegments[0] = STATE_BEGIN_REV1; - recordLength = STANDARD_RECORD_LENGTH; - } - else if (databaseType == DatabaseInfo.CITY_EDITION_REV0 || - databaseType == DatabaseInfo.CITY_EDITION_REV1 || - databaseType == DatabaseInfo.ORG_EDITION || - databaseType == DatabaseInfo.ISP_EDITION || - databaseType == DatabaseInfo.ASNUM_EDITION) { - databaseSegments = new int[1]; - databaseSegments[0] = 0; - if (databaseType == DatabaseInfo.CITY_EDITION_REV0 || - databaseType == DatabaseInfo.CITY_EDITION_REV1 || - databaseType == DatabaseInfo.ASNUM_EDITION) { - recordLength = STANDARD_RECORD_LENGTH; - } - else { - recordLength = ORG_RECORD_LENGTH; - } - file.readFully(buf); - for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) { - databaseSegments[0] += (unsignedByteToInt(buf[j]) << (j * 8)); - } - } - break; - } - else { - file.seek(file.getFilePointer() - 4); - } - } - if ((databaseType == DatabaseInfo.COUNTRY_EDITION) || - (databaseType == DatabaseInfo.COUNTRY_EDITION_V6) || - (databaseType == DatabaseInfo.PROXY_EDITION) || - (databaseType == DatabaseInfo.NETSPEED_EDITION)) { - databaseSegments = new int[1]; - databaseSegments[0] = COUNTRY_BEGIN; - recordLength = STANDARD_RECORD_LENGTH; - } - if ((dboptions & GEOIP_MEMORY_CACHE) == 1) { - int l = (int) file.length(); - dbbuffer = new byte[l]; - file.seek(0); - file.readFully(dbbuffer,0,l); - databaseInfo = this.getDatabaseInfo(); - file.close(); - } - if ((dboptions & GEOIP_INDEX_CACHE) != 0) { - int l = databaseSegments[0] * recordLength * 2; - index_cache = new byte[l]; - if (index_cache != null){ - file.seek(0); - file.readFully(index_cache,0,l); - } - } else { - index_cache = null; - } - } - - /** - * Closes the lookup service. - */ - public void close() { - try { - if (file != null){ - file.close(); - } - file = null; - } - catch (Exception e) { } - } - - /** - * Returns the country the IP address is in. - * - * @param ipAddress String version of an IPv6 address, i.e. "::127.0.0.1" - * @return the country the IP address is from. - */ - public Country getCountryV6(String ipAddress) { - InetAddress addr; - try { - addr = Inet6Address.getByName(ipAddress); - } - catch (UnknownHostException e) { - return UNKNOWN_COUNTRY; - } - return getCountryV6(addr); - } - - /** - * Returns the country the IP address is in. - * - * @param ipAddress String version of an IP address, i.e. "127.0.0.1" - * @return the country the IP address is from. - */ - public Country getCountry(String ipAddress) { - InetAddress addr; - try { - addr = InetAddress.getByName(ipAddress); - } - catch (UnknownHostException e) { - return UNKNOWN_COUNTRY; - } - return getCountry(bytesToLong(addr.getAddress())); - } - - /** - * Returns the country the IP address is in. - * - * @param ipAddress the IP address. - * @return the country the IP address is from. - */ - public synchronized Country getCountry(InetAddress ipAddress) { - return getCountry(bytesToLong(ipAddress.getAddress())); - } - - /** - * Returns the country the IP address is in. - * - * @param addr the IP address as Inet6Address. - * @return the country the IP address is from. - */ - public Country getCountryV6(InetAddress addr) { - if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) { - throw new IllegalStateException("Database has been closed."); - } - int ret = seekCountryV6(addr) - COUNTRY_BEGIN; - if (ret == 0) { - return UNKNOWN_COUNTRY; - } - else { - return new Country(countryCode[ret], countryName[ret]); - } - } - - /** - * Returns the country the IP address is in. - * - * @param ipAddress the IP address in long format. - * @return the country the IP address is from. - */ - public Country getCountry(long ipAddress) { - if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) { - throw new IllegalStateException("Database has been closed."); - } - int ret = seekCountry(ipAddress) - COUNTRY_BEGIN; - if (ret == 0) { - return UNKNOWN_COUNTRY; - } - else { - return new Country(countryCode[ret], countryName[ret]); - } - } - - public int getID(String ipAddress) { - InetAddress addr; - try { - addr = InetAddress.getByName(ipAddress); - } - catch (UnknownHostException e) { - return 0; - } - return getID(bytesToLong(addr.getAddress())); - } - - public int getID(InetAddress ipAddress) { - return getID(bytesToLong(ipAddress.getAddress())); - } - - public synchronized int getID(long ipAddress) { - if (file == null && (dboptions & GEOIP_MEMORY_CACHE) == 0) { - throw new IllegalStateException("Database has been closed."); - } - int ret = seekCountry(ipAddress) - databaseSegments[0]; - return ret; - } - - public int last_netmask() { - return this.last_netmask; - } - - public void netmask(int nm){ - this.last_netmask = nm; - } - - /** - * Returns information about the database. - * - * @return database info. - */ - public synchronized DatabaseInfo getDatabaseInfo() { - if (databaseInfo != null) { - return databaseInfo; - } - try { - _check_mtime(); - boolean hasStructureInfo = false; - byte [] delim = new byte[3]; - // Advance to part of file where database info is stored. - file.seek(file.length() - 3); - for (int i=0; i env = new Hashtable(); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - // TODO don't specify ws1, instead use ns servers for s.maxmind.com - env.put("java.naming.provider.url","dns://ws1.maxmind.com/"); - - DirContext ictx = new InitialDirContext(env); - Attributes attrs = ictx.getAttributes(licenseKey + "." + ip + ".s.maxmind.com", new String[] {"txt"}); - //System.out.println(attrs.get("txt").get()); - String str = attrs.get("txt").get().toString(); - return str; - } - catch(NamingException e) { - // TODO fix this to handle exceptions - System.out.println("DNS error"); - return null; - } - - } - - public Location getLocationwithdnsservice(String str) { - Location record = new Location(); - String key; - String value; - StringTokenizer st = new StringTokenizer(str,";=\""); - while (st.hasMoreTokens()) { - key = st.nextToken(); - if (st.hasMoreTokens()) { - value = st.nextToken(); - } else { - value = "";} - if (key.equals("co")) { - Integer i = (Integer)hashmapcountryCodetoindex.get(value); - record.countryCode = value; - record.countryName = countryName[i.intValue()]; - } - if (key.equals("ci")) { - record.city = value; - } - if (key.equals("re")) { - record.region = value; - } - if (key.equals("zi")) { - record.postalCode = value; - } - // TODO, ISP and Organization - //if (key.equals("or")) { - //record.org = value; - //} - //if (key.equals("is")) { - //record.isp = value; - //} - if (key.equals("la")) { - try{ - record.latitude = Float.parseFloat(value); - } catch(NumberFormatException e) { - record.latitude = 0; - } - } - if (key.equals("lo")) { - try{ - record.longitude = Float.parseFloat(value); - } catch(NumberFormatException e) { - record.latitude = 0; - } - } - // dm depreciated use me ( metro_code ) instead - if (key.equals("dm") || key.equals("me")) { - try{ - record.metro_code = record.dma_code = Integer.parseInt(value); - } catch(NumberFormatException e) { - record.metro_code = record.dma_code = 0; - } - } - if (key.equals("ac")) { - try{ - record.area_code = Integer.parseInt(value); - } catch(NumberFormatException e) { - record.area_code = 0; - } - } - } - return record; - } - - public synchronized Region getRegion(String str) { - InetAddress addr; - try { - addr = InetAddress.getByName(str); - } - catch (UnknownHostException e) { - return null; - } - - return getRegion(bytesToLong(addr.getAddress())); - } - - public synchronized Region getRegion(long ipnum) { - Region record = new Region(); - int seek_region = 0; - if (databaseType == DatabaseInfo.REGION_EDITION_REV0) { - seek_region = seekCountry(ipnum) - STATE_BEGIN_REV0; - char ch[] = new char[2]; - if (seek_region >= 1000) { - record.countryCode = "US"; - record.countryName = "United States"; - ch[0] = (char)(((seek_region - 1000)/26) + 65); - ch[1] = (char)(((seek_region - 1000)%26) + 65); - record.region = new String(ch); - } else { - record.countryCode = countryCode[seek_region]; - record.countryName = countryName[seek_region]; - record.region = ""; - } - } else if (databaseType == DatabaseInfo.REGION_EDITION_REV1) { - seek_region = seekCountry(ipnum) - STATE_BEGIN_REV1; - char ch[] = new char[2]; - if (seek_region < US_OFFSET) { - record.countryCode = ""; - record.countryName = ""; - record.region = ""; - } else if (seek_region < CANADA_OFFSET) { - record.countryCode = "US"; - record.countryName = "United States"; - ch[0] = (char)(((seek_region - US_OFFSET)/26) + 65); - ch[1] = (char)(((seek_region - US_OFFSET)%26) + 65); - record.region = new String(ch); - } else if (seek_region < WORLD_OFFSET) { - record.countryCode = "CA"; - record.countryName = "Canada"; - ch[0] = (char)(((seek_region - CANADA_OFFSET)/26) + 65); - ch[1] = (char)(((seek_region - CANADA_OFFSET)%26) + 65); - record.region = new String(ch); - } else { - record.countryCode = countryCode[(seek_region - WORLD_OFFSET) / FIPS_RANGE]; - record.countryName = countryName[(seek_region - WORLD_OFFSET) / FIPS_RANGE]; - record.region = ""; - } - } - return record; - } - - public synchronized Location getLocation(long ipnum) { - int record_pointer; - byte record_buf[] = new byte[FULL_RECORD_LENGTH]; - int record_buf_offset = 0; - Location record = new Location(); - int str_length = 0; - int j, seek_country; - double latitude = 0, longitude = 0; - - try { - seek_country = seekCountry(ipnum); - if (seek_country == databaseSegments[0]) { - return null; - } - record_pointer = seek_country + (2 * recordLength - 1) * databaseSegments[0]; - - if ((dboptions & GEOIP_MEMORY_CACHE) == 1) { - //read from memory - System.arraycopy(dbbuffer, record_pointer, record_buf, 0, Math.min(dbbuffer.length - record_pointer, FULL_RECORD_LENGTH)); - } else { - //read from disk - file.seek(record_pointer); - file.readFully(record_buf); - } - - // get country - record.countryCode = countryCode[unsignedByteToInt(record_buf[0])]; - record.countryName = countryName[unsignedByteToInt(record_buf[0])]; - record_buf_offset++; - - // get region - while (record_buf[record_buf_offset + str_length] != '\0') - str_length++; - if (str_length > 0) { - record.region = new String(record_buf, record_buf_offset, str_length); - } - record_buf_offset += str_length + 1; - str_length = 0; - - // get city - while (record_buf[record_buf_offset + str_length] != '\0') - str_length++; - if (str_length > 0) { - record.city = new String(record_buf, record_buf_offset, str_length, "ISO-8859-1"); - } - record_buf_offset += str_length + 1; - str_length = 0; - - // get postal code - while (record_buf[record_buf_offset + str_length] != '\0') - str_length++; - if (str_length > 0) { - record.postalCode = new String(record_buf, record_buf_offset, str_length); - } - record_buf_offset += str_length + 1; - - // get latitude - for (j = 0; j < 3; j++) - latitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8)); - record.latitude = (float) latitude/10000 - 180; - record_buf_offset += 3; - - // get longitude - for (j = 0; j < 3; j++) - longitude += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8)); - record.longitude = (float) longitude/10000 - 180; - - record.dma_code = record.metro_code = 0; - record.area_code = 0; - if (databaseType == DatabaseInfo.CITY_EDITION_REV1) { - // get DMA code - int metroarea_combo = 0; - if (record.countryCode == "US") { - record_buf_offset += 3; - for (j = 0; j < 3; j++) - metroarea_combo += (unsignedByteToInt(record_buf[record_buf_offset + j]) << (j * 8)); - record.metro_code = record.dma_code = metroarea_combo/1000; - record.area_code = metroarea_combo % 1000; - } - } - } - catch (IOException e) { - System.err.println("IO Exception while setting up segments"); - } - return record; - } - - public String getOrg(InetAddress addr) { - return getOrg(bytesToLong(addr.getAddress())); - } - - public String getOrg(String str) { - InetAddress addr; - try { - addr = InetAddress.getByName(str); - } - catch (UnknownHostException e) { - return null; - } - return getOrg(addr); - } - - // GeoIP Organization and ISP Edition methods - public synchronized String getOrg(long ipnum) { - int seek_org; - int record_pointer; - int str_length = 0; - byte [] buf = new byte[MAX_ORG_RECORD_LENGTH]; - String org_buf; - - try { - seek_org = seekCountry(ipnum); - if (seek_org == databaseSegments[0]) { - return null; - } - - record_pointer = seek_org + (2 * recordLength - 1) * databaseSegments[0]; - if ((dboptions & GEOIP_MEMORY_CACHE) == 1) { - //read from memory - System.arraycopy(dbbuffer, record_pointer, buf, 0, Math.min(dbbuffer.length - record_pointer, MAX_ORG_RECORD_LENGTH)); - } else { - //read from disk - file.seek(record_pointer); - file.readFully(buf); - } - while (buf[str_length] != '\0') { - str_length++; - } - org_buf = new String(buf, 0, str_length, "ISO-8859-1"); - return org_buf; - } - catch (IOException e) { - System.out.println("IO Exception"); - return null; - } - } - - /** - * Finds the country index value given an IPv6 address. - * - * @param addr the ip address to find in long format. - * @return the country index. - */ - private synchronized int seekCountryV6(InetAddress addr) { - byte [] v6vec = addr.getAddress(); - byte [] buf = new byte[2 * MAX_RECORD_LENGTH]; - int [] x = new int[2]; - int offset = 0; - _check_mtime(); - for (int depth = 127; depth >= 0; depth--) { - if ((dboptions & GEOIP_MEMORY_CACHE) == 1) { - //read from memory - for (int i = 0;i < 2 * MAX_RECORD_LENGTH;i++) { - buf[i] = dbbuffer[(2 * recordLength * offset)+i]; - } - } else if ((dboptions & GEOIP_INDEX_CACHE) != 0) { - //read from index cache - for (int i = 0;i < 2 * MAX_RECORD_LENGTH;i++) { - buf[i] = index_cache[(2 * recordLength * offset)+i]; - } - } else { - //read from disk - try { - file.seek(2 * recordLength * offset); - file.readFully(buf); - } - catch (IOException e) { - System.out.println("IO Exception"); - } - } - for (int i = 0; i<2; i++) { - x[i] = 0; - for (int j = 0; j> 3; - int b_mask = 1 << ( bnum & 7 ^ 7 ); - if ((v6vec[idx] & b_mask) > 0) { - if (x[1] >= databaseSegments[0]) { - last_netmask = 128 - depth; - return x[1]; - } - offset = x[1]; - } - else { - if (x[0] >= databaseSegments[0]) { - last_netmask = 128 - depth; - return x[0]; - } - offset = x[0]; - } - } - - // shouldn't reach here - System.err.println("Error seeking country while seeking " + addr.getHostAddress() ); - return 0; - } - /** - * Finds the country index value given an IP address. - * - * @param ipAddress the ip address to find in long format. - * @return the country index. - */ - private synchronized int seekCountry(long ipAddress) { - byte [] buf = new byte[2 * MAX_RECORD_LENGTH]; - int [] x = new int[2]; - int offset = 0; - _check_mtime(); - for (int depth = 31; depth >= 0; depth--) { - if ((dboptions & GEOIP_MEMORY_CACHE) == 1) { - //read from memory - for (int i = 0;i < 2 * MAX_RECORD_LENGTH;i++) { - buf[i] = dbbuffer[(2 * recordLength * offset)+i]; - } - } else if ((dboptions & GEOIP_INDEX_CACHE) != 0) { - //read from index cache - for (int i = 0;i < 2 * MAX_RECORD_LENGTH;i++) { - buf[i] = index_cache[(2 * recordLength * offset)+i]; - } - } else { - //read from disk - try { - file.seek(2 * recordLength * offset); - file.readFully(buf); - } - catch (IOException e) { - System.out.println("IO Exception"); - } - } - for (int i = 0; i<2; i++) { - x[i] = 0; - for (int j = 0; j 0) { - if (x[1] >= databaseSegments[0]) { - last_netmask = 32 - depth; - return x[1]; - } - offset = x[1]; - } - else { - if (x[0] >= databaseSegments[0]) { - last_netmask = 32 - depth; - return x[0]; - } - offset = x[0]; - } - } - - // shouldn't reach here - System.err.println("Error seeking country while seeking " + ipAddress); - return 0; - } - - /** - * Returns the long version of an IP address given an InetAddress object. - * - * @param address the InetAddress. - * @return the long form of the IP address. - */ - private static long bytesToLong(byte [] address) { - long ipnum = 0; - for (int i = 0; i < 4; ++i) { - long y = address[i]; - if (y < 0) { - y+= 256; - } - ipnum += y << ((3-i)*8); - } - return ipnum; - } - - private static int unsignedByteToInt(byte b) { - return (int) b & 0xFF; - } -} diff --git a/src/main/java/com/maxmind/geoip/Region.java b/src/main/java/com/maxmind/geoip/Region.java deleted file mode 100644 index cba0594b..00000000 --- a/src/main/java/com/maxmind/geoip/Region.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.maxmind.geoip; - -public class Region{ - public String countryCode; - public String countryName; - public String region; -} - diff --git a/src/main/java/com/maxmind/geoip/regionName.java b/src/main/java/com/maxmind/geoip/regionName.java deleted file mode 100644 index da261ca1..00000000 --- a/src/main/java/com/maxmind/geoip/regionName.java +++ /dev/null @@ -1,13400 +0,0 @@ -package com.maxmind.geoip; -// generated automatically from admin/generate_regionName.pl -public class regionName { -static public String regionNameByCode(String country_code,String region_code) { - String name = null; - int region_code2 = -1; - if (region_code == null) { return null; } - if (region_code.equals("")) { return null; } - - if ( ((region_code.charAt(0) >= 48 ) && ( region_code.charAt(0) < ( 48 + 10 ))) - && ((region_code.charAt(1) >= 48 ) && ( region_code.charAt(1) < ( 48 + 10 ))) - ){ - // only numbers, that shorten the large switch statements - region_code2 = (region_code.charAt(0)- 48) * 10 + region_code.charAt(1) - 48; - } - else if ( ( ((region_code.charAt(0) >= 65) && (region_code.charAt(0) < (65 + 26))) - || ((region_code.charAt(0) >= 48) && (region_code.charAt(0) < (48 + 10)))) - && ( ((region_code.charAt(1) >= 65) && (region_code.charAt(1) < (65 + 26))) - || ((region_code.charAt(1) >= 48) && (region_code.charAt(1) < (48 + 10)))) - ) { - - region_code2 = (region_code.charAt(0) - 48) * (65 + 26 - 48) + region_code.charAt(1) - 48 + 100; - } - - if (region_code2 == -1) {return null;} - if (country_code.equals("CA") == true) { - switch (region_code2) { - case 849: - name = "Alberta"; - break; - case 893: - name = "British Columbia"; - break; - case 1365: - name = "Manitoba"; - break; - case 1408: - name = "New Brunswick"; - break; - case 1418: - name = "Newfoundland"; - break; - case 1425: - name = "Nova Scotia"; - break; - case 1427: - name = "Nunavut"; - break; - case 1463: - name = "Ontario"; - break; - case 1497: - name = "Prince Edward Island"; - break; - case 1538: - name = "Quebec"; - break; - case 1632: - name = "Saskatchewan"; - break; - case 1426: - name = "Northwest Territories"; - break; - case 1899: - name = "Yukon Territory"; - break; - } - } - if (country_code.equals("US") == true) { - switch (region_code2) { - case 848: - name = "Armed Forces Americas"; - break; - case 852: - name = "Armed Forces Europe"; - break; - case 858: - name = "Alaska"; - break; - case 859: - name = "Alabama"; - break; - case 863: - name = "Armed Forces Pacific"; - break; - case 865: - name = "Arkansas"; - break; - case 866: - name = "American Samoa"; - break; - case 873: - name = "Arizona"; - break; - case 934: - name = "California"; - break; - case 948: - name = "Colorado"; - break; - case 953: - name = "Connecticut"; - break; - case 979: - name = "District of Columbia"; - break; - case 981: - name = "Delaware"; - break; - case 1074: - name = "Florida"; - break; - case 1075: - name = "Federated States of Micronesia"; - break; - case 1106: - name = "Georgia"; - break; - case 1126: - name = "Guam"; - break; - case 1157: - name = "Hawaii"; - break; - case 1192: - name = "Iowa"; - break; - case 1195: - name = "Idaho"; - break; - case 1203: - name = "Illinois"; - break; - case 1205: - name = "Indiana"; - break; - case 1296: - name = "Kansas"; - break; - case 1302: - name = "Kentucky"; - break; - case 1321: - name = "Louisiana"; - break; - case 1364: - name = "Massachusetts"; - break; - case 1367: - name = "Maryland"; - break; - case 1368: - name = "Maine"; - break; - case 1371: - name = "Marshall Islands"; - break; - case 1372: - name = "Michigan"; - break; - case 1377: - name = "Minnesota"; - break; - case 1378: - name = "Missouri"; - break; - case 1379: - name = "Northern Mariana Islands"; - break; - case 1382: - name = "Mississippi"; - break; - case 1383: - name = "Montana"; - break; - case 1409: - name = "North Carolina"; - break; - case 1410: - name = "North Dakota"; - break; - case 1411: - name = "Nebraska"; - break; - case 1414: - name = "New Hampshire"; - break; - case 1416: - name = "New Jersey"; - break; - case 1419: - name = "New Mexico"; - break; - case 1428: - name = "Nevada"; - break; - case 1431: - name = "New York"; - break; - case 1457: - name = "Ohio"; - break; - case 1460: - name = "Oklahoma"; - break; - case 1467: - name = "Oregon"; - break; - case 1493: - name = "Pennsylvania"; - break; - case 1510: - name = "Puerto Rico"; - break; - case 1515: - name = "Palau"; - break; - case 1587: - name = "Rhode Island"; - break; - case 1624: - name = "South Carolina"; - break; - case 1625: - name = "South Dakota"; - break; - case 1678: - name = "Tennessee"; - break; - case 1688: - name = "Texas"; - break; - case 1727: - name = "Utah"; - break; - case 1751: - name = "Virginia"; - break; - case 1759: - name = "Virgin Islands"; - break; - case 1770: - name = "Vermont"; - break; - case 1794: - name = "Washington"; - break; - case 1815: - name = "West Virginia"; - break; - case 1802: - name = "Wisconsin"; - break; - case 1818: - name = "Wyoming"; - break; - } - } - if (country_code.equals("AD") == true) { - switch (region_code2) { - case 2: - name = "Canillo"; - break; - case 3: - name = "Encamp"; - break; - case 4: - name = "La Massana"; - break; - case 5: - name = "Ordino"; - break; - case 6: - name = "Sant Julia de Loria"; - break; - case 7: - name = "Andorra la Vella"; - break; - case 8: - name = "Escaldes-Engordany"; - break; - } - } - if (country_code.equals("AE") == true) { - switch (region_code2) { - case 1: - name = "Abu Dhabi"; - break; - case 2: - name = "Ajman"; - break; - case 3: - name = "Dubai"; - break; - case 4: - name = "Fujairah"; - break; - case 5: - name = "Ras Al Khaimah"; - break; - case 6: - name = "Sharjah"; - break; - case 7: - name = "Umm Al Quwain"; - break; - } - } - if (country_code.equals("AF") == true) { - switch (region_code2) { - case 1: - name = "Badakhshan"; - break; - case 2: - name = "Badghis"; - break; - case 3: - name = "Baghlan"; - break; - case 5: - name = "Bamian"; - break; - case 6: - name = "Farah"; - break; - case 7: - name = "Faryab"; - break; - case 8: - name = "Ghazni"; - break; - case 9: - name = "Ghowr"; - break; - case 10: - name = "Helmand"; - break; - case 11: - name = "Herat"; - break; - case 13: - name = "Kabol"; - break; - case 14: - name = "Kapisa"; - break; - case 15: - name = "Konar"; - break; - case 16: - name = "Laghman"; - break; - case 17: - name = "Lowgar"; - break; - case 18: - name = "Nangarhar"; - break; - case 19: - name = "Nimruz"; - break; - case 21: - name = "Paktia"; - break; - case 22: - name = "Parvan"; - break; - case 23: - name = "Kandahar"; - break; - case 24: - name = "Kondoz"; - break; - case 26: - name = "Takhar"; - break; - case 27: - name = "Vardak"; - break; - case 28: - name = "Zabol"; - break; - case 29: - name = "Paktika"; - break; - case 30: - name = "Balkh"; - break; - case 31: - name = "Jowzjan"; - break; - case 32: - name = "Samangan"; - break; - case 33: - name = "Sar-e Pol"; - break; - case 34: - name = "Konar"; - break; - case 35: - name = "Laghman"; - break; - case 36: - name = "Paktia"; - break; - case 37: - name = "Khowst"; - break; - case 38: - name = "Nurestan"; - break; - case 39: - name = "Oruzgan"; - break; - case 40: - name = "Parvan"; - break; - case 41: - name = "Daykondi"; - break; - case 42: - name = "Panjshir"; - break; - } - } - if (country_code.equals("AG") == true) { - switch (region_code2) { - case 1: - name = "Barbuda"; - break; - case 3: - name = "Saint George"; - break; - case 4: - name = "Saint John"; - break; - case 5: - name = "Saint Mary"; - break; - case 6: - name = "Saint Paul"; - break; - case 7: - name = "Saint Peter"; - break; - case 8: - name = "Saint Philip"; - break; - } - } - if (country_code.equals("AL") == true) { - switch (region_code2) { - case 40: - name = "Berat"; - break; - case 41: - name = "Diber"; - break; - case 42: - name = "Durres"; - break; - case 43: - name = "Elbasan"; - break; - case 44: - name = "Fier"; - break; - case 45: - name = "Gjirokaster"; - break; - case 46: - name = "Korce"; - break; - case 47: - name = "Kukes"; - break; - case 48: - name = "Lezhe"; - break; - case 49: - name = "Shkoder"; - break; - case 50: - name = "Tirane"; - break; - case 51: - name = "Vlore"; - break; - } - } - if (country_code.equals("AM") == true) { - switch (region_code2) { - case 1: - name = "Aragatsotn"; - break; - case 2: - name = "Ararat"; - break; - case 3: - name = "Armavir"; - break; - case 4: - name = "Geghark'unik'"; - break; - case 5: - name = "Kotayk'"; - break; - case 6: - name = "Lorri"; - break; - case 7: - name = "Shirak"; - break; - case 8: - name = "Syunik'"; - break; - case 9: - name = "Tavush"; - break; - case 10: - name = "Vayots' Dzor"; - break; - case 11: - name = "Yerevan"; - break; - } - } - if (country_code.equals("AO") == true) { - switch (region_code2) { - case 1: - name = "Benguela"; - break; - case 2: - name = "Bie"; - break; - case 3: - name = "Cabinda"; - break; - case 4: - name = "Cuando Cubango"; - break; - case 5: - name = "Cuanza Norte"; - break; - case 6: - name = "Cuanza Sul"; - break; - case 7: - name = "Cunene"; - break; - case 8: - name = "Huambo"; - break; - case 9: - name = "Huila"; - break; - case 10: - name = "Luanda"; - break; - case 12: - name = "Malanje"; - break; - case 13: - name = "Namibe"; - break; - case 14: - name = "Moxico"; - break; - case 15: - name = "Uige"; - break; - case 16: - name = "Zaire"; - break; - case 17: - name = "Lunda Norte"; - break; - case 18: - name = "Lunda Sul"; - break; - case 19: - name = "Bengo"; - break; - case 20: - name = "Luanda"; - break; - } - } - if (country_code.equals("AR") == true) { - switch (region_code2) { - case 1: - name = "Buenos Aires"; - break; - case 2: - name = "Catamarca"; - break; - case 3: - name = "Chaco"; - break; - case 4: - name = "Chubut"; - break; - case 5: - name = "Cordoba"; - break; - case 6: - name = "Corrientes"; - break; - case 7: - name = "Distrito Federal"; - break; - case 8: - name = "Entre Rios"; - break; - case 9: - name = "Formosa"; - break; - case 10: - name = "Jujuy"; - break; - case 11: - name = "La Pampa"; - break; - case 12: - name = "La Rioja"; - break; - case 13: - name = "Mendoza"; - break; - case 14: - name = "Misiones"; - break; - case 15: - name = "Neuquen"; - break; - case 16: - name = "Rio Negro"; - break; - case 17: - name = "Salta"; - break; - case 18: - name = "San Juan"; - break; - case 19: - name = "San Luis"; - break; - case 20: - name = "Santa Cruz"; - break; - case 21: - name = "Santa Fe"; - break; - case 22: - name = "Santiago del Estero"; - break; - case 23: - name = "Tierra del Fuego"; - break; - case 24: - name = "Tucuman"; - break; - } - } - if (country_code.equals("AT") == true) { - switch (region_code2) { - case 1: - name = "Burgenland"; - break; - case 2: - name = "Karnten"; - break; - case 3: - name = "Niederosterreich"; - break; - case 4: - name = "Oberosterreich"; - break; - case 5: - name = "Salzburg"; - break; - case 6: - name = "Steiermark"; - break; - case 7: - name = "Tirol"; - break; - case 8: - name = "Vorarlberg"; - break; - case 9: - name = "Wien"; - break; - } - } - if (country_code.equals("AU") == true) { - switch (region_code2) { - case 1: - name = "Australian Capital Territory"; - break; - case 2: - name = "New South Wales"; - break; - case 3: - name = "Northern Territory"; - break; - case 4: - name = "Queensland"; - break; - case 5: - name = "South Australia"; - break; - case 6: - name = "Tasmania"; - break; - case 7: - name = "Victoria"; - break; - case 8: - name = "Western Australia"; - break; - } - } - if (country_code.equals("AZ") == true) { - switch (region_code2) { - case 1: - name = "Abseron"; - break; - case 2: - name = "Agcabadi"; - break; - case 3: - name = "Agdam"; - break; - case 4: - name = "Agdas"; - break; - case 5: - name = "Agstafa"; - break; - case 6: - name = "Agsu"; - break; - case 7: - name = "Ali Bayramli"; - break; - case 8: - name = "Astara"; - break; - case 9: - name = "Baki"; - break; - case 10: - name = "Balakan"; - break; - case 11: - name = "Barda"; - break; - case 12: - name = "Beylaqan"; - break; - case 13: - name = "Bilasuvar"; - break; - case 14: - name = "Cabrayil"; - break; - case 15: - name = "Calilabad"; - break; - case 16: - name = "Daskasan"; - break; - case 17: - name = "Davaci"; - break; - case 18: - name = "Fuzuli"; - break; - case 19: - name = "Gadabay"; - break; - case 20: - name = "Ganca"; - break; - case 21: - name = "Goranboy"; - break; - case 22: - name = "Goycay"; - break; - case 23: - name = "Haciqabul"; - break; - case 24: - name = "Imisli"; - break; - case 25: - name = "Ismayilli"; - break; - case 26: - name = "Kalbacar"; - break; - case 27: - name = "Kurdamir"; - break; - case 28: - name = "Lacin"; - break; - case 29: - name = "Lankaran"; - break; - case 30: - name = "Lankaran"; - break; - case 31: - name = "Lerik"; - break; - case 32: - name = "Masalli"; - break; - case 33: - name = "Mingacevir"; - break; - case 34: - name = "Naftalan"; - break; - case 35: - name = "Naxcivan"; - break; - case 36: - name = "Neftcala"; - break; - case 37: - name = "Oguz"; - break; - case 38: - name = "Qabala"; - break; - case 39: - name = "Qax"; - break; - case 40: - name = "Qazax"; - break; - case 41: - name = "Qobustan"; - break; - case 42: - name = "Quba"; - break; - case 43: - name = "Qubadli"; - break; - case 44: - name = "Qusar"; - break; - case 45: - name = "Saatli"; - break; - case 46: - name = "Sabirabad"; - break; - case 47: - name = "Saki"; - break; - case 48: - name = "Saki"; - break; - case 49: - name = "Salyan"; - break; - case 50: - name = "Samaxi"; - break; - case 51: - name = "Samkir"; - break; - case 52: - name = "Samux"; - break; - case 53: - name = "Siyazan"; - break; - case 54: - name = "Sumqayit"; - break; - case 55: - name = "Susa"; - break; - case 56: - name = "Susa"; - break; - case 57: - name = "Tartar"; - break; - case 58: - name = "Tovuz"; - break; - case 59: - name = "Ucar"; - break; - case 60: - name = "Xacmaz"; - break; - case 61: - name = "Xankandi"; - break; - case 62: - name = "Xanlar"; - break; - case 63: - name = "Xizi"; - break; - case 64: - name = "Xocali"; - break; - case 65: - name = "Xocavand"; - break; - case 66: - name = "Yardimli"; - break; - case 67: - name = "Yevlax"; - break; - case 68: - name = "Yevlax"; - break; - case 69: - name = "Zangilan"; - break; - case 70: - name = "Zaqatala"; - break; - case 71: - name = "Zardab"; - break; - } - } - if (country_code.equals("BA") == true) { - switch (region_code2) { - case 1: - name = "Federation of Bosnia and Herzegovina"; - break; - case 2: - name = "Republika Srpska"; - break; - } - } - if (country_code.equals("BB") == true) { - switch (region_code2) { - case 1: - name = "Christ Church"; - break; - case 2: - name = "Saint Andrew"; - break; - case 3: - name = "Saint George"; - break; - case 4: - name = "Saint James"; - break; - case 5: - name = "Saint John"; - break; - case 6: - name = "Saint Joseph"; - break; - case 7: - name = "Saint Lucy"; - break; - case 8: - name = "Saint Michael"; - break; - case 9: - name = "Saint Peter"; - break; - case 10: - name = "Saint Philip"; - break; - case 11: - name = "Saint Thomas"; - break; - } - } - if (country_code.equals("BD") == true) { - switch (region_code2) { - case 1: - name = "Barisal"; - break; - case 4: - name = "Bandarban"; - break; - case 5: - name = "Comilla"; - break; - case 12: - name = "Mymensingh"; - break; - case 13: - name = "Noakhali"; - break; - case 15: - name = "Patuakhali"; - break; - case 22: - name = "Bagerhat"; - break; - case 23: - name = "Bhola"; - break; - case 24: - name = "Bogra"; - break; - case 25: - name = "Barguna"; - break; - case 26: - name = "Brahmanbaria"; - break; - case 27: - name = "Chandpur"; - break; - case 28: - name = "Chapai Nawabganj"; - break; - case 29: - name = "Chattagram"; - break; - case 30: - name = "Chuadanga"; - break; - case 31: - name = "Cox's Bazar"; - break; - case 32: - name = "Dhaka"; - break; - case 33: - name = "Dinajpur"; - break; - case 34: - name = "Faridpur"; - break; - case 35: - name = "Feni"; - break; - case 36: - name = "Gaibandha"; - break; - case 37: - name = "Gazipur"; - break; - case 38: - name = "Gopalganj"; - break; - case 39: - name = "Habiganj"; - break; - case 40: - name = "Jaipurhat"; - break; - case 41: - name = "Jamalpur"; - break; - case 42: - name = "Jessore"; - break; - case 43: - name = "Jhalakati"; - break; - case 44: - name = "Jhenaidah"; - break; - case 45: - name = "Khagrachari"; - break; - case 46: - name = "Khulna"; - break; - case 47: - name = "Kishorganj"; - break; - case 48: - name = "Kurigram"; - break; - case 49: - name = "Kushtia"; - break; - case 50: - name = "Laksmipur"; - break; - case 51: - name = "Lalmonirhat"; - break; - case 52: - name = "Madaripur"; - break; - case 53: - name = "Magura"; - break; - case 54: - name = "Manikganj"; - break; - case 55: - name = "Meherpur"; - break; - case 56: - name = "Moulavibazar"; - break; - case 57: - name = "Munshiganj"; - break; - case 58: - name = "Naogaon"; - break; - case 59: - name = "Narail"; - break; - case 60: - name = "Narayanganj"; - break; - case 61: - name = "Narsingdi"; - break; - case 62: - name = "Nator"; - break; - case 63: - name = "Netrakona"; - break; - case 64: - name = "Nilphamari"; - break; - case 65: - name = "Pabna"; - break; - case 66: - name = "Panchagar"; - break; - case 67: - name = "Parbattya Chattagram"; - break; - case 68: - name = "Pirojpur"; - break; - case 69: - name = "Rajbari"; - break; - case 70: - name = "Rajshahi"; - break; - case 71: - name = "Rangpur"; - break; - case 72: - name = "Satkhira"; - break; - case 73: - name = "Shariyatpur"; - break; - case 74: - name = "Sherpur"; - break; - case 75: - name = "Sirajganj"; - break; - case 76: - name = "Sunamganj"; - break; - case 77: - name = "Sylhet"; - break; - case 78: - name = "Tangail"; - break; - case 79: - name = "Thakurgaon"; - break; - case 81: - name = "Dhaka"; - break; - case 82: - name = "Khulna"; - break; - case 83: - name = "Rajshahi"; - break; - case 84: - name = "Chittagong"; - break; - case 85: - name = "Barisal"; - break; - case 86: - name = "Sylhet"; - break; - } - } - if (country_code.equals("BE") == true) { - switch (region_code2) { - case 1: - name = "Antwerpen"; - break; - case 2: - name = "Brabant"; - break; - case 3: - name = "Hainaut"; - break; - case 4: - name = "Liege"; - break; - case 5: - name = "Limburg"; - break; - case 6: - name = "Luxembourg"; - break; - case 7: - name = "Namur"; - break; - case 8: - name = "Oost-Vlaanderen"; - break; - case 9: - name = "West-Vlaanderen"; - break; - case 10: - name = "Brabant Wallon"; - break; - case 11: - name = "Brussels Hoofdstedelijk Gewest"; - break; - case 12: - name = "Vlaams-Brabant"; - break; - } - } - if (country_code.equals("BF") == true) { - switch (region_code2) { - case 15: - name = "Bam"; - break; - case 19: - name = "Boulkiemde"; - break; - case 20: - name = "Ganzourgou"; - break; - case 21: - name = "Gnagna"; - break; - case 28: - name = "Kouritenga"; - break; - case 33: - name = "Oudalan"; - break; - case 34: - name = "Passore"; - break; - case 36: - name = "Sanguie"; - break; - case 40: - name = "Soum"; - break; - case 42: - name = "Tapoa"; - break; - case 44: - name = "Zoundweogo"; - break; - case 45: - name = "Bale"; - break; - case 46: - name = "Banwa"; - break; - case 47: - name = "Bazega"; - break; - case 48: - name = "Bougouriba"; - break; - case 49: - name = "Boulgou"; - break; - case 50: - name = "Gourma"; - break; - case 51: - name = "Houet"; - break; - case 52: - name = "Ioba"; - break; - case 53: - name = "Kadiogo"; - break; - case 54: - name = "Kenedougou"; - break; - case 55: - name = "Komoe"; - break; - case 56: - name = "Komondjari"; - break; - case 57: - name = "Kompienga"; - break; - case 58: - name = "Kossi"; - break; - case 59: - name = "Koulpelogo"; - break; - case 60: - name = "Kourweogo"; - break; - case 61: - name = "Leraba"; - break; - case 62: - name = "Loroum"; - break; - case 63: - name = "Mouhoun"; - break; - case 64: - name = "Namentenga"; - break; - case 65: - name = "Naouri"; - break; - case 66: - name = "Nayala"; - break; - case 67: - name = "Noumbiel"; - break; - case 68: - name = "Oubritenga"; - break; - case 69: - name = "Poni"; - break; - case 70: - name = "Sanmatenga"; - break; - case 71: - name = "Seno"; - break; - case 72: - name = "Sissili"; - break; - case 73: - name = "Sourou"; - break; - case 74: - name = "Tuy"; - break; - case 75: - name = "Yagha"; - break; - case 76: - name = "Yatenga"; - break; - case 77: - name = "Ziro"; - break; - case 78: - name = "Zondoma"; - break; - } - } - if (country_code.equals("BG") == true) { - switch (region_code2) { - case 33: - name = "Mikhaylovgrad"; - break; - case 38: - name = "Blagoevgrad"; - break; - case 39: - name = "Burgas"; - break; - case 40: - name = "Dobrich"; - break; - case 41: - name = "Gabrovo"; - break; - case 42: - name = "Grad Sofiya"; - break; - case 43: - name = "Khaskovo"; - break; - case 44: - name = "Kurdzhali"; - break; - case 45: - name = "Kyustendil"; - break; - case 46: - name = "Lovech"; - break; - case 47: - name = "Montana"; - break; - case 48: - name = "Pazardzhik"; - break; - case 49: - name = "Pernik"; - break; - case 50: - name = "Pleven"; - break; - case 51: - name = "Plovdiv"; - break; - case 52: - name = "Razgrad"; - break; - case 53: - name = "Ruse"; - break; - case 54: - name = "Shumen"; - break; - case 55: - name = "Silistra"; - break; - case 56: - name = "Sliven"; - break; - case 57: - name = "Smolyan"; - break; - case 58: - name = "Sofiya"; - break; - case 59: - name = "Stara Zagora"; - break; - case 60: - name = "Turgovishte"; - break; - case 61: - name = "Varna"; - break; - case 62: - name = "Veliko Turnovo"; - break; - case 63: - name = "Vidin"; - break; - case 64: - name = "Vratsa"; - break; - case 65: - name = "Yambol"; - break; - } - } - if (country_code.equals("BH") == true) { - switch (region_code2) { - case 1: - name = "Al Hadd"; - break; - case 2: - name = "Al Manamah"; - break; - case 3: - name = "Al Muharraq"; - break; - case 5: - name = "Jidd Hafs"; - break; - case 6: - name = "Sitrah"; - break; - case 7: - name = "Ar Rifa' wa al Mintaqah al Janubiyah"; - break; - case 8: - name = "Al Mintaqah al Gharbiyah"; - break; - case 9: - name = "Mintaqat Juzur Hawar"; - break; - case 10: - name = "Al Mintaqah ash Shamaliyah"; - break; - case 11: - name = "Al Mintaqah al Wusta"; - break; - case 12: - name = "Madinat"; - break; - case 13: - name = "Ar Rifa"; - break; - case 14: - name = "Madinat Hamad"; - break; - case 15: - name = "Al Muharraq"; - break; - case 16: - name = "Al Asimah"; - break; - case 17: - name = "Al Janubiyah"; - break; - case 18: - name = "Ash Shamaliyah"; - break; - case 19: - name = "Al Wusta"; - break; - } - } - if (country_code.equals("BI") == true) { - switch (region_code2) { - case 2: - name = "Bujumbura"; - break; - case 9: - name = "Bubanza"; - break; - case 10: - name = "Bururi"; - break; - case 11: - name = "Cankuzo"; - break; - case 12: - name = "Cibitoke"; - break; - case 13: - name = "Gitega"; - break; - case 14: - name = "Karuzi"; - break; - case 15: - name = "Kayanza"; - break; - case 16: - name = "Kirundo"; - break; - case 17: - name = "Makamba"; - break; - case 18: - name = "Muyinga"; - break; - case 19: - name = "Ngozi"; - break; - case 20: - name = "Rutana"; - break; - case 21: - name = "Ruyigi"; - break; - case 22: - name = "Muramvya"; - break; - case 23: - name = "Mwaro"; - break; - } - } - if (country_code.equals("BJ") == true) { - switch (region_code2) { - case 1: - name = "Atakora"; - break; - case 2: - name = "Atlantique"; - break; - case 3: - name = "Borgou"; - break; - case 4: - name = "Mono"; - break; - case 5: - name = "Oueme"; - break; - case 6: - name = "Zou"; - break; - case 7: - name = "Alibori"; - break; - case 8: - name = "Atakora"; - break; - case 9: - name = "Atlanyique"; - break; - case 10: - name = "Borgou"; - break; - case 11: - name = "Collines"; - break; - case 12: - name = "Kouffo"; - break; - case 13: - name = "Donga"; - break; - case 14: - name = "Littoral"; - break; - case 15: - name = "Mono"; - break; - case 16: - name = "Oueme"; - break; - case 17: - name = "Plateau"; - break; - case 18: - name = "Zou"; - break; - } - } - if (country_code.equals("BM") == true) { - switch (region_code2) { - case 1: - name = "Devonshire"; - break; - case 2: - name = "Hamilton"; - break; - case 3: - name = "Hamilton"; - break; - case 4: - name = "Paget"; - break; - case 5: - name = "Pembroke"; - break; - case 6: - name = "Saint George"; - break; - case 7: - name = "Saint George's"; - break; - case 8: - name = "Sandys"; - break; - case 9: - name = "Smiths"; - break; - case 10: - name = "Southampton"; - break; - case 11: - name = "Warwick"; - break; - } - } - if (country_code.equals("BN") == true) { - switch (region_code2) { - case 7: - name = "Alibori"; - break; - case 8: - name = "Belait"; - break; - case 9: - name = "Brunei and Muara"; - break; - case 10: - name = "Temburong"; - break; - case 11: - name = "Collines"; - break; - case 12: - name = "Kouffo"; - break; - case 13: - name = "Donga"; - break; - case 14: - name = "Littoral"; - break; - case 15: - name = "Tutong"; - break; - case 16: - name = "Oueme"; - break; - case 17: - name = "Plateau"; - break; - case 18: - name = "Zou"; - break; - } - } - if (country_code.equals("BO") == true) { - switch (region_code2) { - case 1: - name = "Chuquisaca"; - break; - case 2: - name = "Cochabamba"; - break; - case 3: - name = "El Beni"; - break; - case 4: - name = "La Paz"; - break; - case 5: - name = "Oruro"; - break; - case 6: - name = "Pando"; - break; - case 7: - name = "Potosi"; - break; - case 8: - name = "Santa Cruz"; - break; - case 9: - name = "Tarija"; - break; - } - } - if (country_code.equals("BR") == true) { - switch (region_code2) { - case 1: - name = "Acre"; - break; - case 2: - name = "Alagoas"; - break; - case 3: - name = "Amapa"; - break; - case 4: - name = "Amazonas"; - break; - case 5: - name = "Bahia"; - break; - case 6: - name = "Ceara"; - break; - case 7: - name = "Distrito Federal"; - break; - case 8: - name = "Espirito Santo"; - break; - case 11: - name = "Mato Grosso do Sul"; - break; - case 13: - name = "Maranhao"; - break; - case 14: - name = "Mato Grosso"; - break; - case 15: - name = "Minas Gerais"; - break; - case 16: - name = "Para"; - break; - case 17: - name = "Paraiba"; - break; - case 18: - name = "Parana"; - break; - case 20: - name = "Piaui"; - break; - case 21: - name = "Rio de Janeiro"; - break; - case 22: - name = "Rio Grande do Norte"; - break; - case 23: - name = "Rio Grande do Sul"; - break; - case 24: - name = "Rondonia"; - break; - case 25: - name = "Roraima"; - break; - case 26: - name = "Santa Catarina"; - break; - case 27: - name = "Sao Paulo"; - break; - case 28: - name = "Sergipe"; - break; - case 29: - name = "Goias"; - break; - case 30: - name = "Pernambuco"; - break; - case 31: - name = "Tocantins"; - break; - } - } - if (country_code.equals("BS") == true) { - switch (region_code2) { - case 5: - name = "Bimini"; - break; - case 6: - name = "Cat Island"; - break; - case 10: - name = "Exuma"; - break; - case 13: - name = "Inagua"; - break; - case 15: - name = "Long Island"; - break; - case 16: - name = "Mayaguana"; - break; - case 18: - name = "Ragged Island"; - break; - case 22: - name = "Harbour Island"; - break; - case 23: - name = "New Providence"; - break; - case 24: - name = "Acklins and Crooked Islands"; - break; - case 25: - name = "Freeport"; - break; - case 26: - name = "Fresh Creek"; - break; - case 27: - name = "Governor's Harbour"; - break; - case 28: - name = "Green Turtle Cay"; - break; - case 29: - name = "High Rock"; - break; - case 30: - name = "Kemps Bay"; - break; - case 31: - name = "Marsh Harbour"; - break; - case 32: - name = "Nichollstown and Berry Islands"; - break; - case 33: - name = "Rock Sound"; - break; - case 34: - name = "Sandy Point"; - break; - case 35: - name = "San Salvador and Rum Cay"; - break; - } - } - if (country_code.equals("BT") == true) { - switch (region_code2) { - case 5: - name = "Bumthang"; - break; - case 6: - name = "Chhukha"; - break; - case 7: - name = "Chirang"; - break; - case 8: - name = "Daga"; - break; - case 9: - name = "Geylegphug"; - break; - case 10: - name = "Ha"; - break; - case 11: - name = "Lhuntshi"; - break; - case 12: - name = "Mongar"; - break; - case 13: - name = "Paro"; - break; - case 14: - name = "Pemagatsel"; - break; - case 15: - name = "Punakha"; - break; - case 16: - name = "Samchi"; - break; - case 17: - name = "Samdrup"; - break; - case 18: - name = "Shemgang"; - break; - case 19: - name = "Tashigang"; - break; - case 20: - name = "Thimphu"; - break; - case 21: - name = "Tongsa"; - break; - case 22: - name = "Wangdi Phodrang"; - break; - } - } - if (country_code.equals("BW") == true) { - switch (region_code2) { - case 1: - name = "Central"; - break; - case 3: - name = "Ghanzi"; - break; - case 4: - name = "Kgalagadi"; - break; - case 5: - name = "Kgatleng"; - break; - case 6: - name = "Kweneng"; - break; - case 8: - name = "North-East"; - break; - case 9: - name = "South-East"; - break; - case 10: - name = "Southern"; - break; - case 11: - name = "North-West"; - break; - } - } - if (country_code.equals("BY") == true) { - switch (region_code2) { - case 1: - name = "Brestskaya Voblasts'"; - break; - case 2: - name = "Homyel'skaya Voblasts'"; - break; - case 3: - name = "Hrodzyenskaya Voblasts'"; - break; - case 4: - name = "Minsk"; - break; - case 5: - name = "Minskaya Voblasts'"; - break; - case 6: - name = "Mahilyowskaya Voblasts'"; - break; - case 7: - name = "Vitsyebskaya Voblasts'"; - break; - } - } - if (country_code.equals("BZ") == true) { - switch (region_code2) { - case 1: - name = "Belize"; - break; - case 2: - name = "Cayo"; - break; - case 3: - name = "Corozal"; - break; - case 4: - name = "Orange Walk"; - break; - case 5: - name = "Stann Creek"; - break; - case 6: - name = "Toledo"; - break; - } - } - if (country_code.equals("CD") == true) { - switch (region_code2) { - case 1: - name = "Bandundu"; - break; - case 2: - name = "Equateur"; - break; - case 4: - name = "Kasai-Oriental"; - break; - case 5: - name = "Katanga"; - break; - case 6: - name = "Kinshasa"; - break; - case 7: - name = "Kivu"; - break; - case 8: - name = "Bas-Congo"; - break; - case 9: - name = "Orientale"; - break; - case 10: - name = "Maniema"; - break; - case 11: - name = "Nord-Kivu"; - break; - case 12: - name = "Sud-Kivu"; - break; - case 13: - name = "Cuvette"; - break; - } - } - if (country_code.equals("CF") == true) { - switch (region_code2) { - case 1: - name = "Bamingui-Bangoran"; - break; - case 2: - name = "Basse-Kotto"; - break; - case 3: - name = "Haute-Kotto"; - break; - case 4: - name = "Mambere-Kadei"; - break; - case 5: - name = "Haut-Mbomou"; - break; - case 6: - name = "Kemo"; - break; - case 7: - name = "Lobaye"; - break; - case 8: - name = "Mbomou"; - break; - case 9: - name = "Nana-Mambere"; - break; - case 11: - name = "Ouaka"; - break; - case 12: - name = "Ouham"; - break; - case 13: - name = "Ouham-Pende"; - break; - case 14: - name = "Cuvette-Ouest"; - break; - case 15: - name = "Nana-Grebizi"; - break; - case 16: - name = "Sangha-Mbaere"; - break; - case 17: - name = "Ombella-Mpoko"; - break; - case 18: - name = "Bangui"; - break; - } - } - if (country_code.equals("CG") == true) { - switch (region_code2) { - case 1: - name = "Bouenza"; - break; - case 3: - name = "Cuvette"; - break; - case 4: - name = "Kouilou"; - break; - case 5: - name = "Lekoumou"; - break; - case 6: - name = "Likouala"; - break; - case 7: - name = "Niari"; - break; - case 8: - name = "Plateaux"; - break; - case 10: - name = "Sangha"; - break; - case 11: - name = "Pool"; - break; - case 12: - name = "Brazzaville"; - break; - } - } - if (country_code.equals("CH") == true) { - switch (region_code2) { - case 1: - name = "Aargau"; - break; - case 2: - name = "Ausser-Rhoden"; - break; - case 3: - name = "Basel-Landschaft"; - break; - case 4: - name = "Basel-Stadt"; - break; - case 5: - name = "Bern"; - break; - case 6: - name = "Fribourg"; - break; - case 7: - name = "Geneve"; - break; - case 8: - name = "Glarus"; - break; - case 9: - name = "Graubunden"; - break; - case 10: - name = "Inner-Rhoden"; - break; - case 11: - name = "Luzern"; - break; - case 12: - name = "Neuchatel"; - break; - case 13: - name = "Nidwalden"; - break; - case 14: - name = "Obwalden"; - break; - case 15: - name = "Sankt Gallen"; - break; - case 16: - name = "Schaffhausen"; - break; - case 17: - name = "Schwyz"; - break; - case 18: - name = "Solothurn"; - break; - case 19: - name = "Thurgau"; - break; - case 20: - name = "Ticino"; - break; - case 21: - name = "Uri"; - break; - case 22: - name = "Valais"; - break; - case 23: - name = "Vaud"; - break; - case 24: - name = "Zug"; - break; - case 25: - name = "Zurich"; - break; - case 26: - name = "Jura"; - break; - } - } - if (country_code.equals("CI") == true) { - switch (region_code2) { - case 5: - name = "Atacama"; - break; - case 6: - name = "Biobio"; - break; - case 51: - name = "Sassandra"; - break; - case 61: - name = "Abidjan"; - break; - case 74: - name = "Agneby"; - break; - case 75: - name = "Bafing"; - break; - case 76: - name = "Bas-Sassandra"; - break; - case 77: - name = "Denguele"; - break; - case 78: - name = "Dix-Huit Montagnes"; - break; - case 79: - name = "Fromager"; - break; - case 80: - name = "Haut-Sassandra"; - break; - case 81: - name = "Lacs"; - break; - case 82: - name = "Lagunes"; - break; - case 83: - name = "Marahoue"; - break; - case 84: - name = "Moyen-Cavally"; - break; - case 85: - name = "Moyen-Comoe"; - break; - case 86: - name = "N'zi-Comoe"; - break; - case 87: - name = "Savanes"; - break; - case 88: - name = "Sud-Bandama"; - break; - case 89: - name = "Sud-Comoe"; - break; - case 90: - name = "Vallee du Bandama"; - break; - case 91: - name = "Worodougou"; - break; - case 92: - name = "Zanzan"; - break; - } - } - if (country_code.equals("CL") == true) { - switch (region_code2) { - case 1: - name = "Valparaiso"; - break; - case 2: - name = "Aisen del General Carlos Ibanez del Campo"; - break; - case 3: - name = "Antofagasta"; - break; - case 4: - name = "Araucania"; - break; - case 5: - name = "Atacama"; - break; - case 6: - name = "Bio-Bio"; - break; - case 7: - name = "Coquimbo"; - break; - case 8: - name = "Libertador General Bernardo O'Higgins"; - break; - case 9: - name = "Los Lagos"; - break; - case 10: - name = "Magallanes y de la Antartica Chilena"; - break; - case 11: - name = "Maule"; - break; - case 12: - name = "Region Metropolitana"; - break; - case 13: - name = "Tarapaca"; - break; - case 14: - name = "Los Lagos"; - break; - case 15: - name = "Tarapaca"; - break; - case 16: - name = "Arica y Parinacota"; - break; - case 17: - name = "Los Rios"; - break; - } - } - if (country_code.equals("CM") == true) { - switch (region_code2) { - case 4: - name = "Est"; - break; - case 5: - name = "Littoral"; - break; - case 7: - name = "Nord-Ouest"; - break; - case 8: - name = "Ouest"; - break; - case 9: - name = "Sud-Ouest"; - break; - case 10: - name = "Adamaoua"; - break; - case 11: - name = "Centre"; - break; - case 12: - name = "Extreme-Nord"; - break; - case 13: - name = "Nord"; - break; - case 14: - name = "Sud"; - break; - } - } - if (country_code.equals("CN") == true) { - switch (region_code2) { - case 1: - name = "Anhui"; - break; - case 2: - name = "Zhejiang"; - break; - case 3: - name = "Jiangxi"; - break; - case 4: - name = "Jiangsu"; - break; - case 5: - name = "Jilin"; - break; - case 6: - name = "Qinghai"; - break; - case 7: - name = "Fujian"; - break; - case 8: - name = "Heilongjiang"; - break; - case 9: - name = "Henan"; - break; - case 10: - name = "Hebei"; - break; - case 11: - name = "Hunan"; - break; - case 12: - name = "Hubei"; - break; - case 13: - name = "Xinjiang"; - break; - case 14: - name = "Xizang"; - break; - case 15: - name = "Gansu"; - break; - case 16: - name = "Guangxi"; - break; - case 18: - name = "Guizhou"; - break; - case 19: - name = "Liaoning"; - break; - case 20: - name = "Nei Mongol"; - break; - case 21: - name = "Ningxia"; - break; - case 22: - name = "Beijing"; - break; - case 23: - name = "Shanghai"; - break; - case 24: - name = "Shanxi"; - break; - case 25: - name = "Shandong"; - break; - case 26: - name = "Shaanxi"; - break; - case 28: - name = "Tianjin"; - break; - case 29: - name = "Yunnan"; - break; - case 30: - name = "Guangdong"; - break; - case 31: - name = "Hainan"; - break; - case 32: - name = "Sichuan"; - break; - case 33: - name = "Chongqing"; - break; - } - } - if (country_code.equals("CO") == true) { - switch (region_code2) { - case 1: - name = "Amazonas"; - break; - case 2: - name = "Antioquia"; - break; - case 3: - name = "Arauca"; - break; - case 4: - name = "Atlantico"; - break; - case 5: - name = "Bolivar Department"; - break; - case 6: - name = "Boyaca Department"; - break; - case 7: - name = "Caldas Department"; - break; - case 8: - name = "Caqueta"; - break; - case 9: - name = "Cauca"; - break; - case 10: - name = "Cesar"; - break; - case 11: - name = "Choco"; - break; - case 12: - name = "Cordoba"; - break; - case 14: - name = "Guaviare"; - break; - case 15: - name = "Guainia"; - break; - case 16: - name = "Huila"; - break; - case 17: - name = "La Guajira"; - break; - case 18: - name = "Magdalena Department"; - break; - case 19: - name = "Meta"; - break; - case 20: - name = "Narino"; - break; - case 21: - name = "Norte de Santander"; - break; - case 22: - name = "Putumayo"; - break; - case 23: - name = "Quindio"; - break; - case 24: - name = "Risaralda"; - break; - case 25: - name = "San Andres y Providencia"; - break; - case 26: - name = "Santander"; - break; - case 27: - name = "Sucre"; - break; - case 28: - name = "Tolima"; - break; - case 29: - name = "Valle del Cauca"; - break; - case 30: - name = "Vaupes"; - break; - case 31: - name = "Vichada"; - break; - case 32: - name = "Casanare"; - break; - case 33: - name = "Cundinamarca"; - break; - case 34: - name = "Distrito Especial"; - break; - case 35: - name = "Bolivar"; - break; - case 36: - name = "Boyaca"; - break; - case 37: - name = "Caldas"; - break; - case 38: - name = "Magdalena"; - break; - } - } - if (country_code.equals("CR") == true) { - switch (region_code2) { - case 1: - name = "Alajuela"; - break; - case 2: - name = "Cartago"; - break; - case 3: - name = "Guanacaste"; - break; - case 4: - name = "Heredia"; - break; - case 6: - name = "Limon"; - break; - case 7: - name = "Puntarenas"; - break; - case 8: - name = "San Jose"; - break; - } - } - if (country_code.equals("CU") == true) { - switch (region_code2) { - case 1: - name = "Pinar del Rio"; - break; - case 2: - name = "Ciudad de la Habana"; - break; - case 3: - name = "Matanzas"; - break; - case 4: - name = "Isla de la Juventud"; - break; - case 5: - name = "Camaguey"; - break; - case 7: - name = "Ciego de Avila"; - break; - case 8: - name = "Cienfuegos"; - break; - case 9: - name = "Granma"; - break; - case 10: - name = "Guantanamo"; - break; - case 11: - name = "La Habana"; - break; - case 12: - name = "Holguin"; - break; - case 13: - name = "Las Tunas"; - break; - case 14: - name = "Sancti Spiritus"; - break; - case 15: - name = "Santiago de Cuba"; - break; - case 16: - name = "Villa Clara"; - break; - } - } - if (country_code.equals("CV") == true) { - switch (region_code2) { - case 1: - name = "Boa Vista"; - break; - case 2: - name = "Brava"; - break; - case 4: - name = "Maio"; - break; - case 5: - name = "Paul"; - break; - case 7: - name = "Ribeira Grande"; - break; - case 8: - name = "Sal"; - break; - case 10: - name = "Sao Nicolau"; - break; - case 11: - name = "Sao Vicente"; - break; - case 13: - name = "Mosteiros"; - break; - case 14: - name = "Praia"; - break; - case 15: - name = "Santa Catarina"; - break; - case 16: - name = "Santa Cruz"; - break; - case 17: - name = "Sao Domingos"; - break; - case 18: - name = "Sao Filipe"; - break; - case 19: - name = "Sao Miguel"; - break; - case 20: - name = "Tarrafal"; - break; - } - } - if (country_code.equals("CY") == true) { - switch (region_code2) { - case 1: - name = "Famagusta"; - break; - case 2: - name = "Kyrenia"; - break; - case 3: - name = "Larnaca"; - break; - case 4: - name = "Nicosia"; - break; - case 5: - name = "Limassol"; - break; - case 6: - name = "Paphos"; - break; - } - } - if (country_code.equals("CZ") == true) { - switch (region_code2) { - case 3: - name = "Blansko"; - break; - case 4: - name = "Breclav"; - break; - case 20: - name = "Hradec Kralove"; - break; - case 21: - name = "Jablonec nad Nisou"; - break; - case 23: - name = "Jicin"; - break; - case 24: - name = "Jihlava"; - break; - case 30: - name = "Kolin"; - break; - case 33: - name = "Liberec"; - break; - case 36: - name = "Melnik"; - break; - case 37: - name = "Mlada Boleslav"; - break; - case 39: - name = "Nachod"; - break; - case 41: - name = "Nymburk"; - break; - case 45: - name = "Pardubice"; - break; - case 52: - name = "Hlavni mesto Praha"; - break; - case 61: - name = "Semily"; - break; - case 70: - name = "Trutnov"; - break; - case 78: - name = "Jihomoravsky kraj"; - break; - case 79: - name = "Jihocesky kraj"; - break; - case 80: - name = "Vysocina"; - break; - case 81: - name = "Karlovarsky kraj"; - break; - case 82: - name = "Kralovehradecky kraj"; - break; - case 83: - name = "Liberecky kraj"; - break; - case 84: - name = "Olomoucky kraj"; - break; - case 85: - name = "Moravskoslezsky kraj"; - break; - case 86: - name = "Pardubicky kraj"; - break; - case 87: - name = "Plzensky kraj"; - break; - case 88: - name = "Stredocesky kraj"; - break; - case 89: - name = "Ustecky kraj"; - break; - case 90: - name = "Zlinsky kraj"; - break; - } - } - if (country_code.equals("DE") == true) { - switch (region_code2) { - case 1: - name = "Baden-Wurttemberg"; - break; - case 2: - name = "Bayern"; - break; - case 3: - name = "Bremen"; - break; - case 4: - name = "Hamburg"; - break; - case 5: - name = "Hessen"; - break; - case 6: - name = "Niedersachsen"; - break; - case 7: - name = "Nordrhein-Westfalen"; - break; - case 8: - name = "Rheinland-Pfalz"; - break; - case 9: - name = "Saarland"; - break; - case 10: - name = "Schleswig-Holstein"; - break; - case 11: - name = "Brandenburg"; - break; - case 12: - name = "Mecklenburg-Vorpommern"; - break; - case 13: - name = "Sachsen"; - break; - case 14: - name = "Sachsen-Anhalt"; - break; - case 15: - name = "Thuringen"; - break; - case 16: - name = "Berlin"; - break; - } - } - if (country_code.equals("DJ") == true) { - switch (region_code2) { - case 1: - name = "Ali Sabieh"; - break; - case 4: - name = "Obock"; - break; - case 5: - name = "Tadjoura"; - break; - case 6: - name = "Dikhil"; - break; - case 7: - name = "Djibouti"; - break; - case 8: - name = "Arta"; - break; - } - } - if (country_code.equals("DK") == true) { - switch (region_code2) { - case 17: - name = "Hovedstaden"; - break; - case 18: - name = "Midtjylland"; - break; - case 19: - name = "Nordjylland"; - break; - case 20: - name = "Sjelland"; - break; - case 21: - name = "Syddanmark"; - break; - } - } - if (country_code.equals("DM") == true) { - switch (region_code2) { - case 2: - name = "Saint Andrew"; - break; - case 3: - name = "Saint David"; - break; - case 4: - name = "Saint George"; - break; - case 5: - name = "Saint John"; - break; - case 6: - name = "Saint Joseph"; - break; - case 7: - name = "Saint Luke"; - break; - case 8: - name = "Saint Mark"; - break; - case 9: - name = "Saint Patrick"; - break; - case 10: - name = "Saint Paul"; - break; - case 11: - name = "Saint Peter"; - break; - } - } - if (country_code.equals("DO") == true) { - switch (region_code2) { - case 1: - name = "Azua"; - break; - case 2: - name = "Baoruco"; - break; - case 3: - name = "Barahona"; - break; - case 4: - name = "Dajabon"; - break; - case 5: - name = "Distrito Nacional"; - break; - case 6: - name = "Duarte"; - break; - case 8: - name = "Espaillat"; - break; - case 9: - name = "Independencia"; - break; - case 10: - name = "La Altagracia"; - break; - case 11: - name = "Elias Pina"; - break; - case 12: - name = "La Romana"; - break; - case 14: - name = "Maria Trinidad Sanchez"; - break; - case 15: - name = "Monte Cristi"; - break; - case 16: - name = "Pedernales"; - break; - case 17: - name = "Peravia"; - break; - case 18: - name = "Puerto Plata"; - break; - case 19: - name = "Salcedo"; - break; - case 20: - name = "Samana"; - break; - case 21: - name = "Sanchez Ramirez"; - break; - case 23: - name = "San Juan"; - break; - case 24: - name = "San Pedro De Macoris"; - break; - case 25: - name = "Santiago"; - break; - case 26: - name = "Santiago Rodriguez"; - break; - case 27: - name = "Valverde"; - break; - case 28: - name = "El Seibo"; - break; - case 29: - name = "Hato Mayor"; - break; - case 30: - name = "La Vega"; - break; - case 31: - name = "Monsenor Nouel"; - break; - case 32: - name = "Monte Plata"; - break; - case 33: - name = "San Cristobal"; - break; - case 34: - name = "Distrito Nacional"; - break; - case 35: - name = "Peravia"; - break; - case 36: - name = "San Jose de Ocoa"; - break; - case 37: - name = "Santo Domingo"; - break; - } - } - if (country_code.equals("DZ") == true) { - switch (region_code2) { - case 1: - name = "Alger"; - break; - case 3: - name = "Batna"; - break; - case 4: - name = "Constantine"; - break; - case 6: - name = "Medea"; - break; - case 7: - name = "Mostaganem"; - break; - case 9: - name = "Oran"; - break; - case 10: - name = "Saida"; - break; - case 12: - name = "Setif"; - break; - case 13: - name = "Tiaret"; - break; - case 14: - name = "Tizi Ouzou"; - break; - case 15: - name = "Tlemcen"; - break; - case 18: - name = "Bejaia"; - break; - case 19: - name = "Biskra"; - break; - case 20: - name = "Blida"; - break; - case 21: - name = "Bouira"; - break; - case 22: - name = "Djelfa"; - break; - case 23: - name = "Guelma"; - break; - case 24: - name = "Jijel"; - break; - case 25: - name = "Laghouat"; - break; - case 26: - name = "Mascara"; - break; - case 27: - name = "M'sila"; - break; - case 29: - name = "Oum el Bouaghi"; - break; - case 30: - name = "Sidi Bel Abbes"; - break; - case 31: - name = "Skikda"; - break; - case 33: - name = "Tebessa"; - break; - case 34: - name = "Adrar"; - break; - case 35: - name = "Ain Defla"; - break; - case 36: - name = "Ain Temouchent"; - break; - case 37: - name = "Annaba"; - break; - case 38: - name = "Bechar"; - break; - case 39: - name = "Bordj Bou Arreridj"; - break; - case 40: - name = "Boumerdes"; - break; - case 41: - name = "Chlef"; - break; - case 42: - name = "El Bayadh"; - break; - case 43: - name = "El Oued"; - break; - case 44: - name = "El Tarf"; - break; - case 45: - name = "Ghardaia"; - break; - case 46: - name = "Illizi"; - break; - case 47: - name = "Khenchela"; - break; - case 48: - name = "Mila"; - break; - case 49: - name = "Naama"; - break; - case 50: - name = "Ouargla"; - break; - case 51: - name = "Relizane"; - break; - case 52: - name = "Souk Ahras"; - break; - case 53: - name = "Tamanghasset"; - break; - case 54: - name = "Tindouf"; - break; - case 55: - name = "Tipaza"; - break; - case 56: - name = "Tissemsilt"; - break; - } - } - if (country_code.equals("EC") == true) { - switch (region_code2) { - case 1: - name = "Galapagos"; - break; - case 2: - name = "Azuay"; - break; - case 3: - name = "Bolivar"; - break; - case 4: - name = "Canar"; - break; - case 5: - name = "Carchi"; - break; - case 6: - name = "Chimborazo"; - break; - case 7: - name = "Cotopaxi"; - break; - case 8: - name = "El Oro"; - break; - case 9: - name = "Esmeraldas"; - break; - case 10: - name = "Guayas"; - break; - case 11: - name = "Imbabura"; - break; - case 12: - name = "Loja"; - break; - case 13: - name = "Los Rios"; - break; - case 14: - name = "Manabi"; - break; - case 15: - name = "Morona-Santiago"; - break; - case 17: - name = "Pastaza"; - break; - case 18: - name = "Pichincha"; - break; - case 19: - name = "Tungurahua"; - break; - case 20: - name = "Zamora-Chinchipe"; - break; - case 22: - name = "Sucumbios"; - break; - case 23: - name = "Napo"; - break; - case 24: - name = "Orellana"; - break; - } - } - if (country_code.equals("EE") == true) { - switch (region_code2) { - case 1: - name = "Harjumaa"; - break; - case 2: - name = "Hiiumaa"; - break; - case 3: - name = "Ida-Virumaa"; - break; - case 4: - name = "Jarvamaa"; - break; - case 5: - name = "Jogevamaa"; - break; - case 6: - name = "Kohtla-Jarve"; - break; - case 7: - name = "Laanemaa"; - break; - case 8: - name = "Laane-Virumaa"; - break; - case 9: - name = "Narva"; - break; - case 10: - name = "Parnu"; - break; - case 11: - name = "Parnumaa"; - break; - case 12: - name = "Polvamaa"; - break; - case 13: - name = "Raplamaa"; - break; - case 14: - name = "Saaremaa"; - break; - case 15: - name = "Sillamae"; - break; - case 16: - name = "Tallinn"; - break; - case 17: - name = "Tartu"; - break; - case 18: - name = "Tartumaa"; - break; - case 19: - name = "Valgamaa"; - break; - case 20: - name = "Viljandimaa"; - break; - case 21: - name = "Vorumaa"; - break; - } - } - if (country_code.equals("EG") == true) { - switch (region_code2) { - case 1: - name = "Ad Daqahliyah"; - break; - case 2: - name = "Al Bahr al Ahmar"; - break; - case 3: - name = "Al Buhayrah"; - break; - case 4: - name = "Al Fayyum"; - break; - case 5: - name = "Al Gharbiyah"; - break; - case 6: - name = "Al Iskandariyah"; - break; - case 7: - name = "Al Isma'iliyah"; - break; - case 8: - name = "Al Jizah"; - break; - case 9: - name = "Al Minufiyah"; - break; - case 10: - name = "Al Minya"; - break; - case 11: - name = "Al Qahirah"; - break; - case 12: - name = "Al Qalyubiyah"; - break; - case 13: - name = "Al Wadi al Jadid"; - break; - case 14: - name = "Ash Sharqiyah"; - break; - case 15: - name = "As Suways"; - break; - case 16: - name = "Aswan"; - break; - case 17: - name = "Asyut"; - break; - case 18: - name = "Bani Suwayf"; - break; - case 19: - name = "Bur Sa'id"; - break; - case 20: - name = "Dumyat"; - break; - case 21: - name = "Kafr ash Shaykh"; - break; - case 22: - name = "Matruh"; - break; - case 23: - name = "Qina"; - break; - case 24: - name = "Suhaj"; - break; - case 26: - name = "Janub Sina'"; - break; - case 27: - name = "Shamal Sina'"; - break; - } - } - if (country_code.equals("ER") == true) { - switch (region_code2) { - case 1: - name = "Anseba"; - break; - case 2: - name = "Debub"; - break; - case 3: - name = "Debubawi K'eyih Bahri"; - break; - case 4: - name = "Gash Barka"; - break; - case 5: - name = "Ma'akel"; - break; - case 6: - name = "Semenawi K'eyih Bahri"; - break; - } - } - if (country_code.equals("ES") == true) { - switch (region_code2) { - case 7: - name = "Islas Baleares"; - break; - case 27: - name = "La Rioja"; - break; - case 29: - name = "Madrid"; - break; - case 31: - name = "Murcia"; - break; - case 32: - name = "Navarra"; - break; - case 34: - name = "Asturias"; - break; - case 39: - name = "Cantabria"; - break; - case 51: - name = "Andalucia"; - break; - case 52: - name = "Aragon"; - break; - case 53: - name = "Canarias"; - break; - case 54: - name = "Castilla-La Mancha"; - break; - case 55: - name = "Castilla y Leon"; - break; - case 56: - name = "Catalonia"; - break; - case 57: - name = "Extremadura"; - break; - case 58: - name = "Galicia"; - break; - case 59: - name = "Pais Vasco"; - break; - case 60: - name = "Comunidad Valenciana"; - break; - } - } - if (country_code.equals("ET") == true) { - switch (region_code2) { - case 2: - name = "Amhara"; - break; - case 7: - name = "Somali"; - break; - case 8: - name = "Gambella"; - break; - case 10: - name = "Addis Abeba"; - break; - case 11: - name = "Southern"; - break; - case 12: - name = "Tigray"; - break; - case 13: - name = "Benishangul"; - break; - case 14: - name = "Afar"; - break; - case 44: - name = "Adis Abeba"; - break; - case 45: - name = "Afar"; - break; - case 46: - name = "Amara"; - break; - case 47: - name = "Binshangul Gumuz"; - break; - case 48: - name = "Dire Dawa"; - break; - case 49: - name = "Gambela Hizboch"; - break; - case 50: - name = "Hareri Hizb"; - break; - case 51: - name = "Oromiya"; - break; - case 52: - name = "Sumale"; - break; - case 53: - name = "Tigray"; - break; - case 54: - name = "YeDebub Biheroch Bihereseboch na Hizboch"; - break; - } - } - if (country_code.equals("FI") == true) { - switch (region_code2) { - case 1: - name = "Aland"; - break; - case 6: - name = "Lapland"; - break; - case 8: - name = "Oulu"; - break; - case 13: - name = "Southern Finland"; - break; - case 14: - name = "Eastern Finland"; - break; - case 15: - name = "Western Finland"; - break; - } - } - if (country_code.equals("FJ") == true) { - switch (region_code2) { - case 1: - name = "Central"; - break; - case 2: - name = "Eastern"; - break; - case 3: - name = "Northern"; - break; - case 4: - name = "Rotuma"; - break; - case 5: - name = "Western"; - break; - } - } - if (country_code.equals("FM") == true) { - switch (region_code2) { - case 1: - name = "Kosrae"; - break; - case 2: - name = "Pohnpei"; - break; - case 3: - name = "Chuuk"; - break; - case 4: - name = "Yap"; - break; - } - } - if (country_code.equals("FR") == true) { - switch (region_code2) { - case 97: - name = "Aquitaine"; - break; - case 98: - name = "Auvergne"; - break; - case 99: - name = "Basse-Normandie"; - break; - case 832: - name = "Bourgogne"; - break; - case 833: - name = "Bretagne"; - break; - case 834: - name = "Centre"; - break; - case 835: - name = "Champagne-Ardenne"; - break; - case 836: - name = "Corse"; - break; - case 837: - name = "Franche-Comte"; - break; - case 838: - name = "Haute-Normandie"; - break; - case 839: - name = "Ile-de-France"; - break; - case 840: - name = "Languedoc-Roussillon"; - break; - case 875: - name = "Limousin"; - break; - case 876: - name = "Lorraine"; - break; - case 877: - name = "Midi-Pyrenees"; - break; - case 878: - name = "Nord-Pas-de-Calais"; - break; - case 879: - name = "Pays de la Loire"; - break; - case 880: - name = "Picardie"; - break; - case 881: - name = "Poitou-Charentes"; - break; - case 882: - name = "Provence-Alpes-Cote d'Azur"; - break; - case 883: - name = "Rhone-Alpes"; - break; - case 918: - name = "Alsace"; - break; - } - } - if (country_code.equals("GA") == true) { - switch (region_code2) { - case 1: - name = "Estuaire"; - break; - case 2: - name = "Haut-Ogooue"; - break; - case 3: - name = "Moyen-Ogooue"; - break; - case 4: - name = "Ngounie"; - break; - case 5: - name = "Nyanga"; - break; - case 6: - name = "Ogooue-Ivindo"; - break; - case 7: - name = "Ogooue-Lolo"; - break; - case 8: - name = "Ogooue-Maritime"; - break; - case 9: - name = "Woleu-Ntem"; - break; - } - } - if (country_code.equals("GB") == true) { - switch (region_code2) { - case 1: - name = "Avon"; - break; - case 3: - name = "Berkshire"; - break; - case 7: - name = "Cleveland"; - break; - case 17: - name = "Greater London"; - break; - case 18: - name = "Greater Manchester"; - break; - case 20: - name = "Hereford and Worcester"; - break; - case 22: - name = "Humberside"; - break; - case 28: - name = "Merseyside"; - break; - case 37: - name = "South Yorkshire"; - break; - case 41: - name = "Tyne and Wear"; - break; - case 43: - name = "West Midlands"; - break; - case 45: - name = "West Yorkshire"; - break; - case 79: - name = "Central"; - break; - case 82: - name = "Grampian"; - break; - case 84: - name = "Lothian"; - break; - case 87: - name = "Strathclyde"; - break; - case 88: - name = "Tayside"; - break; - case 90: - name = "Clwyd"; - break; - case 91: - name = "Dyfed"; - break; - case 92: - name = "Gwent"; - break; - case 94: - name = "Mid Glamorgan"; - break; - case 96: - name = "South Glamorgan"; - break; - case 97: - name = "West Glamorgan"; - break; - case 832: - name = "Barking and Dagenham"; - break; - case 833: - name = "Barnet"; - break; - case 834: - name = "Barnsley"; - break; - case 835: - name = "Bath and North East Somerset"; - break; - case 836: - name = "Bedfordshire"; - break; - case 837: - name = "Bexley"; - break; - case 838: - name = "Birmingham"; - break; - case 839: - name = "Blackburn with Darwen"; - break; - case 840: - name = "Blackpool"; - break; - case 875: - name = "Bolton"; - break; - case 876: - name = "Bournemouth"; - break; - case 877: - name = "Bracknell Forest"; - break; - case 878: - name = "Bradford"; - break; - case 879: - name = "Brent"; - break; - case 880: - name = "Brighton and Hove"; - break; - case 881: - name = "Bristol"; - break; - case 882: - name = "Bromley"; - break; - case 883: - name = "Buckinghamshire"; - break; - case 918: - name = "Bury"; - break; - case 919: - name = "Calderdale"; - break; - case 920: - name = "Cambridgeshire"; - break; - case 921: - name = "Camden"; - break; - case 922: - name = "Cheshire"; - break; - case 923: - name = "Cornwall"; - break; - case 924: - name = "Coventry"; - break; - case 925: - name = "Croydon"; - break; - case 926: - name = "Cumbria"; - break; - case 961: - name = "Darlington"; - break; - case 962: - name = "Derby"; - break; - case 963: - name = "Derbyshire"; - break; - case 964: - name = "Devon"; - break; - case 965: - name = "Doncaster"; - break; - case 966: - name = "Dorset"; - break; - case 967: - name = "Dudley"; - break; - case 968: - name = "Durham"; - break; - case 969: - name = "Ealing"; - break; - case 1004: - name = "East Riding of Yorkshire"; - break; - case 1005: - name = "East Sussex"; - break; - case 1006: - name = "Enfield"; - break; - case 1007: - name = "Essex"; - break; - case 1008: - name = "Gateshead"; - break; - case 1009: - name = "Gloucestershire"; - break; - case 1010: - name = "Greenwich"; - break; - case 1011: - name = "Hackney"; - break; - case 1012: - name = "Halton"; - break; - case 1047: - name = "Hammersmith and Fulham"; - break; - case 1048: - name = "Hampshire"; - break; - case 1049: - name = "Haringey"; - break; - case 1050: - name = "Harrow"; - break; - case 1051: - name = "Hartlepool"; - break; - case 1052: - name = "Havering"; - break; - case 1053: - name = "Herefordshire"; - break; - case 1054: - name = "Hertford"; - break; - case 1055: - name = "Hillingdon"; - break; - case 1090: - name = "Hounslow"; - break; - case 1091: - name = "Isle of Wight"; - break; - case 1092: - name = "Islington"; - break; - case 1093: - name = "Kensington and Chelsea"; - break; - case 1094: - name = "Kent"; - break; - case 1095: - name = "Kingston upon Hull"; - break; - case 1096: - name = "Kingston upon Thames"; - break; - case 1097: - name = "Kirklees"; - break; - case 1098: - name = "Knowsley"; - break; - case 1133: - name = "Lambeth"; - break; - case 1134: - name = "Lancashire"; - break; - case 1135: - name = "Leeds"; - break; - case 1136: - name = "Leicester"; - break; - case 1137: - name = "Leicestershire"; - break; - case 1138: - name = "Lewisham"; - break; - case 1139: - name = "Lincolnshire"; - break; - case 1140: - name = "Liverpool"; - break; - case 1141: - name = "London"; - break; - case 1176: - name = "Luton"; - break; - case 1177: - name = "Manchester"; - break; - case 1178: - name = "Medway"; - break; - case 1179: - name = "Merton"; - break; - case 1180: - name = "Middlesbrough"; - break; - case 1181: - name = "Milton Keynes"; - break; - case 1182: - name = "Newcastle upon Tyne"; - break; - case 1183: - name = "Newham"; - break; - case 1184: - name = "Norfolk"; - break; - case 1219: - name = "Northamptonshire"; - break; - case 1220: - name = "North East Lincolnshire"; - break; - case 1221: - name = "North Lincolnshire"; - break; - case 1222: - name = "North Somerset"; - break; - case 1223: - name = "North Tyneside"; - break; - case 1224: - name = "Northumberland"; - break; - case 1225: - name = "North Yorkshire"; - break; - case 1226: - name = "Nottingham"; - break; - case 1227: - name = "Nottinghamshire"; - break; - case 1262: - name = "Oldham"; - break; - case 1263: - name = "Oxfordshire"; - break; - case 1264: - name = "Peterborough"; - break; - case 1265: - name = "Plymouth"; - break; - case 1266: - name = "Poole"; - break; - case 1267: - name = "Portsmouth"; - break; - case 1268: - name = "Reading"; - break; - case 1269: - name = "Redbridge"; - break; - case 1270: - name = "Redcar and Cleveland"; - break; - case 1305: - name = "Richmond upon Thames"; - break; - case 1306: - name = "Rochdale"; - break; - case 1307: - name = "Rotherham"; - break; - case 1308: - name = "Rutland"; - break; - case 1309: - name = "Salford"; - break; - case 1310: - name = "Shropshire"; - break; - case 1311: - name = "Sandwell"; - break; - case 1312: - name = "Sefton"; - break; - case 1313: - name = "Sheffield"; - break; - case 1348: - name = "Slough"; - break; - case 1349: - name = "Solihull"; - break; - case 1350: - name = "Somerset"; - break; - case 1351: - name = "Southampton"; - break; - case 1352: - name = "Southend-on-Sea"; - break; - case 1353: - name = "South Gloucestershire"; - break; - case 1354: - name = "South Tyneside"; - break; - case 1355: - name = "Southwark"; - break; - case 1356: - name = "Staffordshire"; - break; - case 1391: - name = "St. Helens"; - break; - case 1392: - name = "Stockport"; - break; - case 1393: - name = "Stockton-on-Tees"; - break; - case 1394: - name = "Stoke-on-Trent"; - break; - case 1395: - name = "Suffolk"; - break; - case 1396: - name = "Sunderland"; - break; - case 1397: - name = "Surrey"; - break; - case 1398: - name = "Sutton"; - break; - case 1399: - name = "Swindon"; - break; - case 1434: - name = "Tameside"; - break; - case 1435: - name = "Telford and Wrekin"; - break; - case 1436: - name = "Thurrock"; - break; - case 1437: - name = "Torbay"; - break; - case 1438: - name = "Tower Hamlets"; - break; - case 1439: - name = "Trafford"; - break; - case 1440: - name = "Wakefield"; - break; - case 1441: - name = "Walsall"; - break; - case 1442: - name = "Waltham Forest"; - break; - case 1477: - name = "Wandsworth"; - break; - case 1478: - name = "Warrington"; - break; - case 1479: - name = "Warwickshire"; - break; - case 1480: - name = "West Berkshire"; - break; - case 1481: - name = "Westminster"; - break; - case 1482: - name = "West Sussex"; - break; - case 1483: - name = "Wigan"; - break; - case 1484: - name = "Wiltshire"; - break; - case 1485: - name = "Windsor and Maidenhead"; - break; - case 1520: - name = "Wirral"; - break; - case 1521: - name = "Wokingham"; - break; - case 1522: - name = "Wolverhampton"; - break; - case 1523: - name = "Worcestershire"; - break; - case 1524: - name = "York"; - break; - case 1525: - name = "Antrim"; - break; - case 1526: - name = "Ards"; - break; - case 1527: - name = "Armagh"; - break; - case 1528: - name = "Ballymena"; - break; - case 1563: - name = "Ballymoney"; - break; - case 1564: - name = "Banbridge"; - break; - case 1565: - name = "Belfast"; - break; - case 1566: - name = "Carrickfergus"; - break; - case 1567: - name = "Castlereagh"; - break; - case 1568: - name = "Coleraine"; - break; - case 1569: - name = "Cookstown"; - break; - case 1570: - name = "Craigavon"; - break; - case 1571: - name = "Down"; - break; - case 1606: - name = "Dungannon"; - break; - case 1607: - name = "Fermanagh"; - break; - case 1608: - name = "Larne"; - break; - case 1609: - name = "Limavady"; - break; - case 1610: - name = "Lisburn"; - break; - case 1611: - name = "Derry"; - break; - case 1612: - name = "Magherafelt"; - break; - case 1613: - name = "Moyle"; - break; - case 1614: - name = "Newry and Mourne"; - break; - case 1649: - name = "Newtownabbey"; - break; - case 1650: - name = "North Down"; - break; - case 1651: - name = "Omagh"; - break; - case 1652: - name = "Strabane"; - break; - case 1653: - name = "Aberdeen City"; - break; - case 1654: - name = "Aberdeenshire"; - break; - case 1655: - name = "Angus"; - break; - case 1656: - name = "Argyll and Bute"; - break; - case 1657: - name = "Scottish Borders"; - break; - case 1692: - name = "Clackmannanshire"; - break; - case 1693: - name = "Dumfries and Galloway"; - break; - case 1694: - name = "Dundee City"; - break; - case 1695: - name = "East Ayrshire"; - break; - case 1696: - name = "East Dunbartonshire"; - break; - case 1697: - name = "East Lothian"; - break; - case 1698: - name = "East Renfrewshire"; - break; - case 1699: - name = "Edinburgh"; - break; - case 1700: - name = "Falkirk"; - break; - case 1735: - name = "Fife"; - break; - case 1736: - name = "Glasgow City"; - break; - case 1737: - name = "Highland"; - break; - case 1738: - name = "Inverclyde"; - break; - case 1739: - name = "Midlothian"; - break; - case 1740: - name = "Moray"; - break; - case 1741: - name = "North Ayrshire"; - break; - case 1742: - name = "North Lanarkshire"; - break; - case 1743: - name = "Orkney"; - break; - case 1778: - name = "Perth and Kinross"; - break; - case 1779: - name = "Renfrewshire"; - break; - case 1780: - name = "Shetland Islands"; - break; - case 1781: - name = "South Ayrshire"; - break; - case 1782: - name = "South Lanarkshire"; - break; - case 1783: - name = "Stirling"; - break; - case 1784: - name = "West Dunbartonshire"; - break; - case 1785: - name = "Eilean Siar"; - break; - case 1786: - name = "West Lothian"; - break; - case 1821: - name = "Isle of Anglesey"; - break; - case 1822: - name = "Blaenau Gwent"; - break; - case 1823: - name = "Bridgend"; - break; - case 1824: - name = "Caerphilly"; - break; - case 1825: - name = "Cardiff"; - break; - case 1826: - name = "Ceredigion"; - break; - case 1827: - name = "Carmarthenshire"; - break; - case 1828: - name = "Conwy"; - break; - case 1829: - name = "Denbighshire"; - break; - case 1864: - name = "Flintshire"; - break; - case 1865: - name = "Gwynedd"; - break; - case 1866: - name = "Merthyr Tydfil"; - break; - case 1867: - name = "Monmouthshire"; - break; - case 1868: - name = "Neath Port Talbot"; - break; - case 1869: - name = "Newport"; - break; - case 1870: - name = "Pembrokeshire"; - break; - case 1871: - name = "Powys"; - break; - case 1872: - name = "Rhondda Cynon Taff"; - break; - case 1907: - name = "Swansea"; - break; - case 1908: - name = "Torfaen"; - break; - case 1909: - name = "Vale of Glamorgan"; - break; - case 1910: - name = "Wrexham"; - break; - } - } - if (country_code.equals("GD") == true) { - switch (region_code2) { - case 1: - name = "Saint Andrew"; - break; - case 2: - name = "Saint David"; - break; - case 3: - name = "Saint George"; - break; - case 4: - name = "Saint John"; - break; - case 5: - name = "Saint Mark"; - break; - case 6: - name = "Saint Patrick"; - break; - } - } - if (country_code.equals("GE") == true) { - switch (region_code2) { - case 1: - name = "Abashis Raioni"; - break; - case 2: - name = "Abkhazia"; - break; - case 3: - name = "Adigenis Raioni"; - break; - case 4: - name = "Ajaria"; - break; - case 5: - name = "Akhalgoris Raioni"; - break; - case 6: - name = "Akhalk'alak'is Raioni"; - break; - case 7: - name = "Akhalts'ikhis Raioni"; - break; - case 8: - name = "Akhmetis Raioni"; - break; - case 9: - name = "Ambrolauris Raioni"; - break; - case 10: - name = "Aspindzis Raioni"; - break; - case 11: - name = "Baghdat'is Raioni"; - break; - case 12: - name = "Bolnisis Raioni"; - break; - case 13: - name = "Borjomis Raioni"; - break; - case 14: - name = "Chiat'ura"; - break; - case 15: - name = "Ch'khorotsqus Raioni"; - break; - case 16: - name = "Ch'okhatauris Raioni"; - break; - case 17: - name = "Dedop'listsqaros Raioni"; - break; - case 18: - name = "Dmanisis Raioni"; - break; - case 19: - name = "Dushet'is Raioni"; - break; - case 20: - name = "Gardabanis Raioni"; - break; - case 21: - name = "Gori"; - break; - case 22: - name = "Goris Raioni"; - break; - case 23: - name = "Gurjaanis Raioni"; - break; - case 24: - name = "Javis Raioni"; - break; - case 25: - name = "K'arelis Raioni"; - break; - case 26: - name = "Kaspis Raioni"; - break; - case 27: - name = "Kharagaulis Raioni"; - break; - case 28: - name = "Khashuris Raioni"; - break; - case 29: - name = "Khobis Raioni"; - break; - case 30: - name = "Khonis Raioni"; - break; - case 31: - name = "K'ut'aisi"; - break; - case 32: - name = "Lagodekhis Raioni"; - break; - case 33: - name = "Lanch'khut'is Raioni"; - break; - case 34: - name = "Lentekhis Raioni"; - break; - case 35: - name = "Marneulis Raioni"; - break; - case 36: - name = "Martvilis Raioni"; - break; - case 37: - name = "Mestiis Raioni"; - break; - case 38: - name = "Mts'khet'is Raioni"; - break; - case 39: - name = "Ninotsmindis Raioni"; - break; - case 40: - name = "Onis Raioni"; - break; - case 41: - name = "Ozurget'is Raioni"; - break; - case 42: - name = "P'ot'i"; - break; - case 43: - name = "Qazbegis Raioni"; - break; - case 44: - name = "Qvarlis Raioni"; - break; - case 45: - name = "Rust'avi"; - break; - case 46: - name = "Sach'kheris Raioni"; - break; - case 47: - name = "Sagarejos Raioni"; - break; - case 48: - name = "Samtrediis Raioni"; - break; - case 49: - name = "Senakis Raioni"; - break; - case 50: - name = "Sighnaghis Raioni"; - break; - case 51: - name = "T'bilisi"; - break; - case 52: - name = "T'elavis Raioni"; - break; - case 53: - name = "T'erjolis Raioni"; - break; - case 54: - name = "T'et'ritsqaros Raioni"; - break; - case 55: - name = "T'ianet'is Raioni"; - break; - case 56: - name = "Tqibuli"; - break; - case 57: - name = "Ts'ageris Raioni"; - break; - case 58: - name = "Tsalenjikhis Raioni"; - break; - case 59: - name = "Tsalkis Raioni"; - break; - case 60: - name = "Tsqaltubo"; - break; - case 61: - name = "Vanis Raioni"; - break; - case 62: - name = "Zestap'onis Raioni"; - break; - case 63: - name = "Zugdidi"; - break; - case 64: - name = "Zugdidis Raioni"; - break; - } - } - if (country_code.equals("GH") == true) { - switch (region_code2) { - case 1: - name = "Greater Accra"; - break; - case 2: - name = "Ashanti"; - break; - case 3: - name = "Brong-Ahafo"; - break; - case 4: - name = "Central"; - break; - case 5: - name = "Eastern"; - break; - case 6: - name = "Northern"; - break; - case 8: - name = "Volta"; - break; - case 9: - name = "Western"; - break; - case 10: - name = "Upper East"; - break; - case 11: - name = "Upper West"; - break; - } - } - if (country_code.equals("GL") == true) { - switch (region_code2) { - case 1: - name = "Nordgronland"; - break; - case 2: - name = "Ostgronland"; - break; - case 3: - name = "Vestgronland"; - break; - } - } - if (country_code.equals("GM") == true) { - switch (region_code2) { - case 1: - name = "Banjul"; - break; - case 2: - name = "Lower River"; - break; - case 3: - name = "Central River"; - break; - case 4: - name = "Upper River"; - break; - case 5: - name = "Western"; - break; - case 7: - name = "North Bank"; - break; - } - } - if (country_code.equals("GN") == true) { - switch (region_code2) { - case 1: - name = "Beyla"; - break; - case 2: - name = "Boffa"; - break; - case 3: - name = "Boke"; - break; - case 4: - name = "Conakry"; - break; - case 5: - name = "Dabola"; - break; - case 6: - name = "Dalaba"; - break; - case 7: - name = "Dinguiraye"; - break; - case 9: - name = "Faranah"; - break; - case 10: - name = "Forecariah"; - break; - case 11: - name = "Fria"; - break; - case 12: - name = "Gaoual"; - break; - case 13: - name = "Gueckedou"; - break; - case 15: - name = "Kerouane"; - break; - case 16: - name = "Kindia"; - break; - case 17: - name = "Kissidougou"; - break; - case 18: - name = "Koundara"; - break; - case 19: - name = "Kouroussa"; - break; - case 21: - name = "Macenta"; - break; - case 22: - name = "Mali"; - break; - case 23: - name = "Mamou"; - break; - case 25: - name = "Pita"; - break; - case 27: - name = "Telimele"; - break; - case 28: - name = "Tougue"; - break; - case 29: - name = "Yomou"; - break; - case 30: - name = "Coyah"; - break; - case 31: - name = "Dubreka"; - break; - case 32: - name = "Kankan"; - break; - case 33: - name = "Koubia"; - break; - case 34: - name = "Labe"; - break; - case 35: - name = "Lelouma"; - break; - case 36: - name = "Lola"; - break; - case 37: - name = "Mandiana"; - break; - case 38: - name = "Nzerekore"; - break; - case 39: - name = "Siguiri"; - break; - } - } - if (country_code.equals("GQ") == true) { - switch (region_code2) { - case 3: - name = "Annobon"; - break; - case 4: - name = "Bioko Norte"; - break; - case 5: - name = "Bioko Sur"; - break; - case 6: - name = "Centro Sur"; - break; - case 7: - name = "Kie-Ntem"; - break; - case 8: - name = "Litoral"; - break; - case 9: - name = "Wele-Nzas"; - break; - } - } - if (country_code.equals("GR") == true) { - switch (region_code2) { - case 1: - name = "Evros"; - break; - case 2: - name = "Rodhopi"; - break; - case 3: - name = "Xanthi"; - break; - case 4: - name = "Drama"; - break; - case 5: - name = "Serrai"; - break; - case 6: - name = "Kilkis"; - break; - case 7: - name = "Pella"; - break; - case 8: - name = "Florina"; - break; - case 9: - name = "Kastoria"; - break; - case 10: - name = "Grevena"; - break; - case 11: - name = "Kozani"; - break; - case 12: - name = "Imathia"; - break; - case 13: - name = "Thessaloniki"; - break; - case 14: - name = "Kavala"; - break; - case 15: - name = "Khalkidhiki"; - break; - case 16: - name = "Pieria"; - break; - case 17: - name = "Ioannina"; - break; - case 18: - name = "Thesprotia"; - break; - case 19: - name = "Preveza"; - break; - case 20: - name = "Arta"; - break; - case 21: - name = "Larisa"; - break; - case 22: - name = "Trikala"; - break; - case 23: - name = "Kardhitsa"; - break; - case 24: - name = "Magnisia"; - break; - case 25: - name = "Kerkira"; - break; - case 26: - name = "Levkas"; - break; - case 27: - name = "Kefallinia"; - break; - case 28: - name = "Zakinthos"; - break; - case 29: - name = "Fthiotis"; - break; - case 30: - name = "Evritania"; - break; - case 31: - name = "Aitolia kai Akarnania"; - break; - case 32: - name = "Fokis"; - break; - case 33: - name = "Voiotia"; - break; - case 34: - name = "Evvoia"; - break; - case 35: - name = "Attiki"; - break; - case 36: - name = "Argolis"; - break; - case 37: - name = "Korinthia"; - break; - case 38: - name = "Akhaia"; - break; - case 39: - name = "Ilia"; - break; - case 40: - name = "Messinia"; - break; - case 41: - name = "Arkadhia"; - break; - case 42: - name = "Lakonia"; - break; - case 43: - name = "Khania"; - break; - case 44: - name = "Rethimni"; - break; - case 45: - name = "Iraklion"; - break; - case 46: - name = "Lasithi"; - break; - case 47: - name = "Dhodhekanisos"; - break; - case 48: - name = "Samos"; - break; - case 49: - name = "Kikladhes"; - break; - case 50: - name = "Khios"; - break; - case 51: - name = "Lesvos"; - break; - } - } - if (country_code.equals("GT") == true) { - switch (region_code2) { - case 1: - name = "Alta Verapaz"; - break; - case 2: - name = "Baja Verapaz"; - break; - case 3: - name = "Chimaltenango"; - break; - case 4: - name = "Chiquimula"; - break; - case 5: - name = "El Progreso"; - break; - case 6: - name = "Escuintla"; - break; - case 7: - name = "Guatemala"; - break; - case 8: - name = "Huehuetenango"; - break; - case 9: - name = "Izabal"; - break; - case 10: - name = "Jalapa"; - break; - case 11: - name = "Jutiapa"; - break; - case 12: - name = "Peten"; - break; - case 13: - name = "Quetzaltenango"; - break; - case 14: - name = "Quiche"; - break; - case 15: - name = "Retalhuleu"; - break; - case 16: - name = "Sacatepequez"; - break; - case 17: - name = "San Marcos"; - break; - case 18: - name = "Santa Rosa"; - break; - case 19: - name = "Solola"; - break; - case 20: - name = "Suchitepequez"; - break; - case 21: - name = "Totonicapan"; - break; - case 22: - name = "Zacapa"; - break; - } - } - if (country_code.equals("GW") == true) { - switch (region_code2) { - case 1: - name = "Bafata"; - break; - case 2: - name = "Quinara"; - break; - case 4: - name = "Oio"; - break; - case 5: - name = "Bolama"; - break; - case 6: - name = "Cacheu"; - break; - case 7: - name = "Tombali"; - break; - case 10: - name = "Gabu"; - break; - case 11: - name = "Bissau"; - break; - case 12: - name = "Biombo"; - break; - } - } - if (country_code.equals("GY") == true) { - switch (region_code2) { - case 10: - name = "Barima-Waini"; - break; - case 11: - name = "Cuyuni-Mazaruni"; - break; - case 12: - name = "Demerara-Mahaica"; - break; - case 13: - name = "East Berbice-Corentyne"; - break; - case 14: - name = "Essequibo Islands-West Demerara"; - break; - case 15: - name = "Mahaica-Berbice"; - break; - case 16: - name = "Pomeroon-Supenaam"; - break; - case 17: - name = "Potaro-Siparuni"; - break; - case 18: - name = "Upper Demerara-Berbice"; - break; - case 19: - name = "Upper Takutu-Upper Essequibo"; - break; - } - } - if (country_code.equals("HN") == true) { - switch (region_code2) { - case 1: - name = "Atlantida"; - break; - case 2: - name = "Choluteca"; - break; - case 3: - name = "Colon"; - break; - case 4: - name = "Comayagua"; - break; - case 5: - name = "Copan"; - break; - case 6: - name = "Cortes"; - break; - case 7: - name = "El Paraiso"; - break; - case 8: - name = "Francisco Morazan"; - break; - case 9: - name = "Gracias a Dios"; - break; - case 10: - name = "Intibuca"; - break; - case 11: - name = "Islas de la Bahia"; - break; - case 12: - name = "La Paz"; - break; - case 13: - name = "Lempira"; - break; - case 14: - name = "Ocotepeque"; - break; - case 15: - name = "Olancho"; - break; - case 16: - name = "Santa Barbara"; - break; - case 17: - name = "Valle"; - break; - case 18: - name = "Yoro"; - break; - } - } - if (country_code.equals("HR") == true) { - switch (region_code2) { - case 1: - name = "Bjelovarsko-Bilogorska"; - break; - case 2: - name = "Brodsko-Posavska"; - break; - case 3: - name = "Dubrovacko-Neretvanska"; - break; - case 4: - name = "Istarska"; - break; - case 5: - name = "Karlovacka"; - break; - case 6: - name = "Koprivnicko-Krizevacka"; - break; - case 7: - name = "Krapinsko-Zagorska"; - break; - case 8: - name = "Licko-Senjska"; - break; - case 9: - name = "Medimurska"; - break; - case 10: - name = "Osjecko-Baranjska"; - break; - case 11: - name = "Pozesko-Slavonska"; - break; - case 12: - name = "Primorsko-Goranska"; - break; - case 13: - name = "Sibensko-Kninska"; - break; - case 14: - name = "Sisacko-Moslavacka"; - break; - case 15: - name = "Splitsko-Dalmatinska"; - break; - case 16: - name = "Varazdinska"; - break; - case 17: - name = "Viroviticko-Podravska"; - break; - case 18: - name = "Vukovarsko-Srijemska"; - break; - case 19: - name = "Zadarska"; - break; - case 20: - name = "Zagrebacka"; - break; - case 21: - name = "Grad Zagreb"; - break; - } - } - if (country_code.equals("HT") == true) { - switch (region_code2) { - case 3: - name = "Nord-Ouest"; - break; - case 6: - name = "Artibonite"; - break; - case 7: - name = "Centre"; - break; - case 9: - name = "Nord"; - break; - case 10: - name = "Nord-Est"; - break; - case 11: - name = "Ouest"; - break; - case 12: - name = "Sud"; - break; - case 13: - name = "Sud-Est"; - break; - case 14: - name = "Grand' Anse"; - break; - case 15: - name = "Nippes"; - break; - } - } - if (country_code.equals("HU") == true) { - switch (region_code2) { - case 1: - name = "Bacs-Kiskun"; - break; - case 2: - name = "Baranya"; - break; - case 3: - name = "Bekes"; - break; - case 4: - name = "Borsod-Abauj-Zemplen"; - break; - case 5: - name = "Budapest"; - break; - case 6: - name = "Csongrad"; - break; - case 7: - name = "Debrecen"; - break; - case 8: - name = "Fejer"; - break; - case 9: - name = "Gyor-Moson-Sopron"; - break; - case 10: - name = "Hajdu-Bihar"; - break; - case 11: - name = "Heves"; - break; - case 12: - name = "Komarom-Esztergom"; - break; - case 13: - name = "Miskolc"; - break; - case 14: - name = "Nograd"; - break; - case 15: - name = "Pecs"; - break; - case 16: - name = "Pest"; - break; - case 17: - name = "Somogy"; - break; - case 18: - name = "Szabolcs-Szatmar-Bereg"; - break; - case 19: - name = "Szeged"; - break; - case 20: - name = "Jasz-Nagykun-Szolnok"; - break; - case 21: - name = "Tolna"; - break; - case 22: - name = "Vas"; - break; - case 23: - name = "Veszprem"; - break; - case 24: - name = "Zala"; - break; - case 25: - name = "Gyor"; - break; - case 26: - name = "Bekescsaba"; - break; - case 27: - name = "Dunaujvaros"; - break; - case 28: - name = "Eger"; - break; - case 29: - name = "Hodmezovasarhely"; - break; - case 30: - name = "Kaposvar"; - break; - case 31: - name = "Kecskemet"; - break; - case 32: - name = "Nagykanizsa"; - break; - case 33: - name = "Nyiregyhaza"; - break; - case 34: - name = "Sopron"; - break; - case 35: - name = "Szekesfehervar"; - break; - case 36: - name = "Szolnok"; - break; - case 37: - name = "Szombathely"; - break; - case 38: - name = "Tatabanya"; - break; - case 39: - name = "Veszprem"; - break; - case 40: - name = "Zalaegerszeg"; - break; - case 41: - name = "Salgotarjan"; - break; - case 42: - name = "Szekszard"; - break; - } - } - if (country_code.equals("ID") == true) { - switch (region_code2) { - case 1: - name = "Aceh"; - break; - case 2: - name = "Bali"; - break; - case 3: - name = "Bengkulu"; - break; - case 4: - name = "Jakarta Raya"; - break; - case 5: - name = "Jambi"; - break; - case 6: - name = "Jawa Barat"; - break; - case 7: - name = "Jawa Tengah"; - break; - case 8: - name = "Jawa Timur"; - break; - case 9: - name = "Papua"; - break; - case 10: - name = "Yogyakarta"; - break; - case 11: - name = "Kalimantan Barat"; - break; - case 12: - name = "Kalimantan Selatan"; - break; - case 13: - name = "Kalimantan Tengah"; - break; - case 14: - name = "Kalimantan Timur"; - break; - case 15: - name = "Lampung"; - break; - case 16: - name = "Maluku"; - break; - case 17: - name = "Nusa Tenggara Barat"; - break; - case 18: - name = "Nusa Tenggara Timur"; - break; - case 19: - name = "Riau"; - break; - case 20: - name = "Sulawesi Selatan"; - break; - case 21: - name = "Sulawesi Tengah"; - break; - case 22: - name = "Sulawesi Tenggara"; - break; - case 23: - name = "Sulawesi Utara"; - break; - case 24: - name = "Sumatera Barat"; - break; - case 25: - name = "Sumatera Selatan"; - break; - case 26: - name = "Sumatera Utara"; - break; - case 28: - name = "Maluku"; - break; - case 29: - name = "Maluku Utara"; - break; - case 30: - name = "Jawa Barat"; - break; - case 31: - name = "Sulawesi Utara"; - break; - case 32: - name = "Sumatera Selatan"; - break; - case 33: - name = "Banten"; - break; - case 34: - name = "Gorontalo"; - break; - case 35: - name = "Kepulauan Bangka Belitung"; - break; - case 36: - name = "Papua"; - break; - case 37: - name = "Riau"; - break; - case 38: - name = "Sulawesi Selatan"; - break; - case 39: - name = "Irian Jaya Barat"; - break; - case 40: - name = "Kepulauan Riau"; - break; - case 41: - name = "Sulawesi Barat"; - break; - } - } - if (country_code.equals("IE") == true) { - switch (region_code2) { - case 1: - name = "Carlow"; - break; - case 2: - name = "Cavan"; - break; - case 3: - name = "Clare"; - break; - case 4: - name = "Cork"; - break; - case 6: - name = "Donegal"; - break; - case 7: - name = "Dublin"; - break; - case 10: - name = "Galway"; - break; - case 11: - name = "Kerry"; - break; - case 12: - name = "Kildare"; - break; - case 13: - name = "Kilkenny"; - break; - case 14: - name = "Leitrim"; - break; - case 15: - name = "Laois"; - break; - case 16: - name = "Limerick"; - break; - case 18: - name = "Longford"; - break; - case 19: - name = "Louth"; - break; - case 20: - name = "Mayo"; - break; - case 21: - name = "Meath"; - break; - case 22: - name = "Monaghan"; - break; - case 23: - name = "Offaly"; - break; - case 24: - name = "Roscommon"; - break; - case 25: - name = "Sligo"; - break; - case 26: - name = "Tipperary"; - break; - case 27: - name = "Waterford"; - break; - case 29: - name = "Westmeath"; - break; - case 30: - name = "Wexford"; - break; - case 31: - name = "Wicklow"; - break; - } - } - if (country_code.equals("IL") == true) { - switch (region_code2) { - case 1: - name = "HaDarom"; - break; - case 2: - name = "HaMerkaz"; - break; - case 3: - name = "HaZafon"; - break; - case 4: - name = "Hefa"; - break; - case 5: - name = "Tel Aviv"; - break; - case 6: - name = "Yerushalayim"; - break; - } - } - if (country_code.equals("IN") == true) { - switch (region_code2) { - case 1: - name = "Andaman and Nicobar Islands"; - break; - case 2: - name = "Andhra Pradesh"; - break; - case 3: - name = "Assam"; - break; - case 5: - name = "Chandigarh"; - break; - case 6: - name = "Dadra and Nagar Haveli"; - break; - case 7: - name = "Delhi"; - break; - case 9: - name = "Gujarat"; - break; - case 10: - name = "Haryana"; - break; - case 11: - name = "Himachal Pradesh"; - break; - case 12: - name = "Jammu and Kashmir"; - break; - case 13: - name = "Kerala"; - break; - case 14: - name = "Lakshadweep"; - break; - case 16: - name = "Maharashtra"; - break; - case 17: - name = "Manipur"; - break; - case 18: - name = "Meghalaya"; - break; - case 19: - name = "Karnataka"; - break; - case 20: - name = "Nagaland"; - break; - case 21: - name = "Orissa"; - break; - case 22: - name = "Puducherry"; - break; - case 23: - name = "Punjab"; - break; - case 24: - name = "Rajasthan"; - break; - case 25: - name = "Tamil Nadu"; - break; - case 26: - name = "Tripura"; - break; - case 28: - name = "West Bengal"; - break; - case 29: - name = "Sikkim"; - break; - case 30: - name = "Arunachal Pradesh"; - break; - case 31: - name = "Mizoram"; - break; - case 32: - name = "Daman and Diu"; - break; - case 33: - name = "Goa"; - break; - case 34: - name = "Bihar"; - break; - case 35: - name = "Madhya Pradesh"; - break; - case 36: - name = "Uttar Pradesh"; - break; - case 37: - name = "Chhattisgarh"; - break; - case 38: - name = "Jharkhand"; - break; - case 39: - name = "Uttarakhand"; - break; - } - } - if (country_code.equals("IQ") == true) { - switch (region_code2) { - case 1: - name = "Al Anbar"; - break; - case 2: - name = "Al Basrah"; - break; - case 3: - name = "Al Muthanna"; - break; - case 4: - name = "Al Qadisiyah"; - break; - case 5: - name = "As Sulaymaniyah"; - break; - case 6: - name = "Babil"; - break; - case 7: - name = "Baghdad"; - break; - case 8: - name = "Dahuk"; - break; - case 9: - name = "Dhi Qar"; - break; - case 10: - name = "Diyala"; - break; - case 11: - name = "Arbil"; - break; - case 12: - name = "Karbala'"; - break; - case 13: - name = "At Ta'mim"; - break; - case 14: - name = "Maysan"; - break; - case 15: - name = "Ninawa"; - break; - case 16: - name = "Wasit"; - break; - case 17: - name = "An Najaf"; - break; - case 18: - name = "Salah ad Din"; - break; - } - } - if (country_code.equals("IR") == true) { - switch (region_code2) { - case 1: - name = "Azarbayjan-e Bakhtari"; - break; - case 2: - name = "Azarbayjan-e Khavari"; - break; - case 3: - name = "Chahar Mahall va Bakhtiari"; - break; - case 4: - name = "Sistan va Baluchestan"; - break; - case 5: - name = "Kohkiluyeh va Buyer Ahmadi"; - break; - case 7: - name = "Fars"; - break; - case 8: - name = "Gilan"; - break; - case 9: - name = "Hamadan"; - break; - case 10: - name = "Ilam"; - break; - case 11: - name = "Hormozgan"; - break; - case 12: - name = "Kerman"; - break; - case 13: - name = "Bakhtaran"; - break; - case 15: - name = "Khuzestan"; - break; - case 16: - name = "Kordestan"; - break; - case 17: - name = "Mazandaran"; - break; - case 18: - name = "Semnan Province"; - break; - case 19: - name = "Markazi"; - break; - case 21: - name = "Zanjan"; - break; - case 22: - name = "Bushehr"; - break; - case 23: - name = "Lorestan"; - break; - case 24: - name = "Markazi"; - break; - case 25: - name = "Semnan"; - break; - case 26: - name = "Tehran"; - break; - case 27: - name = "Zanjan"; - break; - case 28: - name = "Esfahan"; - break; - case 29: - name = "Kerman"; - break; - case 30: - name = "Khorasan"; - break; - case 31: - name = "Yazd"; - break; - case 32: - name = "Ardabil"; - break; - case 33: - name = "East Azarbaijan"; - break; - case 34: - name = "Markazi"; - break; - case 35: - name = "Mazandaran"; - break; - case 36: - name = "Zanjan"; - break; - case 37: - name = "Golestan"; - break; - case 38: - name = "Qazvin"; - break; - case 39: - name = "Qom"; - break; - case 40: - name = "Yazd"; - break; - case 41: - name = "Khorasan-e Janubi"; - break; - case 42: - name = "Khorasan-e Razavi"; - break; - case 43: - name = "Khorasan-e Shemali"; - break; - } - } - if (country_code.equals("IS") == true) { - switch (region_code2) { - case 3: - name = "Arnessysla"; - break; - case 5: - name = "Austur-Hunavatnssysla"; - break; - case 6: - name = "Austur-Skaftafellssysla"; - break; - case 7: - name = "Borgarfjardarsysla"; - break; - case 9: - name = "Eyjafjardarsysla"; - break; - case 10: - name = "Gullbringusysla"; - break; - case 15: - name = "Kjosarsysla"; - break; - case 17: - name = "Myrasysla"; - break; - case 20: - name = "Nordur-Mulasysla"; - break; - case 21: - name = "Nordur-Tingeyjarsysla"; - break; - case 23: - name = "Rangarvallasysla"; - break; - case 28: - name = "Skagafjardarsysla"; - break; - case 29: - name = "Snafellsnes- og Hnappadalssysla"; - break; - case 30: - name = "Strandasysla"; - break; - case 31: - name = "Sudur-Mulasysla"; - break; - case 32: - name = "Sudur-Tingeyjarsysla"; - break; - case 34: - name = "Vestur-Bardastrandarsysla"; - break; - case 35: - name = "Vestur-Hunavatnssysla"; - break; - case 36: - name = "Vestur-Isafjardarsysla"; - break; - case 37: - name = "Vestur-Skaftafellssysla"; - break; - case 40: - name = "Norourland Eystra"; - break; - case 41: - name = "Norourland Vestra"; - break; - case 42: - name = "Suourland"; - break; - case 43: - name = "Suournes"; - break; - case 44: - name = "Vestfiroir"; - break; - case 45: - name = "Vesturland"; - break; - } - } - if (country_code.equals("IT") == true) { - switch (region_code2) { - case 1: - name = "Abruzzi"; - break; - case 2: - name = "Basilicata"; - break; - case 3: - name = "Calabria"; - break; - case 4: - name = "Campania"; - break; - case 5: - name = "Emilia-Romagna"; - break; - case 6: - name = "Friuli-Venezia Giulia"; - break; - case 7: - name = "Lazio"; - break; - case 8: - name = "Liguria"; - break; - case 9: - name = "Lombardia"; - break; - case 10: - name = "Marche"; - break; - case 11: - name = "Molise"; - break; - case 12: - name = "Piemonte"; - break; - case 13: - name = "Puglia"; - break; - case 14: - name = "Sardegna"; - break; - case 15: - name = "Sicilia"; - break; - case 16: - name = "Toscana"; - break; - case 17: - name = "Trentino-Alto Adige"; - break; - case 18: - name = "Umbria"; - break; - case 19: - name = "Valle d'Aosta"; - break; - case 20: - name = "Veneto"; - break; - } - } - if (country_code.equals("JM") == true) { - switch (region_code2) { - case 1: - name = "Clarendon"; - break; - case 2: - name = "Hanover"; - break; - case 4: - name = "Manchester"; - break; - case 7: - name = "Portland"; - break; - case 8: - name = "Saint Andrew"; - break; - case 9: - name = "Saint Ann"; - break; - case 10: - name = "Saint Catherine"; - break; - case 11: - name = "Saint Elizabeth"; - break; - case 12: - name = "Saint James"; - break; - case 13: - name = "Saint Mary"; - break; - case 14: - name = "Saint Thomas"; - break; - case 15: - name = "Trelawny"; - break; - case 16: - name = "Westmoreland"; - break; - case 17: - name = "Kingston"; - break; - } - } - if (country_code.equals("JO") == true) { - switch (region_code2) { - case 2: - name = "Al Balqa'"; - break; - case 7: - name = "Ma"; - break; - case 9: - name = "Al Karak"; - break; - case 10: - name = "Al Mafraq"; - break; - case 11: - name = "Amman Governorate"; - break; - case 12: - name = "At Tafilah"; - break; - case 13: - name = "Az Zarqa"; - break; - case 14: - name = "Irbid"; - break; - case 16: - name = "Amman"; - break; - } - } - if (country_code.equals("JP") == true) { - switch (region_code2) { - case 1: - name = "Aichi"; - break; - case 2: - name = "Akita"; - break; - case 3: - name = "Aomori"; - break; - case 4: - name = "Chiba"; - break; - case 5: - name = "Ehime"; - break; - case 6: - name = "Fukui"; - break; - case 7: - name = "Fukuoka"; - break; - case 8: - name = "Fukushima"; - break; - case 9: - name = "Gifu"; - break; - case 10: - name = "Gumma"; - break; - case 11: - name = "Hiroshima"; - break; - case 12: - name = "Hokkaido"; - break; - case 13: - name = "Hyogo"; - break; - case 14: - name = "Ibaraki"; - break; - case 15: - name = "Ishikawa"; - break; - case 16: - name = "Iwate"; - break; - case 17: - name = "Kagawa"; - break; - case 18: - name = "Kagoshima"; - break; - case 19: - name = "Kanagawa"; - break; - case 20: - name = "Kochi"; - break; - case 21: - name = "Kumamoto"; - break; - case 22: - name = "Kyoto"; - break; - case 23: - name = "Mie"; - break; - case 24: - name = "Miyagi"; - break; - case 25: - name = "Miyazaki"; - break; - case 26: - name = "Nagano"; - break; - case 27: - name = "Nagasaki"; - break; - case 28: - name = "Nara"; - break; - case 29: - name = "Niigata"; - break; - case 30: - name = "Oita"; - break; - case 31: - name = "Okayama"; - break; - case 32: - name = "Osaka"; - break; - case 33: - name = "Saga"; - break; - case 34: - name = "Saitama"; - break; - case 35: - name = "Shiga"; - break; - case 36: - name = "Shimane"; - break; - case 37: - name = "Shizuoka"; - break; - case 38: - name = "Tochigi"; - break; - case 39: - name = "Tokushima"; - break; - case 40: - name = "Tokyo"; - break; - case 41: - name = "Tottori"; - break; - case 42: - name = "Toyama"; - break; - case 43: - name = "Wakayama"; - break; - case 44: - name = "Yamagata"; - break; - case 45: - name = "Yamaguchi"; - break; - case 46: - name = "Yamanashi"; - break; - case 47: - name = "Okinawa"; - break; - } - } - if (country_code.equals("KE") == true) { - switch (region_code2) { - case 1: - name = "Central"; - break; - case 2: - name = "Coast"; - break; - case 3: - name = "Eastern"; - break; - case 5: - name = "Nairobi Area"; - break; - case 6: - name = "North-Eastern"; - break; - case 7: - name = "Nyanza"; - break; - case 8: - name = "Rift Valley"; - break; - case 9: - name = "Western"; - break; - } - } - if (country_code.equals("KG") == true) { - switch (region_code2) { - case 1: - name = "Bishkek"; - break; - case 2: - name = "Chuy"; - break; - case 3: - name = "Jalal-Abad"; - break; - case 4: - name = "Naryn"; - break; - case 5: - name = "Osh"; - break; - case 6: - name = "Talas"; - break; - case 7: - name = "Ysyk-Kol"; - break; - case 8: - name = "Osh"; - break; - case 9: - name = "Batken"; - break; - } - } - if (country_code.equals("KH") == true) { - switch (region_code2) { - case 1: - name = "Batdambang"; - break; - case 2: - name = "Kampong Cham"; - break; - case 3: - name = "Kampong Chhnang"; - break; - case 4: - name = "Kampong Speu"; - break; - case 5: - name = "Kampong Thum"; - break; - case 6: - name = "Kampot"; - break; - case 7: - name = "Kandal"; - break; - case 8: - name = "Koh Kong"; - break; - case 9: - name = "Kracheh"; - break; - case 10: - name = "Mondulkiri"; - break; - case 11: - name = "Phnum Penh"; - break; - case 12: - name = "Pursat"; - break; - case 13: - name = "Preah Vihear"; - break; - case 14: - name = "Prey Veng"; - break; - case 15: - name = "Ratanakiri Kiri"; - break; - case 16: - name = "Siem Reap"; - break; - case 17: - name = "Stung Treng"; - break; - case 18: - name = "Svay Rieng"; - break; - case 19: - name = "Takeo"; - break; - case 25: - name = "Banteay Meanchey"; - break; - case 29: - name = "Batdambang"; - break; - case 30: - name = "Pailin"; - break; - } - } - if (country_code.equals("KI") == true) { - switch (region_code2) { - case 1: - name = "Gilbert Islands"; - break; - case 2: - name = "Line Islands"; - break; - case 3: - name = "Phoenix Islands"; - break; - } - } - if (country_code.equals("KM") == true) { - switch (region_code2) { - case 1: - name = "Anjouan"; - break; - case 2: - name = "Grande Comore"; - break; - case 3: - name = "Moheli"; - break; - } - } - if (country_code.equals("KN") == true) { - switch (region_code2) { - case 1: - name = "Christ Church Nichola Town"; - break; - case 2: - name = "Saint Anne Sandy Point"; - break; - case 3: - name = "Saint George Basseterre"; - break; - case 4: - name = "Saint George Gingerland"; - break; - case 5: - name = "Saint James Windward"; - break; - case 6: - name = "Saint John Capisterre"; - break; - case 7: - name = "Saint John Figtree"; - break; - case 8: - name = "Saint Mary Cayon"; - break; - case 9: - name = "Saint Paul Capisterre"; - break; - case 10: - name = "Saint Paul Charlestown"; - break; - case 11: - name = "Saint Peter Basseterre"; - break; - case 12: - name = "Saint Thomas Lowland"; - break; - case 13: - name = "Saint Thomas Middle Island"; - break; - case 15: - name = "Trinity Palmetto Point"; - break; - } - } - if (country_code.equals("KP") == true) { - switch (region_code2) { - case 1: - name = "Chagang-do"; - break; - case 3: - name = "Hamgyong-namdo"; - break; - case 6: - name = "Hwanghae-namdo"; - break; - case 7: - name = "Hwanghae-bukto"; - break; - case 8: - name = "Kaesong-si"; - break; - case 9: - name = "Kangwon-do"; - break; - case 11: - name = "P'yongan-bukto"; - break; - case 12: - name = "P'yongyang-si"; - break; - case 13: - name = "Yanggang-do"; - break; - case 14: - name = "Namp'o-si"; - break; - case 15: - name = "P'yongan-namdo"; - break; - case 17: - name = "Hamgyong-bukto"; - break; - case 18: - name = "Najin Sonbong-si"; - break; - } - } - if (country_code.equals("KR") == true) { - switch (region_code2) { - case 1: - name = "Cheju-do"; - break; - case 3: - name = "Cholla-bukto"; - break; - case 5: - name = "Ch'ungch'ong-bukto"; - break; - case 6: - name = "Kangwon-do"; - break; - case 10: - name = "Pusan-jikhalsi"; - break; - case 11: - name = "Seoul-t'ukpyolsi"; - break; - case 12: - name = "Inch'on-jikhalsi"; - break; - case 13: - name = "Kyonggi-do"; - break; - case 14: - name = "Kyongsang-bukto"; - break; - case 15: - name = "Taegu-jikhalsi"; - break; - case 16: - name = "Cholla-namdo"; - break; - case 17: - name = "Ch'ungch'ong-namdo"; - break; - case 18: - name = "Kwangju-jikhalsi"; - break; - case 19: - name = "Taejon-jikhalsi"; - break; - case 20: - name = "Kyongsang-namdo"; - break; - case 21: - name = "Ulsan-gwangyoksi"; - break; - } - } - if (country_code.equals("KW") == true) { - switch (region_code2) { - case 1: - name = "Al Ahmadi"; - break; - case 2: - name = "Al Kuwayt"; - break; - case 5: - name = "Al Jahra"; - break; - case 7: - name = "Al Farwaniyah"; - break; - case 8: - name = "Hawalli"; - break; - case 9: - name = "Mubarak al Kabir"; - break; - } - } - if (country_code.equals("KY") == true) { - switch (region_code2) { - case 1: - name = "Creek"; - break; - case 2: - name = "Eastern"; - break; - case 3: - name = "Midland"; - break; - case 4: - name = "South Town"; - break; - case 5: - name = "Spot Bay"; - break; - case 6: - name = "Stake Bay"; - break; - case 7: - name = "West End"; - break; - case 8: - name = "Western"; - break; - } - } - if (country_code.equals("KZ") == true) { - switch (region_code2) { - case 1: - name = "Almaty"; - break; - case 2: - name = "Almaty City"; - break; - case 3: - name = "Aqmola"; - break; - case 4: - name = "Aqtobe"; - break; - case 5: - name = "Astana"; - break; - case 6: - name = "Atyrau"; - break; - case 7: - name = "West Kazakhstan"; - break; - case 8: - name = "Bayqonyr"; - break; - case 9: - name = "Mangghystau"; - break; - case 10: - name = "South Kazakhstan"; - break; - case 11: - name = "Pavlodar"; - break; - case 12: - name = "Qaraghandy"; - break; - case 13: - name = "Qostanay"; - break; - case 14: - name = "Qyzylorda"; - break; - case 15: - name = "East Kazakhstan"; - break; - case 16: - name = "North Kazakhstan"; - break; - case 17: - name = "Zhambyl"; - break; - } - } - if (country_code.equals("LA") == true) { - switch (region_code2) { - case 1: - name = "Attapu"; - break; - case 2: - name = "Champasak"; - break; - case 3: - name = "Houaphan"; - break; - case 4: - name = "Khammouan"; - break; - case 5: - name = "Louang Namtha"; - break; - case 7: - name = "Oudomxai"; - break; - case 8: - name = "Phongsali"; - break; - case 9: - name = "Saravan"; - break; - case 10: - name = "Savannakhet"; - break; - case 11: - name = "Vientiane"; - break; - case 13: - name = "Xaignabouri"; - break; - case 14: - name = "Xiangkhoang"; - break; - case 17: - name = "Louangphrabang"; - break; - } - } - if (country_code.equals("LB") == true) { - switch (region_code2) { - case 1: - name = "Beqaa"; - break; - case 2: - name = "Al Janub"; - break; - case 3: - name = "Liban-Nord"; - break; - case 4: - name = "Beyrouth"; - break; - case 5: - name = "Mont-Liban"; - break; - case 6: - name = "Liban-Sud"; - break; - case 7: - name = "Nabatiye"; - break; - case 8: - name = "Beqaa"; - break; - case 9: - name = "Liban-Nord"; - break; - case 10: - name = "Aakk"; - break; - case 11: - name = "Baalbek-Hermel"; - break; - } - } - if (country_code.equals("LC") == true) { - switch (region_code2) { - case 1: - name = "Anse-la-Raye"; - break; - case 2: - name = "Dauphin"; - break; - case 3: - name = "Castries"; - break; - case 4: - name = "Choiseul"; - break; - case 5: - name = "Dennery"; - break; - case 6: - name = "Gros-Islet"; - break; - case 7: - name = "Laborie"; - break; - case 8: - name = "Micoud"; - break; - case 9: - name = "Soufriere"; - break; - case 10: - name = "Vieux-Fort"; - break; - case 11: - name = "Praslin"; - break; - } - } - if (country_code.equals("LI") == true) { - switch (region_code2) { - case 1: - name = "Balzers"; - break; - case 2: - name = "Eschen"; - break; - case 3: - name = "Gamprin"; - break; - case 4: - name = "Mauren"; - break; - case 5: - name = "Planken"; - break; - case 6: - name = "Ruggell"; - break; - case 7: - name = "Schaan"; - break; - case 8: - name = "Schellenberg"; - break; - case 9: - name = "Triesen"; - break; - case 10: - name = "Triesenberg"; - break; - case 11: - name = "Vaduz"; - break; - case 21: - name = "Gbarpolu"; - break; - case 22: - name = "River Gee"; - break; - } - } - if (country_code.equals("LK") == true) { - switch (region_code2) { - case 1: - name = "Amparai"; - break; - case 2: - name = "Anuradhapura"; - break; - case 3: - name = "Badulla"; - break; - case 4: - name = "Batticaloa"; - break; - case 6: - name = "Galle"; - break; - case 7: - name = "Hambantota"; - break; - case 9: - name = "Kalutara"; - break; - case 10: - name = "Kandy"; - break; - case 11: - name = "Kegalla"; - break; - case 12: - name = "Kurunegala"; - break; - case 14: - name = "Matale"; - break; - case 15: - name = "Matara"; - break; - case 16: - name = "Moneragala"; - break; - case 17: - name = "Nuwara Eliya"; - break; - case 18: - name = "Polonnaruwa"; - break; - case 19: - name = "Puttalam"; - break; - case 20: - name = "Ratnapura"; - break; - case 21: - name = "Trincomalee"; - break; - case 23: - name = "Colombo"; - break; - case 24: - name = "Gampaha"; - break; - case 25: - name = "Jaffna"; - break; - case 26: - name = "Mannar"; - break; - case 27: - name = "Mullaittivu"; - break; - case 28: - name = "Vavuniya"; - break; - case 29: - name = "Central"; - break; - case 30: - name = "North Central"; - break; - case 31: - name = "Northern"; - break; - case 32: - name = "North Western"; - break; - case 33: - name = "Sabaragamuwa"; - break; - case 34: - name = "Southern"; - break; - case 35: - name = "Uva"; - break; - case 36: - name = "Western"; - break; - } - } - if (country_code.equals("LR") == true) { - switch (region_code2) { - case 1: - name = "Bong"; - break; - case 4: - name = "Grand Cape Mount"; - break; - case 5: - name = "Lofa"; - break; - case 6: - name = "Maryland"; - break; - case 7: - name = "Monrovia"; - break; - case 9: - name = "Nimba"; - break; - case 10: - name = "Sino"; - break; - case 11: - name = "Grand Bassa"; - break; - case 12: - name = "Grand Cape Mount"; - break; - case 13: - name = "Maryland"; - break; - case 14: - name = "Montserrado"; - break; - case 17: - name = "Margibi"; - break; - case 18: - name = "River Cess"; - break; - case 19: - name = "Grand Gedeh"; - break; - case 20: - name = "Lofa"; - break; - case 21: - name = "Gbarpolu"; - break; - case 22: - name = "River Gee"; - break; - } - } - if (country_code.equals("LS") == true) { - switch (region_code2) { - case 10: - name = "Berea"; - break; - case 11: - name = "Butha-Buthe"; - break; - case 12: - name = "Leribe"; - break; - case 13: - name = "Mafeteng"; - break; - case 14: - name = "Maseru"; - break; - case 15: - name = "Mohales Hoek"; - break; - case 16: - name = "Mokhotlong"; - break; - case 17: - name = "Qachas Nek"; - break; - case 18: - name = "Quthing"; - break; - case 19: - name = "Thaba-Tseka"; - break; - } - } - if (country_code.equals("LT") == true) { - switch (region_code2) { - case 56: - name = "Alytaus Apskritis"; - break; - case 57: - name = "Kauno Apskritis"; - break; - case 58: - name = "Klaipedos Apskritis"; - break; - case 59: - name = "Marijampoles Apskritis"; - break; - case 60: - name = "Panevezio Apskritis"; - break; - case 61: - name = "Siauliu Apskritis"; - break; - case 62: - name = "Taurages Apskritis"; - break; - case 63: - name = "Telsiu Apskritis"; - break; - case 64: - name = "Utenos Apskritis"; - break; - case 65: - name = "Vilniaus Apskritis"; - break; - } - } - if (country_code.equals("LU") == true) { - switch (region_code2) { - case 1: - name = "Diekirch"; - break; - case 2: - name = "Grevenmacher"; - break; - case 3: - name = "Luxembourg"; - break; - } - } - if (country_code.equals("LV") == true) { - switch (region_code2) { - case 1: - name = "Aizkraukles"; - break; - case 2: - name = "Aluksnes"; - break; - case 3: - name = "Balvu"; - break; - case 4: - name = "Bauskas"; - break; - case 5: - name = "Cesu"; - break; - case 6: - name = "Daugavpils"; - break; - case 7: - name = "Daugavpils"; - break; - case 8: - name = "Dobeles"; - break; - case 9: - name = "Gulbenes"; - break; - case 10: - name = "Jekabpils"; - break; - case 11: - name = "Jelgava"; - break; - case 12: - name = "Jelgavas"; - break; - case 13: - name = "Jurmala"; - break; - case 14: - name = "Kraslavas"; - break; - case 15: - name = "Kuldigas"; - break; - case 16: - name = "Liepaja"; - break; - case 17: - name = "Liepajas"; - break; - case 18: - name = "Limbazu"; - break; - case 19: - name = "Ludzas"; - break; - case 20: - name = "Madonas"; - break; - case 21: - name = "Ogres"; - break; - case 22: - name = "Preilu"; - break; - case 23: - name = "Rezekne"; - break; - case 24: - name = "Rezeknes"; - break; - case 25: - name = "Riga"; - break; - case 26: - name = "Rigas"; - break; - case 27: - name = "Saldus"; - break; - case 28: - name = "Talsu"; - break; - case 29: - name = "Tukuma"; - break; - case 30: - name = "Valkas"; - break; - case 31: - name = "Valmieras"; - break; - case 32: - name = "Ventspils"; - break; - case 33: - name = "Ventspils"; - break; - } - } - if (country_code.equals("LY") == true) { - switch (region_code2) { - case 3: - name = "Al Aziziyah"; - break; - case 5: - name = "Al Jufrah"; - break; - case 8: - name = "Al Kufrah"; - break; - case 13: - name = "Ash Shati'"; - break; - case 30: - name = "Murzuq"; - break; - case 34: - name = "Sabha"; - break; - case 41: - name = "Tarhunah"; - break; - case 42: - name = "Tubruq"; - break; - case 45: - name = "Zlitan"; - break; - case 47: - name = "Ajdabiya"; - break; - case 48: - name = "Al Fatih"; - break; - case 49: - name = "Al Jabal al Akhdar"; - break; - case 50: - name = "Al Khums"; - break; - case 51: - name = "An Nuqat al Khams"; - break; - case 52: - name = "Awbari"; - break; - case 53: - name = "Az Zawiyah"; - break; - case 54: - name = "Banghazi"; - break; - case 55: - name = "Darnah"; - break; - case 56: - name = "Ghadamis"; - break; - case 57: - name = "Gharyan"; - break; - case 58: - name = "Misratah"; - break; - case 59: - name = "Sawfajjin"; - break; - case 60: - name = "Surt"; - break; - case 61: - name = "Tarabulus"; - break; - case 62: - name = "Yafran"; - break; - } - } - if (country_code.equals("MA") == true) { - switch (region_code2) { - case 1: - name = "Agadir"; - break; - case 2: - name = "Al Hoceima"; - break; - case 3: - name = "Azilal"; - break; - case 4: - name = "Ben Slimane"; - break; - case 5: - name = "Beni Mellal"; - break; - case 6: - name = "Boulemane"; - break; - case 7: - name = "Casablanca"; - break; - case 8: - name = "Chaouen"; - break; - case 9: - name = "El Jadida"; - break; - case 10: - name = "El Kelaa des Srarhna"; - break; - case 11: - name = "Er Rachidia"; - break; - case 12: - name = "Essaouira"; - break; - case 13: - name = "Fes"; - break; - case 14: - name = "Figuig"; - break; - case 15: - name = "Kenitra"; - break; - case 16: - name = "Khemisset"; - break; - case 17: - name = "Khenifra"; - break; - case 18: - name = "Khouribga"; - break; - case 19: - name = "Marrakech"; - break; - case 20: - name = "Meknes"; - break; - case 21: - name = "Nador"; - break; - case 22: - name = "Ouarzazate"; - break; - case 23: - name = "Oujda"; - break; - case 24: - name = "Rabat-Sale"; - break; - case 25: - name = "Safi"; - break; - case 26: - name = "Settat"; - break; - case 27: - name = "Tanger"; - break; - case 29: - name = "Tata"; - break; - case 30: - name = "Taza"; - break; - case 32: - name = "Tiznit"; - break; - case 33: - name = "Guelmim"; - break; - case 34: - name = "Ifrane"; - break; - case 35: - name = "Laayoune"; - break; - case 36: - name = "Tan-Tan"; - break; - case 37: - name = "Taounate"; - break; - case 38: - name = "Sidi Kacem"; - break; - case 39: - name = "Taroudannt"; - break; - case 40: - name = "Tetouan"; - break; - case 41: - name = "Larache"; - break; - case 45: - name = "Grand Casablanca"; - break; - case 46: - name = "Fes-Boulemane"; - break; - case 47: - name = "Marrakech-Tensift-Al Haouz"; - break; - case 48: - name = "Meknes-Tafilalet"; - break; - case 49: - name = "Rabat-Sale-Zemmour-Zaer"; - break; - case 50: - name = "Chaouia-Ouardigha"; - break; - case 51: - name = "Doukkala-Abda"; - break; - case 52: - name = "Gharb-Chrarda-Beni Hssen"; - break; - case 53: - name = "Guelmim-Es Smara"; - break; - case 54: - name = "Oriental"; - break; - case 55: - name = "Souss-Massa-Dr"; - break; - case 56: - name = "Tadla-Azilal"; - break; - case 57: - name = "Tanger-Tetouan"; - break; - case 58: - name = "Taza-Al Hoceima-Taounate"; - break; - case 59: - name = "La"; - break; - } - } - if (country_code.equals("MC") == true) { - switch (region_code2) { - case 1: - name = "La Condamine"; - break; - case 2: - name = "Monaco"; - break; - case 3: - name = "Monte-Carlo"; - break; - } - } - if (country_code.equals("MD") == true) { - switch (region_code2) { - case 46: - name = "Balti"; - break; - case 47: - name = "Cahul"; - break; - case 48: - name = "Chisinau"; - break; - case 49: - name = "Stinga Nistrului"; - break; - case 50: - name = "Edinet"; - break; - case 51: - name = "Gagauzia"; - break; - case 52: - name = "Lapusna"; - break; - case 53: - name = "Orhei"; - break; - case 54: - name = "Soroca"; - break; - case 55: - name = "Tighina"; - break; - case 56: - name = "Ungheni"; - break; - case 58: - name = "Stinga Nistrului"; - break; - case 59: - name = "Anenii Noi"; - break; - case 60: - name = "Balti"; - break; - case 61: - name = "Basarabeasca"; - break; - case 62: - name = "Bender"; - break; - case 63: - name = "Briceni"; - break; - case 64: - name = "Cahul"; - break; - case 65: - name = "Cantemir"; - break; - case 66: - name = "Calarasi"; - break; - case 67: - name = "Causeni"; - break; - case 68: - name = "Cimislia"; - break; - case 69: - name = "Criuleni"; - break; - case 70: - name = "Donduseni"; - break; - case 71: - name = "Drochia"; - break; - case 72: - name = "Dubasari"; - break; - case 73: - name = "Edinet"; - break; - case 74: - name = "Falesti"; - break; - case 75: - name = "Floresti"; - break; - case 76: - name = "Glodeni"; - break; - case 77: - name = "Hincesti"; - break; - case 78: - name = "Ialoveni"; - break; - case 79: - name = "Leova"; - break; - case 80: - name = "Nisporeni"; - break; - case 81: - name = "Ocnita"; - break; - case 83: - name = "Rezina"; - break; - case 84: - name = "Riscani"; - break; - case 85: - name = "Singerei"; - break; - case 86: - name = "Soldanesti"; - break; - case 87: - name = "Soroca"; - break; - case 88: - name = "Stefan-Voda"; - break; - case 89: - name = "Straseni"; - break; - case 90: - name = "Taraclia"; - break; - case 91: - name = "Telenesti"; - break; - case 92: - name = "Ungheni"; - break; - } - } - if (country_code.equals("MG") == true) { - switch (region_code2) { - case 1: - name = "Antsiranana"; - break; - case 2: - name = "Fianarantsoa"; - break; - case 3: - name = "Mahajanga"; - break; - case 4: - name = "Toamasina"; - break; - case 5: - name = "Antananarivo"; - break; - case 6: - name = "Toliara"; - break; - } - } - if (country_code.equals("MK") == true) { - switch (region_code2) { - case 1: - name = "Aracinovo"; - break; - case 2: - name = "Bac"; - break; - case 3: - name = "Belcista"; - break; - case 4: - name = "Berovo"; - break; - case 5: - name = "Bistrica"; - break; - case 6: - name = "Bitola"; - break; - case 7: - name = "Blatec"; - break; - case 8: - name = "Bogdanci"; - break; - case 9: - name = "Bogomila"; - break; - case 10: - name = "Bogovinje"; - break; - case 11: - name = "Bosilovo"; - break; - case 12: - name = "Brvenica"; - break; - case 13: - name = "Cair"; - break; - case 14: - name = "Capari"; - break; - case 15: - name = "Caska"; - break; - case 16: - name = "Cegrane"; - break; - case 17: - name = "Centar"; - break; - case 18: - name = "Centar Zupa"; - break; - case 19: - name = "Cesinovo"; - break; - case 20: - name = "Cucer-Sandevo"; - break; - case 21: - name = "Debar"; - break; - case 22: - name = "Delcevo"; - break; - case 23: - name = "Delogozdi"; - break; - case 24: - name = "Demir Hisar"; - break; - case 25: - name = "Demir Kapija"; - break; - case 26: - name = "Dobrusevo"; - break; - case 27: - name = "Dolna Banjica"; - break; - case 28: - name = "Dolneni"; - break; - case 29: - name = "Dorce Petrov"; - break; - case 30: - name = "Drugovo"; - break; - case 31: - name = "Dzepciste"; - break; - case 32: - name = "Gazi Baba"; - break; - case 33: - name = "Gevgelija"; - break; - case 34: - name = "Gostivar"; - break; - case 35: - name = "Gradsko"; - break; - case 36: - name = "Ilinden"; - break; - case 37: - name = "Izvor"; - break; - case 38: - name = "Jegunovce"; - break; - case 39: - name = "Kamenjane"; - break; - case 40: - name = "Karbinci"; - break; - case 41: - name = "Karpos"; - break; - case 42: - name = "Kavadarci"; - break; - case 43: - name = "Kicevo"; - break; - case 44: - name = "Kisela Voda"; - break; - case 45: - name = "Klecevce"; - break; - case 46: - name = "Kocani"; - break; - case 47: - name = "Konce"; - break; - case 48: - name = "Kondovo"; - break; - case 49: - name = "Konopiste"; - break; - case 50: - name = "Kosel"; - break; - case 51: - name = "Kratovo"; - break; - case 52: - name = "Kriva Palanka"; - break; - case 53: - name = "Krivogastani"; - break; - case 54: - name = "Krusevo"; - break; - case 55: - name = "Kuklis"; - break; - case 56: - name = "Kukurecani"; - break; - case 57: - name = "Kumanovo"; - break; - case 58: - name = "Labunista"; - break; - case 59: - name = "Lipkovo"; - break; - case 60: - name = "Lozovo"; - break; - case 61: - name = "Lukovo"; - break; - case 62: - name = "Makedonska Kamenica"; - break; - case 63: - name = "Makedonski Brod"; - break; - case 64: - name = "Mavrovi Anovi"; - break; - case 65: - name = "Meseista"; - break; - case 66: - name = "Miravci"; - break; - case 67: - name = "Mogila"; - break; - case 68: - name = "Murtino"; - break; - case 69: - name = "Negotino"; - break; - case 70: - name = "Negotino-Polosko"; - break; - case 71: - name = "Novaci"; - break; - case 72: - name = "Novo Selo"; - break; - case 73: - name = "Oblesevo"; - break; - case 74: - name = "Ohrid"; - break; - case 75: - name = "Orasac"; - break; - case 76: - name = "Orizari"; - break; - case 77: - name = "Oslomej"; - break; - case 78: - name = "Pehcevo"; - break; - case 79: - name = "Petrovec"; - break; - case 80: - name = "Plasnica"; - break; - case 81: - name = "Podares"; - break; - case 82: - name = "Prilep"; - break; - case 83: - name = "Probistip"; - break; - case 84: - name = "Radovis"; - break; - case 85: - name = "Rankovce"; - break; - case 86: - name = "Resen"; - break; - case 87: - name = "Rosoman"; - break; - case 88: - name = "Rostusa"; - break; - case 89: - name = "Samokov"; - break; - case 90: - name = "Saraj"; - break; - case 91: - name = "Sipkovica"; - break; - case 92: - name = "Sopiste"; - break; - case 93: - name = "Sopotnica"; - break; - case 94: - name = "Srbinovo"; - break; - case 95: - name = "Staravina"; - break; - case 96: - name = "Star Dojran"; - break; - case 97: - name = "Staro Nagoricane"; - break; - case 98: - name = "Stip"; - break; - case 99: - name = "Struga"; - break; - case 832: - name = "Strumica"; - break; - case 833: - name = "Studenicani"; - break; - case 834: - name = "Suto Orizari"; - break; - case 835: - name = "Sveti Nikole"; - break; - case 836: - name = "Tearce"; - break; - case 837: - name = "Tetovo"; - break; - case 838: - name = "Topolcani"; - break; - case 839: - name = "Valandovo"; - break; - case 840: - name = "Vasilevo"; - break; - case 875: - name = "Veles"; - break; - case 876: - name = "Velesta"; - break; - case 877: - name = "Vevcani"; - break; - case 878: - name = "Vinica"; - break; - case 879: - name = "Vitoliste"; - break; - case 880: - name = "Vranestica"; - break; - case 881: - name = "Vrapciste"; - break; - case 882: - name = "Vratnica"; - break; - case 883: - name = "Vrutok"; - break; - case 918: - name = "Zajas"; - break; - case 919: - name = "Zelenikovo"; - break; - case 920: - name = "Zelino"; - break; - case 921: - name = "Zitose"; - break; - case 922: - name = "Zletovo"; - break; - case 923: - name = "Zrnovci"; - break; - } - } - if (country_code.equals("ML") == true) { - switch (region_code2) { - case 1: - name = "Bamako"; - break; - case 3: - name = "Kayes"; - break; - case 4: - name = "Mopti"; - break; - case 5: - name = "Segou"; - break; - case 6: - name = "Sikasso"; - break; - case 7: - name = "Koulikoro"; - break; - case 8: - name = "Tombouctou"; - break; - case 9: - name = "Gao"; - break; - case 10: - name = "Kidal"; - break; - } - } - if (country_code.equals("MM") == true) { - switch (region_code2) { - case 1: - name = "Rakhine State"; - break; - case 2: - name = "Chin State"; - break; - case 3: - name = "Irrawaddy"; - break; - case 4: - name = "Kachin State"; - break; - case 5: - name = "Karan State"; - break; - case 6: - name = "Kayah State"; - break; - case 7: - name = "Magwe"; - break; - case 8: - name = "Mandalay"; - break; - case 9: - name = "Pegu"; - break; - case 10: - name = "Sagaing"; - break; - case 11: - name = "Shan State"; - break; - case 12: - name = "Tenasserim"; - break; - case 13: - name = "Mon State"; - break; - case 14: - name = "Rangoon"; - break; - case 17: - name = "Yangon"; - break; - } - } - if (country_code.equals("MN") == true) { - switch (region_code2) { - case 1: - name = "Arhangay"; - break; - case 2: - name = "Bayanhongor"; - break; - case 3: - name = "Bayan-Olgiy"; - break; - case 5: - name = "Darhan"; - break; - case 6: - name = "Dornod"; - break; - case 7: - name = "Dornogovi"; - break; - case 8: - name = "Dundgovi"; - break; - case 9: - name = "Dzavhan"; - break; - case 10: - name = "Govi-Altay"; - break; - case 11: - name = "Hentiy"; - break; - case 12: - name = "Hovd"; - break; - case 13: - name = "Hovsgol"; - break; - case 14: - name = "Omnogovi"; - break; - case 15: - name = "Ovorhangay"; - break; - case 16: - name = "Selenge"; - break; - case 17: - name = "Suhbaatar"; - break; - case 18: - name = "Tov"; - break; - case 19: - name = "Uvs"; - break; - case 20: - name = "Ulaanbaatar"; - break; - case 21: - name = "Bulgan"; - break; - case 22: - name = "Erdenet"; - break; - case 23: - name = "Darhan-Uul"; - break; - case 24: - name = "Govisumber"; - break; - case 25: - name = "Orhon"; - break; - } - } - if (country_code.equals("MO") == true) { - switch (region_code2) { - case 1: - name = "Ilhas"; - break; - case 2: - name = "Macau"; - break; - } - } - if (country_code.equals("MR") == true) { - switch (region_code2) { - case 1: - name = "Hodh Ech Chargui"; - break; - case 2: - name = "Hodh El Gharbi"; - break; - case 3: - name = "Assaba"; - break; - case 4: - name = "Gorgol"; - break; - case 5: - name = "Brakna"; - break; - case 6: - name = "Trarza"; - break; - case 7: - name = "Adrar"; - break; - case 8: - name = "Dakhlet Nouadhibou"; - break; - case 9: - name = "Tagant"; - break; - case 10: - name = "Guidimaka"; - break; - case 11: - name = "Tiris Zemmour"; - break; - case 12: - name = "Inchiri"; - break; - } - } - if (country_code.equals("MS") == true) { - switch (region_code2) { - case 1: - name = "Saint Anthony"; - break; - case 2: - name = "Saint Georges"; - break; - case 3: - name = "Saint Peter"; - break; - } - } - if (country_code.equals("MU") == true) { - switch (region_code2) { - case 12: - name = "Black River"; - break; - case 13: - name = "Flacq"; - break; - case 14: - name = "Grand Port"; - break; - case 15: - name = "Moka"; - break; - case 16: - name = "Pamplemousses"; - break; - case 17: - name = "Plaines Wilhems"; - break; - case 18: - name = "Port Louis"; - break; - case 19: - name = "Riviere du Rempart"; - break; - case 20: - name = "Savanne"; - break; - case 21: - name = "Agalega Islands"; - break; - case 22: - name = "Cargados Carajos"; - break; - case 23: - name = "Rodrigues"; - break; - } - } - if (country_code.equals("MV") == true) { - switch (region_code2) { - case 1: - name = "Seenu"; - break; - case 2: - name = "Aliff"; - break; - case 3: - name = "Laviyani"; - break; - case 4: - name = "Waavu"; - break; - case 5: - name = "Laamu"; - break; - case 7: - name = "Haa Aliff"; - break; - case 8: - name = "Thaa"; - break; - case 12: - name = "Meemu"; - break; - case 13: - name = "Raa"; - break; - case 14: - name = "Faafu"; - break; - case 17: - name = "Daalu"; - break; - case 20: - name = "Baa"; - break; - case 23: - name = "Haa Daalu"; - break; - case 24: - name = "Shaviyani"; - break; - case 25: - name = "Noonu"; - break; - case 26: - name = "Kaafu"; - break; - case 27: - name = "Gaafu Aliff"; - break; - case 28: - name = "Gaafu Daalu"; - break; - case 29: - name = "Naviyani"; - break; - case 40: - name = "Male"; - break; - } - } - if (country_code.equals("MW") == true) { - switch (region_code2) { - case 2: - name = "Chikwawa"; - break; - case 3: - name = "Chiradzulu"; - break; - case 4: - name = "Chitipa"; - break; - case 5: - name = "Thyolo"; - break; - case 6: - name = "Dedza"; - break; - case 7: - name = "Dowa"; - break; - case 8: - name = "Karonga"; - break; - case 9: - name = "Kasungu"; - break; - case 11: - name = "Lilongwe"; - break; - case 12: - name = "Mangochi"; - break; - case 13: - name = "Mchinji"; - break; - case 15: - name = "Mzimba"; - break; - case 16: - name = "Ntcheu"; - break; - case 17: - name = "Nkhata Bay"; - break; - case 18: - name = "Nkhotakota"; - break; - case 19: - name = "Nsanje"; - break; - case 20: - name = "Ntchisi"; - break; - case 21: - name = "Rumphi"; - break; - case 22: - name = "Salima"; - break; - case 23: - name = "Zomba"; - break; - case 24: - name = "Blantyre"; - break; - case 25: - name = "Mwanza"; - break; - case 26: - name = "Balaka"; - break; - case 27: - name = "Likoma"; - break; - case 28: - name = "Machinga"; - break; - case 29: - name = "Mulanje"; - break; - case 30: - name = "Phalombe"; - break; - } - } - if (country_code.equals("MX") == true) { - switch (region_code2) { - case 1: - name = "Aguascalientes"; - break; - case 2: - name = "Baja California"; - break; - case 3: - name = "Baja California Sur"; - break; - case 4: - name = "Campeche"; - break; - case 5: - name = "Chiapas"; - break; - case 6: - name = "Chihuahua"; - break; - case 7: - name = "Coahuila de Zaragoza"; - break; - case 8: - name = "Colima"; - break; - case 9: - name = "Distrito Federal"; - break; - case 10: - name = "Durango"; - break; - case 11: - name = "Guanajuato"; - break; - case 12: - name = "Guerrero"; - break; - case 13: - name = "Hidalgo"; - break; - case 14: - name = "Jalisco"; - break; - case 15: - name = "Mexico"; - break; - case 16: - name = "Michoacan de Ocampo"; - break; - case 17: - name = "Morelos"; - break; - case 18: - name = "Nayarit"; - break; - case 19: - name = "Nuevo Leon"; - break; - case 20: - name = "Oaxaca"; - break; - case 21: - name = "Puebla"; - break; - case 22: - name = "Queretaro de Arteaga"; - break; - case 23: - name = "Quintana Roo"; - break; - case 24: - name = "San Luis Potosi"; - break; - case 25: - name = "Sinaloa"; - break; - case 26: - name = "Sonora"; - break; - case 27: - name = "Tabasco"; - break; - case 28: - name = "Tamaulipas"; - break; - case 29: - name = "Tlaxcala"; - break; - case 30: - name = "Veracruz-Llave"; - break; - case 31: - name = "Yucatan"; - break; - case 32: - name = "Zacatecas"; - break; - } - } - if (country_code.equals("MY") == true) { - switch (region_code2) { - case 1: - name = "Johor"; - break; - case 2: - name = "Kedah"; - break; - case 3: - name = "Kelantan"; - break; - case 4: - name = "Melaka"; - break; - case 5: - name = "Negeri Sembilan"; - break; - case 6: - name = "Pahang"; - break; - case 7: - name = "Perak"; - break; - case 8: - name = "Perlis"; - break; - case 9: - name = "Pulau Pinang"; - break; - case 11: - name = "Sarawak"; - break; - case 12: - name = "Selangor"; - break; - case 13: - name = "Terengganu"; - break; - case 14: - name = "Kuala Lumpur"; - break; - case 15: - name = "Labuan"; - break; - case 16: - name = "Sabah"; - break; - case 17: - name = "Putrajaya"; - break; - } - } - if (country_code.equals("MZ") == true) { - switch (region_code2) { - case 1: - name = "Cabo Delgado"; - break; - case 2: - name = "Gaza"; - break; - case 3: - name = "Inhambane"; - break; - case 4: - name = "Maputo"; - break; - case 5: - name = "Sofala"; - break; - case 6: - name = "Nampula"; - break; - case 7: - name = "Niassa"; - break; - case 8: - name = "Tete"; - break; - case 9: - name = "Zambezia"; - break; - case 10: - name = "Manica"; - break; - case 11: - name = "Maputo"; - break; - } - } - if (country_code.equals("NA") == true) { - switch (region_code2) { - case 1: - name = "Bethanien"; - break; - case 2: - name = "Caprivi Oos"; - break; - case 3: - name = "Boesmanland"; - break; - case 4: - name = "Gobabis"; - break; - case 5: - name = "Grootfontein"; - break; - case 6: - name = "Kaokoland"; - break; - case 7: - name = "Karibib"; - break; - case 8: - name = "Keetmanshoop"; - break; - case 9: - name = "Luderitz"; - break; - case 10: - name = "Maltahohe"; - break; - case 11: - name = "Okahandja"; - break; - case 12: - name = "Omaruru"; - break; - case 13: - name = "Otjiwarongo"; - break; - case 14: - name = "Outjo"; - break; - case 15: - name = "Owambo"; - break; - case 16: - name = "Rehoboth"; - break; - case 17: - name = "Swakopmund"; - break; - case 18: - name = "Tsumeb"; - break; - case 20: - name = "Karasburg"; - break; - case 21: - name = "Windhoek"; - break; - case 22: - name = "Damaraland"; - break; - case 23: - name = "Hereroland Oos"; - break; - case 24: - name = "Hereroland Wes"; - break; - case 25: - name = "Kavango"; - break; - case 26: - name = "Mariental"; - break; - case 27: - name = "Namaland"; - break; - case 28: - name = "Caprivi"; - break; - case 29: - name = "Erongo"; - break; - case 30: - name = "Hardap"; - break; - case 31: - name = "Karas"; - break; - case 32: - name = "Kunene"; - break; - case 33: - name = "Ohangwena"; - break; - case 34: - name = "Okavango"; - break; - case 35: - name = "Omaheke"; - break; - case 36: - name = "Omusati"; - break; - case 37: - name = "Oshana"; - break; - case 38: - name = "Oshikoto"; - break; - case 39: - name = "Otjozondjupa"; - break; - } - } - if (country_code.equals("NE") == true) { - switch (region_code2) { - case 1: - name = "Agadez"; - break; - case 2: - name = "Diffa"; - break; - case 3: - name = "Dosso"; - break; - case 4: - name = "Maradi"; - break; - case 5: - name = "Niamey"; - break; - case 6: - name = "Tahoua"; - break; - case 7: - name = "Zinder"; - break; - case 8: - name = "Niamey"; - break; - } - } - if (country_code.equals("NG") == true) { - switch (region_code2) { - case 5: - name = "Lagos"; - break; - case 10: - name = "Rivers"; - break; - case 11: - name = "Federal Capital Territory"; - break; - case 12: - name = "Gongola"; - break; - case 16: - name = "Ogun"; - break; - case 17: - name = "Ondo"; - break; - case 18: - name = "Oyo"; - break; - case 21: - name = "Akwa Ibom"; - break; - case 22: - name = "Cross River"; - break; - case 23: - name = "Kaduna"; - break; - case 24: - name = "Katsina"; - break; - case 25: - name = "Anambra"; - break; - case 26: - name = "Benue"; - break; - case 27: - name = "Borno"; - break; - case 28: - name = "Imo"; - break; - case 29: - name = "Kano"; - break; - case 30: - name = "Kwara"; - break; - case 31: - name = "Niger"; - break; - case 32: - name = "Oyo"; - break; - case 35: - name = "Adamawa"; - break; - case 36: - name = "Delta"; - break; - case 37: - name = "Edo"; - break; - case 39: - name = "Jigawa"; - break; - case 40: - name = "Kebbi"; - break; - case 41: - name = "Kogi"; - break; - case 42: - name = "Osun"; - break; - case 43: - name = "Taraba"; - break; - case 44: - name = "Yobe"; - break; - case 45: - name = "Abia"; - break; - case 46: - name = "Bauchi"; - break; - case 47: - name = "Enugu"; - break; - case 48: - name = "Ondo"; - break; - case 49: - name = "Plateau"; - break; - case 50: - name = "Rivers"; - break; - case 51: - name = "Sokoto"; - break; - case 52: - name = "Bayelsa"; - break; - case 53: - name = "Ebonyi"; - break; - case 54: - name = "Ekiti"; - break; - case 55: - name = "Gombe"; - break; - case 56: - name = "Nassarawa"; - break; - case 57: - name = "Zamfara"; - break; - } - } - if (country_code.equals("NI") == true) { - switch (region_code2) { - case 1: - name = "Boaco"; - break; - case 2: - name = "Carazo"; - break; - case 3: - name = "Chinandega"; - break; - case 4: - name = "Chontales"; - break; - case 5: - name = "Esteli"; - break; - case 6: - name = "Granada"; - break; - case 7: - name = "Jinotega"; - break; - case 8: - name = "Leon"; - break; - case 9: - name = "Madriz"; - break; - case 10: - name = "Managua"; - break; - case 11: - name = "Masaya"; - break; - case 12: - name = "Matagalpa"; - break; - case 13: - name = "Nueva Segovia"; - break; - case 14: - name = "Rio San Juan"; - break; - case 15: - name = "Rivas"; - break; - case 16: - name = "Zelaya"; - break; - case 17: - name = "Autonoma Atlantico Norte"; - break; - case 18: - name = "Region Autonoma Atlantico Sur"; - break; - } - } - if (country_code.equals("NL") == true) { - switch (region_code2) { - case 1: - name = "Drenthe"; - break; - case 2: - name = "Friesland"; - break; - case 3: - name = "Gelderland"; - break; - case 4: - name = "Groningen"; - break; - case 5: - name = "Limburg"; - break; - case 6: - name = "Noord-Brabant"; - break; - case 7: - name = "Noord-Holland"; - break; - case 8: - name = "Overijssel"; - break; - case 9: - name = "Utrecht"; - break; - case 10: - name = "Zeeland"; - break; - case 11: - name = "Zuid-Holland"; - break; - case 12: - name = "Dronten"; - break; - case 13: - name = "Zuidelijke IJsselmeerpolders"; - break; - case 14: - name = "Lelystad"; - break; - case 15: - name = "Overijssel"; - break; - case 16: - name = "Flevoland"; - break; - } - } - if (country_code.equals("NO") == true) { - switch (region_code2) { - case 1: - name = "Akershus"; - break; - case 2: - name = "Aust-Agder"; - break; - case 4: - name = "Buskerud"; - break; - case 5: - name = "Finnmark"; - break; - case 6: - name = "Hedmark"; - break; - case 7: - name = "Hordaland"; - break; - case 8: - name = "More og Romsdal"; - break; - case 9: - name = "Nordland"; - break; - case 10: - name = "Nord-Trondelag"; - break; - case 11: - name = "Oppland"; - break; - case 12: - name = "Oslo"; - break; - case 13: - name = "Ostfold"; - break; - case 14: - name = "Rogaland"; - break; - case 15: - name = "Sogn og Fjordane"; - break; - case 16: - name = "Sor-Trondelag"; - break; - case 17: - name = "Telemark"; - break; - case 18: - name = "Troms"; - break; - case 19: - name = "Vest-Agder"; - break; - case 20: - name = "Vestfold"; - break; - } - } - if (country_code.equals("NP") == true) { - switch (region_code2) { - case 1: - name = "Bagmati"; - break; - case 2: - name = "Bheri"; - break; - case 3: - name = "Dhawalagiri"; - break; - case 4: - name = "Gandaki"; - break; - case 5: - name = "Janakpur"; - break; - case 6: - name = "Karnali"; - break; - case 7: - name = "Kosi"; - break; - case 8: - name = "Lumbini"; - break; - case 9: - name = "Mahakali"; - break; - case 10: - name = "Mechi"; - break; - case 11: - name = "Narayani"; - break; - case 12: - name = "Rapti"; - break; - case 13: - name = "Sagarmatha"; - break; - case 14: - name = "Seti"; - break; - } - } - if (country_code.equals("NR") == true) { - switch (region_code2) { - case 1: - name = "Aiwo"; - break; - case 2: - name = "Anabar"; - break; - case 3: - name = "Anetan"; - break; - case 4: - name = "Anibare"; - break; - case 5: - name = "Baiti"; - break; - case 6: - name = "Boe"; - break; - case 7: - name = "Buada"; - break; - case 8: - name = "Denigomodu"; - break; - case 9: - name = "Ewa"; - break; - case 10: - name = "Ijuw"; - break; - case 11: - name = "Meneng"; - break; - case 12: - name = "Nibok"; - break; - case 13: - name = "Uaboe"; - break; - case 14: - name = "Yaren"; - break; - } - } - if (country_code.equals("NZ") == true) { - switch (region_code2) { - case 10: - name = "Chatham Islands"; - break; - case 1010: - name = "Auckland"; - break; - case 1011: - name = "Bay of Plenty"; - break; - case 1012: - name = "Canterbury"; - break; - case 1047: - name = "Gisborne"; - break; - case 1048: - name = "Hawke's Bay"; - break; - case 1049: - name = "Manawatu-Wanganui"; - break; - case 1050: - name = "Marlborough"; - break; - case 1051: - name = "Nelson"; - break; - case 1052: - name = "Northland"; - break; - case 1053: - name = "Otago"; - break; - case 1054: - name = "Southland"; - break; - case 1055: - name = "Taranaki"; - break; - case 1090: - name = "Waikato"; - break; - case 1091: - name = "Wellington"; - break; - case 1092: - name = "West Coast"; - break; - case 85: - name = "Waikato"; - break; - } - } - if (country_code.equals("OM") == true) { - switch (region_code2) { - case 1: - name = "Ad Dakhiliyah"; - break; - case 2: - name = "Al Batinah"; - break; - case 3: - name = "Al Wusta"; - break; - case 4: - name = "Ash Sharqiyah"; - break; - case 5: - name = "Az Zahirah"; - break; - case 6: - name = "Masqat"; - break; - case 7: - name = "Musandam"; - break; - case 8: - name = "Zufar"; - break; - } - } - if (country_code.equals("PA") == true) { - switch (region_code2) { - case 1: - name = "Bocas del Toro"; - break; - case 2: - name = "Chiriqui"; - break; - case 3: - name = "Cocle"; - break; - case 4: - name = "Colon"; - break; - case 5: - name = "Darien"; - break; - case 6: - name = "Herrera"; - break; - case 7: - name = "Los Santos"; - break; - case 8: - name = "Panama"; - break; - case 9: - name = "San Blas"; - break; - case 10: - name = "Veraguas"; - break; - } - } - if (country_code.equals("PE") == true) { - switch (region_code2) { - case 1: - name = "Amazonas"; - break; - case 2: - name = "Ancash"; - break; - case 3: - name = "Apurimac"; - break; - case 4: - name = "Arequipa"; - break; - case 5: - name = "Ayacucho"; - break; - case 6: - name = "Cajamarca"; - break; - case 7: - name = "Callao"; - break; - case 8: - name = "Cusco"; - break; - case 9: - name = "Huancavelica"; - break; - case 10: - name = "Huanuco"; - break; - case 11: - name = "Ica"; - break; - case 12: - name = "Junin"; - break; - case 13: - name = "La Libertad"; - break; - case 14: - name = "Lambayeque"; - break; - case 15: - name = "Lima"; - break; - case 16: - name = "Loreto"; - break; - case 17: - name = "Madre de Dios"; - break; - case 18: - name = "Moquegua"; - break; - case 19: - name = "Pasco"; - break; - case 20: - name = "Piura"; - break; - case 21: - name = "Puno"; - break; - case 22: - name = "San Martin"; - break; - case 23: - name = "Tacna"; - break; - case 24: - name = "Tumbes"; - break; - case 25: - name = "Ucayali"; - break; - } - } - if (country_code.equals("PG") == true) { - switch (region_code2) { - case 1: - name = "Central"; - break; - case 2: - name = "Gulf"; - break; - case 3: - name = "Milne Bay"; - break; - case 4: - name = "Northern"; - break; - case 5: - name = "Southern Highlands"; - break; - case 6: - name = "Western"; - break; - case 7: - name = "North Solomons"; - break; - case 8: - name = "Chimbu"; - break; - case 9: - name = "Eastern Highlands"; - break; - case 10: - name = "East New Britain"; - break; - case 11: - name = "East Sepik"; - break; - case 12: - name = "Madang"; - break; - case 13: - name = "Manus"; - break; - case 14: - name = "Morobe"; - break; - case 15: - name = "New Ireland"; - break; - case 16: - name = "Western Highlands"; - break; - case 17: - name = "West New Britain"; - break; - case 18: - name = "Sandaun"; - break; - case 19: - name = "Enga"; - break; - case 20: - name = "National Capital"; - break; - } - } - if (country_code.equals("PH") == true) { - switch (region_code2) { - case 1: - name = "Abra"; - break; - case 2: - name = "Agusan del Norte"; - break; - case 3: - name = "Agusan del Sur"; - break; - case 4: - name = "Aklan"; - break; - case 5: - name = "Albay"; - break; - case 6: - name = "Antique"; - break; - case 7: - name = "Bataan"; - break; - case 8: - name = "Batanes"; - break; - case 9: - name = "Batangas"; - break; - case 10: - name = "Benguet"; - break; - case 11: - name = "Bohol"; - break; - case 12: - name = "Bukidnon"; - break; - case 13: - name = "Bulacan"; - break; - case 14: - name = "Cagayan"; - break; - case 15: - name = "Camarines Norte"; - break; - case 16: - name = "Camarines Sur"; - break; - case 17: - name = "Camiguin"; - break; - case 18: - name = "Capiz"; - break; - case 19: - name = "Catanduanes"; - break; - case 20: - name = "Cavite"; - break; - case 21: - name = "Cebu"; - break; - case 22: - name = "Basilan"; - break; - case 23: - name = "Eastern Samar"; - break; - case 24: - name = "Davao"; - break; - case 25: - name = "Davao del Sur"; - break; - case 26: - name = "Davao Oriental"; - break; - case 27: - name = "Ifugao"; - break; - case 28: - name = "Ilocos Norte"; - break; - case 29: - name = "Ilocos Sur"; - break; - case 30: - name = "Iloilo"; - break; - case 31: - name = "Isabela"; - break; - case 32: - name = "Kalinga-Apayao"; - break; - case 33: - name = "Laguna"; - break; - case 34: - name = "Lanao del Norte"; - break; - case 35: - name = "Lanao del Sur"; - break; - case 36: - name = "La Union"; - break; - case 37: - name = "Leyte"; - break; - case 38: - name = "Marinduque"; - break; - case 39: - name = "Masbate"; - break; - case 40: - name = "Mindoro Occidental"; - break; - case 41: - name = "Mindoro Oriental"; - break; - case 42: - name = "Misamis Occidental"; - break; - case 43: - name = "Misamis Oriental"; - break; - case 44: - name = "Mountain"; - break; - case 45: - name = "Negros Occidental"; - break; - case 46: - name = "Negros Oriental"; - break; - case 47: - name = "Nueva Ecija"; - break; - case 48: - name = "Nueva Vizcaya"; - break; - case 49: - name = "Palawan"; - break; - case 50: - name = "Pampanga"; - break; - case 51: - name = "Pangasinan"; - break; - case 53: - name = "Rizal"; - break; - case 54: - name = "Romblon"; - break; - case 55: - name = "Samar"; - break; - case 56: - name = "Maguindanao"; - break; - case 57: - name = "North Cotabato"; - break; - case 58: - name = "Sorsogon"; - break; - case 59: - name = "Southern Leyte"; - break; - case 60: - name = "Sulu"; - break; - case 61: - name = "Surigao del Norte"; - break; - case 62: - name = "Surigao del Sur"; - break; - case 63: - name = "Tarlac"; - break; - case 64: - name = "Zambales"; - break; - case 65: - name = "Zamboanga del Norte"; - break; - case 66: - name = "Zamboanga del Sur"; - break; - case 67: - name = "Northern Samar"; - break; - case 68: - name = "Quirino"; - break; - case 69: - name = "Siquijor"; - break; - case 70: - name = "South Cotabato"; - break; - case 71: - name = "Sultan Kudarat"; - break; - case 72: - name = "Tawitawi"; - break; - case 832: - name = "Angeles"; - break; - case 833: - name = "Bacolod"; - break; - case 834: - name = "Bago"; - break; - case 835: - name = "Baguio"; - break; - case 836: - name = "Bais"; - break; - case 837: - name = "Basilan City"; - break; - case 838: - name = "Batangas City"; - break; - case 839: - name = "Butuan"; - break; - case 840: - name = "Cabanatuan"; - break; - case 875: - name = "Cadiz"; - break; - case 876: - name = "Cagayan de Oro"; - break; - case 877: - name = "Calbayog"; - break; - case 878: - name = "Caloocan"; - break; - case 879: - name = "Canlaon"; - break; - case 880: - name = "Cavite City"; - break; - case 881: - name = "Cebu City"; - break; - case 882: - name = "Cotabato"; - break; - case 883: - name = "Dagupan"; - break; - case 918: - name = "Danao"; - break; - case 919: - name = "Dapitan"; - break; - case 920: - name = "Davao City"; - break; - case 921: - name = "Dipolog"; - break; - case 922: - name = "Dumaguete"; - break; - case 923: - name = "General Santos"; - break; - case 924: - name = "Gingoog"; - break; - case 925: - name = "Iligan"; - break; - case 926: - name = "Iloilo City"; - break; - case 961: - name = "Iriga"; - break; - case 962: - name = "La Carlota"; - break; - case 963: - name = "Laoag"; - break; - case 964: - name = "Lapu-Lapu"; - break; - case 965: - name = "Legaspi"; - break; - case 966: - name = "Lipa"; - break; - case 967: - name = "Lucena"; - break; - case 968: - name = "Mandaue"; - break; - case 969: - name = "Manila"; - break; - case 1004: - name = "Marawi"; - break; - case 1005: - name = "Naga"; - break; - case 1006: - name = "Olongapo"; - break; - case 1007: - name = "Ormoc"; - break; - case 1008: - name = "Oroquieta"; - break; - case 1009: - name = "Ozamis"; - break; - case 1010: - name = "Pagadian"; - break; - case 1011: - name = "Palayan"; - break; - case 1012: - name = "Pasay"; - break; - case 1047: - name = "Puerto Princesa"; - break; - case 1048: - name = "Quezon City"; - break; - case 1049: - name = "Roxas"; - break; - case 1050: - name = "San Carlos"; - break; - case 1051: - name = "San Carlos"; - break; - case 1052: - name = "San Jose"; - break; - case 1053: - name = "San Pablo"; - break; - case 1054: - name = "Silay"; - break; - case 1055: - name = "Surigao"; - break; - case 1090: - name = "Tacloban"; - break; - case 1091: - name = "Tagaytay"; - break; - case 1092: - name = "Tagbilaran"; - break; - case 1093: - name = "Tangub"; - break; - case 1094: - name = "Toledo"; - break; - case 1095: - name = "Trece Martires"; - break; - case 1096: - name = "Zamboanga"; - break; - case 1097: - name = "Aurora"; - break; - case 1134: - name = "Quezon"; - break; - case 1135: - name = "Negros Occidental"; - break; - } - } - if (country_code.equals("PK") == true) { - switch (region_code2) { - case 1: - name = "Federally Administered Tribal Areas"; - break; - case 2: - name = "Balochistan"; - break; - case 3: - name = "North-West Frontier"; - break; - case 4: - name = "Punjab"; - break; - case 5: - name = "Sindh"; - break; - case 6: - name = "Azad Kashmir"; - break; - case 7: - name = "Northern Areas"; - break; - case 8: - name = "Islamabad"; - break; - } - } - if (country_code.equals("PL") == true) { - switch (region_code2) { - case 72: - name = "Dolnoslaskie"; - break; - case 73: - name = "Kujawsko-Pomorskie"; - break; - case 74: - name = "Lodzkie"; - break; - case 75: - name = "Lubelskie"; - break; - case 76: - name = "Lubuskie"; - break; - case 77: - name = "Malopolskie"; - break; - case 78: - name = "Mazowieckie"; - break; - case 79: - name = "Opolskie"; - break; - case 80: - name = "Podkarpackie"; - break; - case 81: - name = "Podlaskie"; - break; - case 82: - name = "Pomorskie"; - break; - case 83: - name = "Slaskie"; - break; - case 84: - name = "Swietokrzyskie"; - break; - case 85: - name = "Warminsko-Mazurskie"; - break; - case 86: - name = "Wielkopolskie"; - break; - case 87: - name = "Zachodniopomorskie"; - break; - } - } - if (country_code.equals("PS") == true) { - switch (region_code2) { - case 1131: - name = "Gaza"; - break; - case 1798: - name = "West Bank"; - break; - } - } - if (country_code.equals("PT") == true) { - switch (region_code2) { - case 2: - name = "Aveiro"; - break; - case 3: - name = "Beja"; - break; - case 4: - name = "Braga"; - break; - case 5: - name = "Braganca"; - break; - case 6: - name = "Castelo Branco"; - break; - case 7: - name = "Coimbra"; - break; - case 8: - name = "Evora"; - break; - case 9: - name = "Faro"; - break; - case 10: - name = "Madeira"; - break; - case 11: - name = "Guarda"; - break; - case 13: - name = "Leiria"; - break; - case 14: - name = "Lisboa"; - break; - case 16: - name = "Portalegre"; - break; - case 17: - name = "Porto"; - break; - case 18: - name = "Santarem"; - break; - case 19: - name = "Setubal"; - break; - case 20: - name = "Viana do Castelo"; - break; - case 21: - name = "Vila Real"; - break; - case 22: - name = "Viseu"; - break; - case 23: - name = "Azores"; - break; - } - } - if (country_code.equals("PY") == true) { - switch (region_code2) { - case 1: - name = "Alto Parana"; - break; - case 2: - name = "Amambay"; - break; - case 3: - name = "Boqueron"; - break; - case 4: - name = "Caaguazu"; - break; - case 5: - name = "Caazapa"; - break; - case 6: - name = "Central"; - break; - case 7: - name = "Concepcion"; - break; - case 8: - name = "Cordillera"; - break; - case 10: - name = "Guaira"; - break; - case 11: - name = "Itapua"; - break; - case 12: - name = "Misiones"; - break; - case 13: - name = "Neembucu"; - break; - case 15: - name = "Paraguari"; - break; - case 16: - name = "Presidente Hayes"; - break; - case 17: - name = "San Pedro"; - break; - case 19: - name = "Canindeyu"; - break; - case 20: - name = "Chaco"; - break; - case 21: - name = "Nueva Asuncion"; - break; - case 23: - name = "Alto Paraguay"; - break; - } - } - if (country_code.equals("QA") == true) { - switch (region_code2) { - case 1: - name = "Ad Dawhah"; - break; - case 2: - name = "Al Ghuwariyah"; - break; - case 3: - name = "Al Jumaliyah"; - break; - case 4: - name = "Al Khawr"; - break; - case 5: - name = "Al Wakrah Municipality"; - break; - case 6: - name = "Ar Rayyan"; - break; - case 8: - name = "Madinat ach Shamal"; - break; - case 9: - name = "Umm Salal"; - break; - case 10: - name = "Al Wakrah"; - break; - case 11: - name = "Jariyan al Batnah"; - break; - case 12: - name = "Umm Sa'id"; - break; - } - } - if (country_code.equals("RO") == true) { - switch (region_code2) { - case 1: - name = "Alba"; - break; - case 2: - name = "Arad"; - break; - case 3: - name = "Arges"; - break; - case 4: - name = "Bacau"; - break; - case 5: - name = "Bihor"; - break; - case 6: - name = "Bistrita-Nasaud"; - break; - case 7: - name = "Botosani"; - break; - case 8: - name = "Braila"; - break; - case 9: - name = "Brasov"; - break; - case 10: - name = "Bucuresti"; - break; - case 11: - name = "Buzau"; - break; - case 12: - name = "Caras-Severin"; - break; - case 13: - name = "Cluj"; - break; - case 14: - name = "Constanta"; - break; - case 15: - name = "Covasna"; - break; - case 16: - name = "Dambovita"; - break; - case 17: - name = "Dolj"; - break; - case 18: - name = "Galati"; - break; - case 19: - name = "Gorj"; - break; - case 20: - name = "Harghita"; - break; - case 21: - name = "Hunedoara"; - break; - case 22: - name = "Ialomita"; - break; - case 23: - name = "Iasi"; - break; - case 25: - name = "Maramures"; - break; - case 26: - name = "Mehedinti"; - break; - case 27: - name = "Mures"; - break; - case 28: - name = "Neamt"; - break; - case 29: - name = "Olt"; - break; - case 30: - name = "Prahova"; - break; - case 31: - name = "Salaj"; - break; - case 32: - name = "Satu Mare"; - break; - case 33: - name = "Sibiu"; - break; - case 34: - name = "Suceava"; - break; - case 35: - name = "Teleorman"; - break; - case 36: - name = "Timis"; - break; - case 37: - name = "Tulcea"; - break; - case 38: - name = "Vaslui"; - break; - case 39: - name = "Valcea"; - break; - case 40: - name = "Vrancea"; - break; - case 41: - name = "Calarasi"; - break; - case 42: - name = "Giurgiu"; - break; - case 43: - name = "Ilfov"; - break; - } - } - if (country_code.equals("RS") == true) { - switch (region_code2) { - case 1: - name = "Kosovo"; - break; - case 2: - name = "Vojvodina"; - break; - } - } - if (country_code.equals("RU") == true) { - switch (region_code2) { - case 1: - name = "Adygeya"; - break; - case 2: - name = "Aginsky Buryatsky AO"; - break; - case 3: - name = "Gorno-Altay"; - break; - case 4: - name = "Altaisky krai"; - break; - case 5: - name = "Amur"; - break; - case 6: - name = "Arkhangel'sk"; - break; - case 7: - name = "Astrakhan'"; - break; - case 8: - name = "Bashkortostan"; - break; - case 9: - name = "Belgorod"; - break; - case 10: - name = "Bryansk"; - break; - case 11: - name = "Buryat"; - break; - case 12: - name = "Chechnya"; - break; - case 13: - name = "Chelyabinsk"; - break; - case 14: - name = "Chita"; - break; - case 15: - name = "Chukot"; - break; - case 16: - name = "Chuvashia"; - break; - case 17: - name = "Dagestan"; - break; - case 18: - name = "Evenk"; - break; - case 19: - name = "Ingush"; - break; - case 20: - name = "Irkutsk"; - break; - case 21: - name = "Ivanovo"; - break; - case 22: - name = "Kabardin-Balkar"; - break; - case 23: - name = "Kaliningrad"; - break; - case 24: - name = "Kalmyk"; - break; - case 25: - name = "Kaluga"; - break; - case 26: - name = "Kamchatka"; - break; - case 27: - name = "Karachay-Cherkess"; - break; - case 28: - name = "Karelia"; - break; - case 29: - name = "Kemerovo"; - break; - case 30: - name = "Khabarovsk"; - break; - case 31: - name = "Khakass"; - break; - case 32: - name = "Khanty-Mansiy"; - break; - case 33: - name = "Kirov"; - break; - case 34: - name = "Komi"; - break; - case 35: - name = "Komi-Permyak"; - break; - case 36: - name = "Koryak"; - break; - case 37: - name = "Kostroma"; - break; - case 38: - name = "Krasnodar"; - break; - case 39: - name = "Krasnoyarsk"; - break; - case 40: - name = "Kurgan"; - break; - case 41: - name = "Kursk"; - break; - case 42: - name = "Leningrad"; - break; - case 43: - name = "Lipetsk"; - break; - case 44: - name = "Magadan"; - break; - case 45: - name = "Mariy-El"; - break; - case 46: - name = "Mordovia"; - break; - case 47: - name = "Moskva"; - break; - case 48: - name = "Moscow City"; - break; - case 49: - name = "Murmansk"; - break; - case 50: - name = "Nenets"; - break; - case 51: - name = "Nizhegorod"; - break; - case 52: - name = "Novgorod"; - break; - case 53: - name = "Novosibirsk"; - break; - case 54: - name = "Omsk"; - break; - case 55: - name = "Orenburg"; - break; - case 56: - name = "Orel"; - break; - case 57: - name = "Penza"; - break; - case 58: - name = "Perm'"; - break; - case 59: - name = "Primor'ye"; - break; - case 60: - name = "Pskov"; - break; - case 61: - name = "Rostov"; - break; - case 62: - name = "Ryazan'"; - break; - case 63: - name = "Sakha"; - break; - case 64: - name = "Sakhalin"; - break; - case 65: - name = "Samara"; - break; - case 66: - name = "Saint Petersburg City"; - break; - case 67: - name = "Saratov"; - break; - case 68: - name = "North Ossetia"; - break; - case 69: - name = "Smolensk"; - break; - case 70: - name = "Stavropol'"; - break; - case 71: - name = "Sverdlovsk"; - break; - case 72: - name = "Tambovskaya oblast"; - break; - case 73: - name = "Tatarstan"; - break; - case 74: - name = "Taymyr"; - break; - case 75: - name = "Tomsk"; - break; - case 76: - name = "Tula"; - break; - case 77: - name = "Tver'"; - break; - case 78: - name = "Tyumen'"; - break; - case 79: - name = "Tuva"; - break; - case 80: - name = "Udmurt"; - break; - case 81: - name = "Ul'yanovsk"; - break; - case 82: - name = "Ust-Orda Buryat"; - break; - case 83: - name = "Vladimir"; - break; - case 84: - name = "Volgograd"; - break; - case 85: - name = "Vologda"; - break; - case 86: - name = "Voronezh"; - break; - case 87: - name = "Yamal-Nenets"; - break; - case 88: - name = "Yaroslavl'"; - break; - case 89: - name = "Yevrey"; - break; - case 90: - name = "Permskiy Kray"; - break; - case 91: - name = "Krasnoyarskiy Kray"; - break; - case 942: - name = "Chechnya Republic"; - break; - } - } - if (country_code.equals("RW") == true) { - switch (region_code2) { - case 1: - name = "Butare"; - break; - case 6: - name = "Gitarama"; - break; - case 7: - name = "Kibungo"; - break; - case 9: - name = "Kigali"; - break; - case 11: - name = "Est"; - break; - case 12: - name = "Kigali"; - break; - case 13: - name = "Nord"; - break; - case 14: - name = "Ouest"; - break; - case 15: - name = "Sud"; - break; - } - } - if (country_code.equals("SA") == true) { - switch (region_code2) { - case 2: - name = "Al Bahah"; - break; - case 3: - name = "Al Jawf"; - break; - case 5: - name = "Al Madinah"; - break; - case 6: - name = "Ash Sharqiyah"; - break; - case 8: - name = "Al Qasim"; - break; - case 9: - name = "Al Qurayyat"; - break; - case 10: - name = "Ar Riyad"; - break; - case 13: - name = "Ha'il"; - break; - case 14: - name = "Makkah"; - break; - case 15: - name = "Al Hudud ash Shamaliyah"; - break; - case 16: - name = "Najran"; - break; - case 17: - name = "Jizan"; - break; - case 19: - name = "Tabuk"; - break; - case 20: - name = "Al Jawf"; - break; - } - } - if (country_code.equals("SB") == true) { - switch (region_code2) { - case 3: - name = "Malaita"; - break; - case 6: - name = "Guadalcanal"; - break; - case 7: - name = "Isabel"; - break; - case 8: - name = "Makira"; - break; - case 9: - name = "Temotu"; - break; - case 10: - name = "Central"; - break; - case 11: - name = "Western"; - break; - case 12: - name = "Choiseul"; - break; - case 13: - name = "Rennell and Bellona"; - break; - } - } - if (country_code.equals("SC") == true) { - switch (region_code2) { - case 1: - name = "Anse aux Pins"; - break; - case 2: - name = "Anse Boileau"; - break; - case 3: - name = "Anse Etoile"; - break; - case 4: - name = "Anse Louis"; - break; - case 5: - name = "Anse Royale"; - break; - case 6: - name = "Baie Lazare"; - break; - case 7: - name = "Baie Sainte Anne"; - break; - case 8: - name = "Beau Vallon"; - break; - case 9: - name = "Bel Air"; - break; - case 10: - name = "Bel Ombre"; - break; - case 11: - name = "Cascade"; - break; - case 12: - name = "Glacis"; - break; - case 13: - name = "Grand' Anse"; - break; - case 14: - name = "Grand' Anse"; - break; - case 15: - name = "La Digue"; - break; - case 16: - name = "La Riviere Anglaise"; - break; - case 17: - name = "Mont Buxton"; - break; - case 18: - name = "Mont Fleuri"; - break; - case 19: - name = "Plaisance"; - break; - case 20: - name = "Pointe La Rue"; - break; - case 21: - name = "Port Glaud"; - break; - case 22: - name = "Saint Louis"; - break; - case 23: - name = "Takamaka"; - break; - } - } - if (country_code.equals("SD") == true) { - switch (region_code2) { - case 27: - name = "Al Wusta"; - break; - case 28: - name = "Al Istiwa'iyah"; - break; - case 29: - name = "Al Khartum"; - break; - case 30: - name = "Ash Shamaliyah"; - break; - case 31: - name = "Ash Sharqiyah"; - break; - case 32: - name = "Bahr al Ghazal"; - break; - case 33: - name = "Darfur"; - break; - case 34: - name = "Kurdufan"; - break; - case 35: - name = "Upper Nile"; - break; - case 40: - name = "Al Wahadah State"; - break; - case 44: - name = "Central Equatoria State"; - break; - } - } - if (country_code.equals("SE") == true) { - switch (region_code2) { - case 1: - name = "Alvsborgs Lan"; - break; - case 2: - name = "Blekinge Lan"; - break; - case 3: - name = "Gavleborgs Lan"; - break; - case 4: - name = "Goteborgs och Bohus Lan"; - break; - case 5: - name = "Gotlands Lan"; - break; - case 6: - name = "Hallands Lan"; - break; - case 7: - name = "Jamtlands Lan"; - break; - case 8: - name = "Jonkopings Lan"; - break; - case 9: - name = "Kalmar Lan"; - break; - case 10: - name = "Dalarnas Lan"; - break; - case 11: - name = "Kristianstads Lan"; - break; - case 12: - name = "Kronobergs Lan"; - break; - case 13: - name = "Malmohus Lan"; - break; - case 14: - name = "Norrbottens Lan"; - break; - case 15: - name = "Orebro Lan"; - break; - case 16: - name = "Ostergotlands Lan"; - break; - case 17: - name = "Skaraborgs Lan"; - break; - case 18: - name = "Sodermanlands Lan"; - break; - case 21: - name = "Uppsala Lan"; - break; - case 22: - name = "Varmlands Lan"; - break; - case 23: - name = "Vasterbottens Lan"; - break; - case 24: - name = "Vasternorrlands Lan"; - break; - case 25: - name = "Vastmanlands Lan"; - break; - case 26: - name = "Stockholms Lan"; - break; - case 27: - name = "Skane Lan"; - break; - case 28: - name = "Vastra Gotaland"; - break; - } - } - if (country_code.equals("SH") == true) { - switch (region_code2) { - case 1: - name = "Ascension"; - break; - case 2: - name = "Saint Helena"; - break; - case 3: - name = "Tristan da Cunha"; - break; - } - } - if (country_code.equals("SI") == true) { - switch (region_code2) { - case 1: - name = "Ajdovscina"; - break; - case 2: - name = "Beltinci"; - break; - case 3: - name = "Bled"; - break; - case 4: - name = "Bohinj"; - break; - case 5: - name = "Borovnica"; - break; - case 6: - name = "Bovec"; - break; - case 7: - name = "Brda"; - break; - case 8: - name = "Brezice"; - break; - case 9: - name = "Brezovica"; - break; - case 11: - name = "Celje"; - break; - case 12: - name = "Cerklje na Gorenjskem"; - break; - case 13: - name = "Cerknica"; - break; - case 14: - name = "Cerkno"; - break; - case 15: - name = "Crensovci"; - break; - case 16: - name = "Crna na Koroskem"; - break; - case 17: - name = "Crnomelj"; - break; - case 19: - name = "Divaca"; - break; - case 20: - name = "Dobrepolje"; - break; - case 22: - name = "Dol pri Ljubljani"; - break; - case 24: - name = "Dornava"; - break; - case 25: - name = "Dravograd"; - break; - case 26: - name = "Duplek"; - break; - case 27: - name = "Gorenja Vas-Poljane"; - break; - case 28: - name = "Gorisnica"; - break; - case 29: - name = "Gornja Radgona"; - break; - case 30: - name = "Gornji Grad"; - break; - case 31: - name = "Gornji Petrovci"; - break; - case 32: - name = "Grosuplje"; - break; - case 34: - name = "Hrastnik"; - break; - case 35: - name = "Hrpelje-Kozina"; - break; - case 36: - name = "Idrija"; - break; - case 37: - name = "Ig"; - break; - case 38: - name = "Ilirska Bistrica"; - break; - case 39: - name = "Ivancna Gorica"; - break; - case 40: - name = "Izola-Isola"; - break; - case 42: - name = "Jursinci"; - break; - case 44: - name = "Kanal"; - break; - case 45: - name = "Kidricevo"; - break; - case 46: - name = "Kobarid"; - break; - case 47: - name = "Kobilje"; - break; - case 49: - name = "Komen"; - break; - case 50: - name = "Koper-Capodistria"; - break; - case 51: - name = "Kozje"; - break; - case 52: - name = "Kranj"; - break; - case 53: - name = "Kranjska Gora"; - break; - case 54: - name = "Krsko"; - break; - case 55: - name = "Kungota"; - break; - case 57: - name = "Lasko"; - break; - case 61: - name = "Ljubljana"; - break; - case 62: - name = "Ljubno"; - break; - case 64: - name = "Logatec"; - break; - case 66: - name = "Loski Potok"; - break; - case 68: - name = "Lukovica"; - break; - case 71: - name = "Medvode"; - break; - case 72: - name = "Menges"; - break; - case 73: - name = "Metlika"; - break; - case 74: - name = "Mezica"; - break; - case 76: - name = "Mislinja"; - break; - case 77: - name = "Moravce"; - break; - case 78: - name = "Moravske Toplice"; - break; - case 79: - name = "Mozirje"; - break; - case 80: - name = "Murska Sobota"; - break; - case 81: - name = "Muta"; - break; - case 82: - name = "Naklo"; - break; - case 83: - name = "Nazarje"; - break; - case 84: - name = "Nova Gorica"; - break; - case 86: - name = "Odranci"; - break; - case 87: - name = "Ormoz"; - break; - case 88: - name = "Osilnica"; - break; - case 89: - name = "Pesnica"; - break; - case 91: - name = "Pivka"; - break; - case 92: - name = "Podcetrtek"; - break; - case 94: - name = "Postojna"; - break; - case 97: - name = "Puconci"; - break; - case 98: - name = "Racam"; - break; - case 99: - name = "Radece"; - break; - case 832: - name = "Radenci"; - break; - case 833: - name = "Radlje ob Dravi"; - break; - case 834: - name = "Radovljica"; - break; - case 837: - name = "Rogasovci"; - break; - case 838: - name = "Rogaska Slatina"; - break; - case 839: - name = "Rogatec"; - break; - case 875: - name = "Semic"; - break; - case 876: - name = "Sencur"; - break; - case 877: - name = "Sentilj"; - break; - case 878: - name = "Sentjernej"; - break; - case 880: - name = "Sevnica"; - break; - case 881: - name = "Sezana"; - break; - case 882: - name = "Skocjan"; - break; - case 883: - name = "Skofja Loka"; - break; - case 918: - name = "Skofljica"; - break; - case 919: - name = "Slovenj Gradec"; - break; - case 921: - name = "Slovenske Konjice"; - break; - case 922: - name = "Smarje pri Jelsah"; - break; - case 923: - name = "Smartno ob Paki"; - break; - case 924: - name = "Sostanj"; - break; - case 925: - name = "Starse"; - break; - case 926: - name = "Store"; - break; - case 961: - name = "Sveti Jurij"; - break; - case 962: - name = "Tolmin"; - break; - case 963: - name = "Trbovlje"; - break; - case 964: - name = "Trebnje"; - break; - case 965: - name = "Trzic"; - break; - case 966: - name = "Turnisce"; - break; - case 967: - name = "Velenje"; - break; - case 968: - name = "Velike Lasce"; - break; - case 1004: - name = "Vipava"; - break; - case 1005: - name = "Vitanje"; - break; - case 1006: - name = "Vodice"; - break; - case 1008: - name = "Vrhnika"; - break; - case 1009: - name = "Vuzenica"; - break; - case 1010: - name = "Zagorje ob Savi"; - break; - case 1012: - name = "Zavrc"; - break; - case 1047: - name = "Zelezniki"; - break; - case 1048: - name = "Ziri"; - break; - case 1049: - name = "Zrece"; - break; - case 1093: - name = "Dobrova-Horjul-Polhov Gradec"; - break; - case 1096: - name = "Domzale"; - break; - case 1136: - name = "Jesenice"; - break; - case 1138: - name = "Kamnik"; - break; - case 1139: - name = "Kocevje"; - break; - case 1177: - name = "Kuzma"; - break; - case 1178: - name = "Lenart"; - break; - case 1180: - name = "Litija"; - break; - case 1181: - name = "Ljutomer"; - break; - case 1182: - name = "Loska Dolina"; - break; - case 1184: - name = "Luce"; - break; - case 1219: - name = "Majsperk"; - break; - case 1220: - name = "Maribor"; - break; - case 1223: - name = "Miren-Kostanjevica"; - break; - case 1225: - name = "Novo Mesto"; - break; - case 1227: - name = "Piran"; - break; - case 1266: - name = "Preddvor"; - break; - case 1268: - name = "Ptuj"; - break; - case 1305: - name = "Ribnica"; - break; - case 1307: - name = "Ruse"; - break; - case 1311: - name = "Sentjur pri Celju"; - break; - case 1312: - name = "Slovenska Bistrica"; - break; - case 1392: - name = "Videm"; - break; - case 1393: - name = "Vojnik"; - break; - case 1395: - name = "Zalec"; - break; - } - } - if (country_code.equals("SK") == true) { - switch (region_code2) { - case 1: - name = "Banska Bystrica"; - break; - case 2: - name = "Bratislava"; - break; - case 3: - name = "Kosice"; - break; - case 4: - name = "Nitra"; - break; - case 5: - name = "Presov"; - break; - case 6: - name = "Trencin"; - break; - case 7: - name = "Trnava"; - break; - case 8: - name = "Zilina"; - break; - } - } - if (country_code.equals("SL") == true) { - switch (region_code2) { - case 1: - name = "Eastern"; - break; - case 2: - name = "Northern"; - break; - case 3: - name = "Southern"; - break; - case 4: - name = "Western Area"; - break; - } - } - if (country_code.equals("SM") == true) { - switch (region_code2) { - case 1: - name = "Acquaviva"; - break; - case 2: - name = "Chiesanuova"; - break; - case 3: - name = "Domagnano"; - break; - case 4: - name = "Faetano"; - break; - case 5: - name = "Fiorentino"; - break; - case 6: - name = "Borgo Maggiore"; - break; - case 7: - name = "San Marino"; - break; - case 8: - name = "Monte Giardino"; - break; - case 9: - name = "Serravalle"; - break; - } - } - if (country_code.equals("SN") == true) { - switch (region_code2) { - case 1: - name = "Dakar"; - break; - case 3: - name = "Diourbel"; - break; - case 4: - name = "Saint-Louis"; - break; - case 5: - name = "Tambacounda"; - break; - case 7: - name = "Thies"; - break; - case 8: - name = "Louga"; - break; - case 9: - name = "Fatick"; - break; - case 10: - name = "Kaolack"; - break; - case 11: - name = "Kolda"; - break; - case 12: - name = "Ziguinchor"; - break; - case 13: - name = "Louga"; - break; - case 14: - name = "Saint-Louis"; - break; - case 15: - name = "Matam"; - break; - } - } - if (country_code.equals("SO") == true) { - switch (region_code2) { - case 1: - name = "Bakool"; - break; - case 2: - name = "Banaadir"; - break; - case 3: - name = "Bari"; - break; - case 4: - name = "Bay"; - break; - case 5: - name = "Galguduud"; - break; - case 6: - name = "Gedo"; - break; - case 7: - name = "Hiiraan"; - break; - case 8: - name = "Jubbada Dhexe"; - break; - case 9: - name = "Jubbada Hoose"; - break; - case 10: - name = "Mudug"; - break; - case 11: - name = "Nugaal"; - break; - case 12: - name = "Sanaag"; - break; - case 13: - name = "Shabeellaha Dhexe"; - break; - case 14: - name = "Shabeellaha Hoose"; - break; - case 16: - name = "Woqooyi Galbeed"; - break; - case 18: - name = "Nugaal"; - break; - case 19: - name = "Togdheer"; - break; - case 20: - name = "Woqooyi Galbeed"; - break; - case 21: - name = "Awdal"; - break; - case 22: - name = "Sool"; - break; - } - } - if (country_code.equals("SR") == true) { - switch (region_code2) { - case 10: - name = "Brokopondo"; - break; - case 11: - name = "Commewijne"; - break; - case 12: - name = "Coronie"; - break; - case 13: - name = "Marowijne"; - break; - case 14: - name = "Nickerie"; - break; - case 15: - name = "Para"; - break; - case 16: - name = "Paramaribo"; - break; - case 17: - name = "Saramacca"; - break; - case 18: - name = "Sipaliwini"; - break; - case 19: - name = "Wanica"; - break; - } - } - if (country_code.equals("ST") == true) { - switch (region_code2) { - case 1: - name = "Principe"; - break; - case 2: - name = "Sao Tome"; - break; - } - } - if (country_code.equals("SV") == true) { - switch (region_code2) { - case 1: - name = "Ahuachapan"; - break; - case 2: - name = "Cabanas"; - break; - case 3: - name = "Chalatenango"; - break; - case 4: - name = "Cuscatlan"; - break; - case 5: - name = "La Libertad"; - break; - case 6: - name = "La Paz"; - break; - case 7: - name = "La Union"; - break; - case 8: - name = "Morazan"; - break; - case 9: - name = "San Miguel"; - break; - case 10: - name = "San Salvador"; - break; - case 11: - name = "Santa Ana"; - break; - case 12: - name = "San Vicente"; - break; - case 13: - name = "Sonsonate"; - break; - case 14: - name = "Usulutan"; - break; - } - } - if (country_code.equals("SY") == true) { - switch (region_code2) { - case 1: - name = "Al Hasakah"; - break; - case 2: - name = "Al Ladhiqiyah"; - break; - case 3: - name = "Al Qunaytirah"; - break; - case 4: - name = "Ar Raqqah"; - break; - case 5: - name = "As Suwayda'"; - break; - case 6: - name = "Dar"; - break; - case 7: - name = "Dayr az Zawr"; - break; - case 8: - name = "Rif Dimashq"; - break; - case 9: - name = "Halab"; - break; - case 10: - name = "Hamah"; - break; - case 11: - name = "Hims"; - break; - case 12: - name = "Idlib"; - break; - case 13: - name = "Dimashq"; - break; - case 14: - name = "Tartus"; - break; - } - } - if (country_code.equals("SZ") == true) { - switch (region_code2) { - case 1: - name = "Hhohho"; - break; - case 2: - name = "Lubombo"; - break; - case 3: - name = "Manzini"; - break; - case 4: - name = "Shiselweni"; - break; - case 5: - name = "Praslin"; - break; - } - } - if (country_code.equals("TD") == true) { - switch (region_code2) { - case 1: - name = "Batha"; - break; - case 2: - name = "Biltine"; - break; - case 3: - name = "Borkou-Ennedi-Tibesti"; - break; - case 4: - name = "Chari-Baguirmi"; - break; - case 5: - name = "Guera"; - break; - case 6: - name = "Kanem"; - break; - case 7: - name = "Lac"; - break; - case 8: - name = "Logone Occidental"; - break; - case 9: - name = "Logone Oriental"; - break; - case 10: - name = "Mayo-Kebbi"; - break; - case 11: - name = "Moyen-Chari"; - break; - case 12: - name = "Ouaddai"; - break; - case 13: - name = "Salamat"; - break; - case 14: - name = "Tandjile"; - break; - } - } - if (country_code.equals("TG") == true) { - switch (region_code2) { - case 9: - name = "Lama-Kara"; - break; - case 18: - name = "Tsevie"; - break; - case 22: - name = "Centrale"; - break; - case 23: - name = "Kara"; - break; - case 24: - name = "Maritime"; - break; - case 25: - name = "Plateaux"; - break; - case 26: - name = "Savanes"; - break; - } - } - if (country_code.equals("TH") == true) { - switch (region_code2) { - case 1: - name = "Mae Hong Son"; - break; - case 2: - name = "Chiang Mai"; - break; - case 3: - name = "Chiang Rai"; - break; - case 4: - name = "Nan"; - break; - case 5: - name = "Lamphun"; - break; - case 6: - name = "Lampang"; - break; - case 7: - name = "Phrae"; - break; - case 8: - name = "Tak"; - break; - case 9: - name = "Sukhothai"; - break; - case 10: - name = "Uttaradit"; - break; - case 11: - name = "Kamphaeng Phet"; - break; - case 12: - name = "Phitsanulok"; - break; - case 13: - name = "Phichit"; - break; - case 14: - name = "Phetchabun"; - break; - case 15: - name = "Uthai Thani"; - break; - case 16: - name = "Nakhon Sawan"; - break; - case 17: - name = "Nong Khai"; - break; - case 18: - name = "Loei"; - break; - case 20: - name = "Sakon Nakhon"; - break; - case 21: - name = "Nakhon Phanom"; - break; - case 22: - name = "Khon Kaen"; - break; - case 23: - name = "Kalasin"; - break; - case 24: - name = "Maha Sarakham"; - break; - case 25: - name = "Roi Et"; - break; - case 26: - name = "Chaiyaphum"; - break; - case 27: - name = "Nakhon Ratchasima"; - break; - case 28: - name = "Buriram"; - break; - case 29: - name = "Surin"; - break; - case 30: - name = "Sisaket"; - break; - case 31: - name = "Narathiwat"; - break; - case 32: - name = "Chai Nat"; - break; - case 33: - name = "Sing Buri"; - break; - case 34: - name = "Lop Buri"; - break; - case 35: - name = "Ang Thong"; - break; - case 36: - name = "Phra Nakhon Si Ayutthaya"; - break; - case 37: - name = "Saraburi"; - break; - case 38: - name = "Nonthaburi"; - break; - case 39: - name = "Pathum Thani"; - break; - case 40: - name = "Krung Thep"; - break; - case 41: - name = "Phayao"; - break; - case 42: - name = "Samut Prakan"; - break; - case 43: - name = "Nakhon Nayok"; - break; - case 44: - name = "Chachoengsao"; - break; - case 45: - name = "Prachin Buri"; - break; - case 46: - name = "Chon Buri"; - break; - case 47: - name = "Rayong"; - break; - case 48: - name = "Chanthaburi"; - break; - case 49: - name = "Trat"; - break; - case 50: - name = "Kanchanaburi"; - break; - case 51: - name = "Suphan Buri"; - break; - case 52: - name = "Ratchaburi"; - break; - case 53: - name = "Nakhon Pathom"; - break; - case 54: - name = "Samut Songkhram"; - break; - case 55: - name = "Samut Sakhon"; - break; - case 56: - name = "Phetchaburi"; - break; - case 57: - name = "Prachuap Khiri Khan"; - break; - case 58: - name = "Chumphon"; - break; - case 59: - name = "Ranong"; - break; - case 60: - name = "Surat Thani"; - break; - case 61: - name = "Phangnga"; - break; - case 62: - name = "Phuket"; - break; - case 63: - name = "Krabi"; - break; - case 64: - name = "Nakhon Si Thammarat"; - break; - case 65: - name = "Trang"; - break; - case 66: - name = "Phatthalung"; - break; - case 67: - name = "Satun"; - break; - case 68: - name = "Songkhla"; - break; - case 69: - name = "Pattani"; - break; - case 70: - name = "Yala"; - break; - case 71: - name = "Ubon Ratchathani"; - break; - case 72: - name = "Yasothon"; - break; - case 73: - name = "Nakhon Phanom"; - break; - case 75: - name = "Ubon Ratchathani"; - break; - case 76: - name = "Udon Thani"; - break; - case 77: - name = "Amnat Charoen"; - break; - case 78: - name = "Mukdahan"; - break; - case 79: - name = "Nong Bua Lamphu"; - break; - case 80: - name = "Sa Kaeo"; - break; - } - } - if (country_code.equals("TJ") == true) { - switch (region_code2) { - case 1: - name = "Kuhistoni Badakhshon"; - break; - case 2: - name = "Khatlon"; - break; - case 3: - name = "Sughd"; - break; - } - } - if (country_code.equals("TM") == true) { - switch (region_code2) { - case 1: - name = "Ahal"; - break; - case 2: - name = "Balkan"; - break; - case 3: - name = "Dashoguz"; - break; - case 4: - name = "Lebap"; - break; - case 5: - name = "Mary"; - break; - } - } - if (country_code.equals("TN") == true) { - switch (region_code2) { - case 2: - name = "Kasserine"; - break; - case 3: - name = "Kairouan"; - break; - case 6: - name = "Jendouba"; - break; - case 14: - name = "El Kef"; - break; - case 15: - name = "Al Mahdia"; - break; - case 16: - name = "Al Munastir"; - break; - case 17: - name = "Bajah"; - break; - case 18: - name = "Bizerte"; - break; - case 19: - name = "Nabeul"; - break; - case 22: - name = "Siliana"; - break; - case 23: - name = "Sousse"; - break; - case 26: - name = "Ariana"; - break; - case 27: - name = "Ben Arous"; - break; - case 28: - name = "Madanin"; - break; - case 29: - name = "Gabes"; - break; - case 30: - name = "Gafsa"; - break; - case 31: - name = "Kebili"; - break; - case 32: - name = "Sfax"; - break; - case 33: - name = "Sidi Bou Zid"; - break; - case 34: - name = "Tataouine"; - break; - case 35: - name = "Tozeur"; - break; - case 36: - name = "Tunis"; - break; - case 37: - name = "Zaghouan"; - break; - case 38: - name = "Aiana"; - break; - case 39: - name = "Manouba"; - break; - } - } - if (country_code.equals("TO") == true) { - switch (region_code2) { - case 1: - name = "Ha"; - break; - case 2: - name = "Tongatapu"; - break; - case 3: - name = "Vava"; - break; - } - } - if (country_code.equals("TR") == true) { - switch (region_code2) { - case 2: - name = "Adiyaman"; - break; - case 3: - name = "Afyonkarahisar"; - break; - case 4: - name = "Agri"; - break; - case 5: - name = "Amasya"; - break; - case 7: - name = "Antalya"; - break; - case 8: - name = "Artvin"; - break; - case 9: - name = "Aydin"; - break; - case 10: - name = "Balikesir"; - break; - case 11: - name = "Bilecik"; - break; - case 12: - name = "Bingol"; - break; - case 13: - name = "Bitlis"; - break; - case 14: - name = "Bolu"; - break; - case 15: - name = "Burdur"; - break; - case 16: - name = "Bursa"; - break; - case 17: - name = "Canakkale"; - break; - case 19: - name = "Corum"; - break; - case 20: - name = "Denizli"; - break; - case 21: - name = "Diyarbakir"; - break; - case 22: - name = "Edirne"; - break; - case 23: - name = "Elazig"; - break; - case 24: - name = "Erzincan"; - break; - case 25: - name = "Erzurum"; - break; - case 26: - name = "Eskisehir"; - break; - case 28: - name = "Giresun"; - break; - case 31: - name = "Hatay"; - break; - case 32: - name = "Mersin"; - break; - case 33: - name = "Isparta"; - break; - case 34: - name = "Istanbul"; - break; - case 35: - name = "Izmir"; - break; - case 37: - name = "Kastamonu"; - break; - case 38: - name = "Kayseri"; - break; - case 39: - name = "Kirklareli"; - break; - case 40: - name = "Kirsehir"; - break; - case 41: - name = "Kocaeli"; - break; - case 43: - name = "Kutahya"; - break; - case 44: - name = "Malatya"; - break; - case 45: - name = "Manisa"; - break; - case 46: - name = "Kahramanmaras"; - break; - case 48: - name = "Mugla"; - break; - case 49: - name = "Mus"; - break; - case 50: - name = "Nevsehir"; - break; - case 52: - name = "Ordu"; - break; - case 53: - name = "Rize"; - break; - case 54: - name = "Sakarya"; - break; - case 55: - name = "Samsun"; - break; - case 57: - name = "Sinop"; - break; - case 58: - name = "Sivas"; - break; - case 59: - name = "Tekirdag"; - break; - case 60: - name = "Tokat"; - break; - case 61: - name = "Trabzon"; - break; - case 62: - name = "Tunceli"; - break; - case 63: - name = "Sanliurfa"; - break; - case 64: - name = "Usak"; - break; - case 65: - name = "Van"; - break; - case 66: - name = "Yozgat"; - break; - case 68: - name = "Ankara"; - break; - case 69: - name = "Gumushane"; - break; - case 70: - name = "Hakkari"; - break; - case 71: - name = "Konya"; - break; - case 72: - name = "Mardin"; - break; - case 73: - name = "Nigde"; - break; - case 74: - name = "Siirt"; - break; - case 75: - name = "Aksaray"; - break; - case 76: - name = "Batman"; - break; - case 77: - name = "Bayburt"; - break; - case 78: - name = "Karaman"; - break; - case 79: - name = "Kirikkale"; - break; - case 80: - name = "Sirnak"; - break; - case 81: - name = "Adana"; - break; - case 82: - name = "Cankiri"; - break; - case 83: - name = "Gaziantep"; - break; - case 84: - name = "Kars"; - break; - case 85: - name = "Zonguldak"; - break; - case 86: - name = "Ardahan"; - break; - case 87: - name = "Bartin"; - break; - case 88: - name = "Igdir"; - break; - case 89: - name = "Karabuk"; - break; - case 90: - name = "Kilis"; - break; - case 91: - name = "Osmaniye"; - break; - case 92: - name = "Yalova"; - break; - case 93: - name = "Duzce"; - break; - } - } - if (country_code.equals("TT") == true) { - switch (region_code2) { - case 1: - name = "Arima"; - break; - case 2: - name = "Caroni"; - break; - case 3: - name = "Mayaro"; - break; - case 4: - name = "Nariva"; - break; - case 5: - name = "Port-of-Spain"; - break; - case 6: - name = "Saint Andrew"; - break; - case 7: - name = "Saint David"; - break; - case 8: - name = "Saint George"; - break; - case 9: - name = "Saint Patrick"; - break; - case 10: - name = "San Fernando"; - break; - case 11: - name = "Tobago"; - break; - case 12: - name = "Victoria"; - break; - } - } - if (country_code.equals("TW") == true) { - switch (region_code2) { - case 1: - name = "Fu-chien"; - break; - case 2: - name = "Kao-hsiung"; - break; - case 3: - name = "T'ai-pei"; - break; - case 4: - name = "T'ai-wan"; - break; - } - } - if (country_code.equals("TZ") == true) { - switch (region_code2) { - case 2: - name = "Pwani"; - break; - case 3: - name = "Dodoma"; - break; - case 4: - name = "Iringa"; - break; - case 5: - name = "Kigoma"; - break; - case 6: - name = "Kilimanjaro"; - break; - case 7: - name = "Lindi"; - break; - case 8: - name = "Mara"; - break; - case 9: - name = "Mbeya"; - break; - case 10: - name = "Morogoro"; - break; - case 11: - name = "Mtwara"; - break; - case 12: - name = "Mwanza"; - break; - case 13: - name = "Pemba North"; - break; - case 14: - name = "Ruvuma"; - break; - case 15: - name = "Shinyanga"; - break; - case 16: - name = "Singida"; - break; - case 17: - name = "Tabora"; - break; - case 18: - name = "Tanga"; - break; - case 19: - name = "Kagera"; - break; - case 20: - name = "Pemba South"; - break; - case 21: - name = "Zanzibar Central"; - break; - case 22: - name = "Zanzibar North"; - break; - case 23: - name = "Dar es Salaam"; - break; - case 24: - name = "Rukwa"; - break; - case 25: - name = "Zanzibar Urban"; - break; - case 26: - name = "Arusha"; - break; - case 27: - name = "Manyara"; - break; - } - } - if (country_code.equals("UA") == true) { - switch (region_code2) { - case 1: - name = "Cherkas'ka Oblast'"; - break; - case 2: - name = "Chernihivs'ka Oblast'"; - break; - case 3: - name = "Chernivets'ka Oblast'"; - break; - case 4: - name = "Dnipropetrovs'ka Oblast'"; - break; - case 5: - name = "Donets'ka Oblast'"; - break; - case 6: - name = "Ivano-Frankivs'ka Oblast'"; - break; - case 7: - name = "Kharkivs'ka Oblast'"; - break; - case 8: - name = "Khersons'ka Oblast'"; - break; - case 9: - name = "Khmel'nyts'ka Oblast'"; - break; - case 10: - name = "Kirovohrads'ka Oblast'"; - break; - case 11: - name = "Krym"; - break; - case 12: - name = "Kyyiv"; - break; - case 13: - name = "Kyyivs'ka Oblast'"; - break; - case 14: - name = "Luhans'ka Oblast'"; - break; - case 15: - name = "L'vivs'ka Oblast'"; - break; - case 16: - name = "Mykolayivs'ka Oblast'"; - break; - case 17: - name = "Odes'ka Oblast'"; - break; - case 18: - name = "Poltavs'ka Oblast'"; - break; - case 19: - name = "Rivnens'ka Oblast'"; - break; - case 20: - name = "Sevastopol'"; - break; - case 21: - name = "Sums'ka Oblast'"; - break; - case 22: - name = "Ternopil's'ka Oblast'"; - break; - case 23: - name = "Vinnyts'ka Oblast'"; - break; - case 24: - name = "Volyns'ka Oblast'"; - break; - case 25: - name = "Zakarpats'ka Oblast'"; - break; - case 26: - name = "Zaporiz'ka Oblast'"; - break; - case 27: - name = "Zhytomyrs'ka Oblast'"; - break; - } - } - if (country_code.equals("UG") == true) { - switch (region_code2) { - case 5: - name = "Busoga"; - break; - case 8: - name = "Karamoja"; - break; - case 12: - name = "South Buganda"; - break; - case 18: - name = "Central"; - break; - case 20: - name = "Eastern"; - break; - case 21: - name = "Nile"; - break; - case 22: - name = "North Buganda"; - break; - case 23: - name = "Northern"; - break; - case 24: - name = "Southern"; - break; - case 25: - name = "Western"; - break; - case 33: - name = "Jinja"; - break; - case 36: - name = "Kalangala"; - break; - case 37: - name = "Kampala"; - break; - case 42: - name = "Kiboga"; - break; - case 52: - name = "Mbarara"; - break; - case 56: - name = "Mubende"; - break; - case 65: - name = "Adjumani"; - break; - case 66: - name = "Bugiri"; - break; - case 67: - name = "Busia"; - break; - case 69: - name = "Katakwi"; - break; - case 71: - name = "Masaka"; - break; - case 73: - name = "Nakasongola"; - break; - case 74: - name = "Sembabule"; - break; - case 77: - name = "Arua"; - break; - case 78: - name = "Iganga"; - break; - case 79: - name = "Kabarole"; - break; - case 80: - name = "Kaberamaido"; - break; - case 81: - name = "Kamwenge"; - break; - case 82: - name = "Kanungu"; - break; - case 83: - name = "Kayunga"; - break; - case 84: - name = "Kitgum"; - break; - case 85: - name = "Kyenjojo"; - break; - case 86: - name = "Mayuge"; - break; - case 87: - name = "Mbale"; - break; - case 88: - name = "Moroto"; - break; - case 89: - name = "Mpigi"; - break; - case 90: - name = "Mukono"; - break; - case 91: - name = "Nakapiripirit"; - break; - case 92: - name = "Pader"; - break; - case 93: - name = "Rukungiri"; - break; - case 94: - name = "Sironko"; - break; - case 95: - name = "Soroti"; - break; - case 96: - name = "Wakiso"; - break; - case 97: - name = "Yumbe"; - break; - } - } - if (country_code.equals("UY") == true) { - switch (region_code2) { - case 1: - name = "Artigas"; - break; - case 2: - name = "Canelones"; - break; - case 3: - name = "Cerro Largo"; - break; - case 4: - name = "Colonia"; - break; - case 5: - name = "Durazno"; - break; - case 6: - name = "Flores"; - break; - case 7: - name = "Florida"; - break; - case 8: - name = "Lavalleja"; - break; - case 9: - name = "Maldonado"; - break; - case 10: - name = "Montevideo"; - break; - case 11: - name = "Paysandu"; - break; - case 12: - name = "Rio Negro"; - break; - case 13: - name = "Rivera"; - break; - case 14: - name = "Rocha"; - break; - case 15: - name = "Salto"; - break; - case 16: - name = "San Jose"; - break; - case 17: - name = "Soriano"; - break; - case 18: - name = "Tacuarembo"; - break; - case 19: - name = "Treinta y Tres"; - break; - } - } - if (country_code.equals("UZ") == true) { - switch (region_code2) { - case 1: - name = "Andijon"; - break; - case 2: - name = "Bukhoro"; - break; - case 3: - name = "Farghona"; - break; - case 4: - name = "Jizzakh"; - break; - case 5: - name = "Khorazm"; - break; - case 6: - name = "Namangan"; - break; - case 7: - name = "Nawoiy"; - break; - case 8: - name = "Qashqadaryo"; - break; - case 9: - name = "Qoraqalpoghiston"; - break; - case 10: - name = "Samarqand"; - break; - case 11: - name = "Sirdaryo"; - break; - case 12: - name = "Surkhondaryo"; - break; - case 13: - name = "Toshkent"; - break; - case 14: - name = "Toshkent"; - break; - } - } - if (country_code.equals("VC") == true) { - switch (region_code2) { - case 1: - name = "Charlotte"; - break; - case 2: - name = "Saint Andrew"; - break; - case 3: - name = "Saint David"; - break; - case 4: - name = "Saint George"; - break; - case 5: - name = "Saint Patrick"; - break; - case 6: - name = "Grenadines"; - break; - } - } - if (country_code.equals("VE") == true) { - switch (region_code2) { - case 1: - name = "Amazonas"; - break; - case 2: - name = "Anzoategui"; - break; - case 3: - name = "Apure"; - break; - case 4: - name = "Aragua"; - break; - case 5: - name = "Barinas"; - break; - case 6: - name = "Bolivar"; - break; - case 7: - name = "Carabobo"; - break; - case 8: - name = "Cojedes"; - break; - case 9: - name = "Delta Amacuro"; - break; - case 11: - name = "Falcon"; - break; - case 12: - name = "Guarico"; - break; - case 13: - name = "Lara"; - break; - case 14: - name = "Merida"; - break; - case 15: - name = "Miranda"; - break; - case 16: - name = "Monagas"; - break; - case 17: - name = "Nueva Esparta"; - break; - case 18: - name = "Portuguesa"; - break; - case 19: - name = "Sucre"; - break; - case 20: - name = "Tachira"; - break; - case 21: - name = "Trujillo"; - break; - case 22: - name = "Yaracuy"; - break; - case 23: - name = "Zulia"; - break; - case 24: - name = "Dependencias Federales"; - break; - case 25: - name = "Distrito Federal"; - break; - case 26: - name = "Vargas"; - break; - } - } - if (country_code.equals("VN") == true) { - switch (region_code2) { - case 1: - name = "An Giang"; - break; - case 2: - name = "Bac Thai"; - break; - case 3: - name = "Ben Tre"; - break; - case 4: - name = "Binh Tri Thien"; - break; - case 5: - name = "Cao Bang"; - break; - case 6: - name = "Cuu Long"; - break; - case 7: - name = "Dac Lac"; - break; - case 9: - name = "Dong Thap"; - break; - case 11: - name = "Ha Bac"; - break; - case 12: - name = "Hai Hung"; - break; - case 13: - name = "Hai Phong"; - break; - case 14: - name = "Ha Nam Ninh"; - break; - case 15: - name = "Ha Noi"; - break; - case 16: - name = "Ha Son Binh"; - break; - case 17: - name = "Ha Tuyen"; - break; - case 19: - name = "Hoang Lien Son"; - break; - case 20: - name = "Ho Chi Minh"; - break; - case 21: - name = "Kien Giang"; - break; - case 22: - name = "Lai Chau"; - break; - case 23: - name = "Lam Dong"; - break; - case 24: - name = "Long An"; - break; - case 25: - name = "Minh Hai"; - break; - case 26: - name = "Nghe Tinh"; - break; - case 27: - name = "Nghia Binh"; - break; - case 28: - name = "Phu Khanh"; - break; - case 29: - name = "Quang Nam-Da Nang"; - break; - case 30: - name = "Quang Ninh"; - break; - case 31: - name = "Song Be"; - break; - case 32: - name = "Son La"; - break; - case 33: - name = "Tay Ninh"; - break; - case 34: - name = "Thanh Hoa"; - break; - case 35: - name = "Thai Binh"; - break; - case 36: - name = "Thuan Hai"; - break; - case 37: - name = "Tien Giang"; - break; - case 38: - name = "Vinh Phu"; - break; - case 39: - name = "Lang Son"; - break; - case 40: - name = "Dong Nai"; - break; - case 43: - name = "An Giang"; - break; - case 44: - name = "Dac Lac"; - break; - case 45: - name = "Dong Nai"; - break; - case 46: - name = "Dong Thap"; - break; - case 47: - name = "Kien Giang"; - break; - case 48: - name = "Minh Hai"; - break; - case 49: - name = "Song Be"; - break; - case 50: - name = "Vinh Phu"; - break; - case 51: - name = "Ha Noi"; - break; - case 52: - name = "Ho Chi Minh"; - break; - case 53: - name = "Ba Ria-Vung Tau"; - break; - case 54: - name = "Binh Dinh"; - break; - case 55: - name = "Binh Thuan"; - break; - case 56: - name = "Can Tho"; - break; - case 57: - name = "Gia Lai"; - break; - case 58: - name = "Ha Giang"; - break; - case 59: - name = "Ha Tay"; - break; - case 60: - name = "Ha Tinh"; - break; - case 61: - name = "Hoa Binh"; - break; - case 62: - name = "Khanh Hoa"; - break; - case 63: - name = "Kon Tum"; - break; - case 64: - name = "Quang Tri"; - break; - case 65: - name = "Nam Ha"; - break; - case 66: - name = "Nghe An"; - break; - case 67: - name = "Ninh Binh"; - break; - case 68: - name = "Ninh Thuan"; - break; - case 69: - name = "Phu Yen"; - break; - case 70: - name = "Quang Binh"; - break; - case 71: - name = "Quang Ngai"; - break; - case 72: - name = "Quang Tri"; - break; - case 73: - name = "Soc Trang"; - break; - case 74: - name = "Thua Thien"; - break; - case 75: - name = "Tra Vinh"; - break; - case 76: - name = "Tuyen Quang"; - break; - case 77: - name = "Vinh Long"; - break; - case 78: - name = "Da Nang"; - break; - case 79: - name = "Hai Duong"; - break; - case 80: - name = "Ha Nam"; - break; - case 81: - name = "Hung Yen"; - break; - case 82: - name = "Nam Dinh"; - break; - case 83: - name = "Phu Tho"; - break; - case 84: - name = "Quang Nam"; - break; - case 85: - name = "Thai Nguyen"; - break; - case 86: - name = "Vinh Puc Province"; - break; - case 87: - name = "Can Tho"; - break; - case 88: - name = "Dak Lak"; - break; - case 89: - name = "Lai Chau"; - break; - case 90: - name = "Lao Cai"; - break; - case 91: - name = "Dak Nong"; - break; - case 92: - name = "Dien Bien"; - break; - case 93: - name = "Hau Giang"; - break; - } - } - if (country_code.equals("VU") == true) { - switch (region_code2) { - case 5: - name = "Ambrym"; - break; - case 6: - name = "Aoba"; - break; - case 7: - name = "Torba"; - break; - case 8: - name = "Efate"; - break; - case 9: - name = "Epi"; - break; - case 10: - name = "Malakula"; - break; - case 11: - name = "Paama"; - break; - case 12: - name = "Pentecote"; - break; - case 13: - name = "Sanma"; - break; - case 14: - name = "Shepherd"; - break; - case 15: - name = "Tafea"; - break; - case 16: - name = "Malampa"; - break; - case 17: - name = "Penama"; - break; - case 18: - name = "Shefa"; - break; - } - } - if (country_code.equals("WS") == true) { - switch (region_code2) { - case 2: - name = "Aiga-i-le-Tai"; - break; - case 3: - name = "Atua"; - break; - case 4: - name = "Fa"; - break; - case 5: - name = "Gaga"; - break; - case 6: - name = "Va"; - break; - case 7: - name = "Gagaifomauga"; - break; - case 8: - name = "Palauli"; - break; - case 9: - name = "Satupa"; - break; - case 10: - name = "Tuamasaga"; - break; - case 11: - name = "Vaisigano"; - break; - } - } - if (country_code.equals("YE") == true) { - switch (region_code2) { - case 1: - name = "Abyan"; - break; - case 2: - name = "Adan"; - break; - case 3: - name = "Al Mahrah"; - break; - case 4: - name = "Hadramawt"; - break; - case 5: - name = "Shabwah"; - break; - case 6: - name = "Al Ghaydah"; - break; - case 8: - name = "Al Hudaydah"; - break; - case 10: - name = "Al Mahwit"; - break; - case 11: - name = "Dhamar"; - break; - case 14: - name = "Ma'rib"; - break; - case 15: - name = "Sa"; - break; - case 16: - name = "San"; - break; - case 20: - name = "Al Bayda'"; - break; - case 21: - name = "Al Jawf"; - break; - case 22: - name = "Hajjah"; - break; - case 23: - name = "Ibb"; - break; - case 24: - name = "Lahij"; - break; - case 25: - name = "Ta"; - break; - } - } - if (country_code.equals("ZA") == true) { - switch (region_code2) { - case 1: - name = "North-Western Province"; - break; - case 2: - name = "KwaZulu-Natal"; - break; - case 3: - name = "Free State"; - break; - case 5: - name = "Eastern Cape"; - break; - case 6: - name = "Gauteng"; - break; - case 7: - name = "Mpumalanga"; - break; - case 8: - name = "Northern Cape"; - break; - case 9: - name = "Limpopo"; - break; - case 10: - name = "North-West"; - break; - case 11: - name = "Western Cape"; - break; - } - } - if (country_code.equals("ZM") == true) { - switch (region_code2) { - case 1: - name = "Western"; - break; - case 2: - name = "Central"; - break; - case 3: - name = "Eastern"; - break; - case 4: - name = "Luapula"; - break; - case 5: - name = "Northern"; - break; - case 6: - name = "North-Western"; - break; - case 7: - name = "Southern"; - break; - case 8: - name = "Copperbelt"; - break; - case 9: - name = "Lusaka"; - break; - } - } - if (country_code.equals("ZW") == true) { - switch (region_code2) { - case 1: - name = "Manicaland"; - break; - case 2: - name = "Midlands"; - break; - case 3: - name = "Mashonaland Central"; - break; - case 4: - name = "Mashonaland East"; - break; - case 5: - name = "Mashonaland West"; - break; - case 6: - name = "Matabeleland North"; - break; - case 7: - name = "Matabeleland South"; - break; - case 8: - name = "Masvingo"; - break; - case 9: - name = "Bulawayo"; - break; - case 10: - name = "Harare"; - break; - } - } - return name; - } -} diff --git a/src/main/java/com/maxmind/geoip/timeZone.java b/src/main/java/com/maxmind/geoip/timeZone.java deleted file mode 100644 index c47802a8..00000000 --- a/src/main/java/com/maxmind/geoip/timeZone.java +++ /dev/null @@ -1,1403 +0,0 @@ -package com.maxmind.geoip; -// generated automatically from admin/generate_timeZone.pl -public class timeZone { - static public String timeZoneByCountryAndRegion(String country,String region) { - String timezone = null; - if (country == null) { - return null; - } - if (region == null) { - region = ""; - } - if (country.equals("US") == true) { - if (region.equals("AL") == true) { - timezone = "America/Chicago"; - } else if (region.equals("AK") == true) { - timezone = "America/Anchorage"; - } else if (region.equals("AZ") == true) { - timezone = "America/Phoenix"; - } else if (region.equals("AR") == true) { - timezone = "America/Chicago"; - } else if (region.equals("CA") == true) { - timezone = "America/Los_Angeles"; - } else if (region.equals("CO") == true) { - timezone = "America/Denver"; - } else if (region.equals("CT") == true) { - timezone = "America/New_York"; - } else if (region.equals("DE") == true) { - timezone = "America/New_York"; - } else if (region.equals("DC") == true) { - timezone = "America/New_York"; - } else if (region.equals("FL") == true) { - timezone = "America/New_York"; - } else if (region.equals("GA") == true) { - timezone = "America/New_York"; - } else if (region.equals("HI") == true) { - timezone = "Pacific/Honolulu"; - } else if (region.equals("ID") == true) { - timezone = "America/Denver"; - } else if (region.equals("IL") == true) { - timezone = "America/Chicago"; - } else if (region.equals("IN") == true) { - timezone = "America/Indianapolis"; - } else if (region.equals("IA") == true) { - timezone = "America/Chicago"; - } else if (region.equals("KS") == true) { - timezone = "America/Chicago"; - } else if (region.equals("KY") == true) { - timezone = "America/New_York"; - } else if (region.equals("LA") == true) { - timezone = "America/Chicago"; - } else if (region.equals("ME") == true) { - timezone = "America/New_York"; - } else if (region.equals("MD") == true) { - timezone = "America/New_York"; - } else if (region.equals("MA") == true) { - timezone = "America/New_York"; - } else if (region.equals("MI") == true) { - timezone = "America/New_York"; - } else if (region.equals("MN") == true) { - timezone = "America/Chicago"; - } else if (region.equals("MS") == true) { - timezone = "America/Chicago"; - } else if (region.equals("MO") == true) { - timezone = "America/Chicago"; - } else if (region.equals("MT") == true) { - timezone = "America/Denver"; - } else if (region.equals("NE") == true) { - timezone = "America/Chicago"; - } else if (region.equals("NV") == true) { - timezone = "America/Los_Angeles"; - } else if (region.equals("NH") == true) { - timezone = "America/New_York"; - } else if (region.equals("NJ") == true) { - timezone = "America/New_York"; - } else if (region.equals("NM") == true) { - timezone = "America/Denver"; - } else if (region.equals("NY") == true) { - timezone = "America/New_York"; - } else if (region.equals("NC") == true) { - timezone = "America/New_York"; - } else if (region.equals("ND") == true) { - timezone = "America/Chicago"; - } else if (region.equals("OH") == true) { - timezone = "America/New_York"; - } else if (region.equals("OK") == true) { - timezone = "America/Chicago"; - } else if (region.equals("OR") == true) { - timezone = "America/Los_Angeles"; - } else if (region.equals("PA") == true) { - timezone = "America/New_York"; - } else if (region.equals("RI") == true) { - timezone = "America/New_York"; - } else if (region.equals("SC") == true) { - timezone = "America/New_York"; - } else if (region.equals("SD") == true) { - timezone = "America/Chicago"; - } else if (region.equals("TN") == true) { - timezone = "America/Chicago"; - } else if (region.equals("TX") == true) { - timezone = "America/Chicago"; - } else if (region.equals("UT") == true) { - timezone = "America/Denver"; - } else if (region.equals("VT") == true) { - timezone = "America/New_York"; - } else if (region.equals("VA") == true) { - timezone = "America/New_York"; - } else if (region.equals("WA") == true) { - timezone = "America/Los_Angeles"; - } else if (region.equals("WV") == true) { - timezone = "America/New_York"; - } else if (region.equals("WI") == true) { - timezone = "America/Chicago"; - } else if (region.equals("WY") == true) { - timezone = "America/Denver"; - } - } else if (country.equals("CA") == true) { - if (region.equals("AB") == true) { - timezone = "America/Edmonton"; - } else if (region.equals("BC") == true) { - timezone = "America/Vancouver"; - } else if (region.equals("MB") == true) { - timezone = "America/Winnipeg"; - } else if (region.equals("NB") == true) { - timezone = "America/Halifax"; - } else if (region.equals("NL") == true) { - timezone = "America/St_Johns"; - } else if (region.equals("NT") == true) { - timezone = "America/Yellowknife"; - } else if (region.equals("NS") == true) { - timezone = "America/Halifax"; - } else if (region.equals("NU") == true) { - timezone = "America/Rankin_Inlet"; - } else if (region.equals("ON") == true) { - timezone = "America/Rainy_River"; - } else if (region.equals("PE") == true) { - timezone = "America/Halifax"; - } else if (region.equals("QC") == true) { - timezone = "America/Montreal"; - } else if (region.equals("SK") == true) { - timezone = "America/Regina"; - } else if (region.equals("YT") == true) { - timezone = "America/Whitehorse"; - } - } else if (country.equals("AU") == true) { - if (region.equals("01") == true) { - timezone = "Australia/Canberra"; - } else if (region.equals("02") == true) { - timezone = "Australia/NSW"; - } else if (region.equals("03") == true) { - timezone = "Australia/North"; - } else if (region.equals("04") == true) { - timezone = "Australia/Queensland"; - } else if (region.equals("05") == true) { - timezone = "Australia/South"; - } else if (region.equals("06") == true) { - timezone = "Australia/Tasmania"; - } else if (region.equals("07") == true) { - timezone = "Australia/Victoria"; - } else if (region.equals("08") == true) { - timezone = "Australia/West"; - } - } else if (country.equals("AS") == true) { - timezone = "US/Samoa"; - } else if (country.equals("CI") == true) { - timezone = "Africa/Abidjan"; - } else if (country.equals("GH") == true) { - timezone = "Africa/Accra"; - } else if (country.equals("DZ") == true) { - timezone = "Africa/Algiers"; - } else if (country.equals("ER") == true) { - timezone = "Africa/Asmera"; - } else if (country.equals("ML") == true) { - timezone = "Africa/Bamako"; - } else if (country.equals("CF") == true) { - timezone = "Africa/Bangui"; - } else if (country.equals("GM") == true) { - timezone = "Africa/Banjul"; - } else if (country.equals("GW") == true) { - timezone = "Africa/Bissau"; - } else if (country.equals("CG") == true) { - timezone = "Africa/Brazzaville"; - } else if (country.equals("BI") == true) { - timezone = "Africa/Bujumbura"; - } else if (country.equals("EG") == true) { - timezone = "Africa/Cairo"; - } else if (country.equals("MA") == true) { - timezone = "Africa/Casablanca"; - } else if (country.equals("GN") == true) { - timezone = "Africa/Conakry"; - } else if (country.equals("SN") == true) { - timezone = "Africa/Dakar"; - } else if (country.equals("DJ") == true) { - timezone = "Africa/Djibouti"; - } else if (country.equals("SL") == true) { - timezone = "Africa/Freetown"; - } else if (country.equals("BW") == true) { - timezone = "Africa/Gaborone"; - } else if (country.equals("ZW") == true) { - timezone = "Africa/Harare"; - } else if (country.equals("ZA") == true) { - timezone = "Africa/Johannesburg"; - } else if (country.equals("UG") == true) { - timezone = "Africa/Kampala"; - } else if (country.equals("SD") == true) { - timezone = "Africa/Khartoum"; - } else if (country.equals("RW") == true) { - timezone = "Africa/Kigali"; - } else if (country.equals("NG") == true) { - timezone = "Africa/Lagos"; - } else if (country.equals("GA") == true) { - timezone = "Africa/Libreville"; - } else if (country.equals("TG") == true) { - timezone = "Africa/Lome"; - } else if (country.equals("AO") == true) { - timezone = "Africa/Luanda"; - } else if (country.equals("ZM") == true) { - timezone = "Africa/Lusaka"; - } else if (country.equals("GQ") == true) { - timezone = "Africa/Malabo"; - } else if (country.equals("MZ") == true) { - timezone = "Africa/Maputo"; - } else if (country.equals("LS") == true) { - timezone = "Africa/Maseru"; - } else if (country.equals("SZ") == true) { - timezone = "Africa/Mbabane"; - } else if (country.equals("SO") == true) { - timezone = "Africa/Mogadishu"; - } else if (country.equals("LR") == true) { - timezone = "Africa/Monrovia"; - } else if (country.equals("KE") == true) { - timezone = "Africa/Nairobi"; - } else if (country.equals("TD") == true) { - timezone = "Africa/Ndjamena"; - } else if (country.equals("NE") == true) { - timezone = "Africa/Niamey"; - } else if (country.equals("MR") == true) { - timezone = "Africa/Nouakchott"; - } else if (country.equals("BF") == true) { - timezone = "Africa/Ouagadougou"; - } else if (country.equals("ST") == true) { - timezone = "Africa/Sao_Tome"; - } else if (country.equals("LY") == true) { - timezone = "Africa/Tripoli"; - } else if (country.equals("TN") == true) { - timezone = "Africa/Tunis"; - } else if (country.equals("AI") == true) { - timezone = "America/Anguilla"; - } else if (country.equals("AG") == true) { - timezone = "America/Antigua"; - } else if (country.equals("AW") == true) { - timezone = "America/Aruba"; - } else if (country.equals("BB") == true) { - timezone = "America/Barbados"; - } else if (country.equals("BZ") == true) { - timezone = "America/Belize"; - } else if (country.equals("CO") == true) { - timezone = "America/Bogota"; - } else if (country.equals("VE") == true) { - timezone = "America/Caracas"; - } else if (country.equals("KY") == true) { - timezone = "America/Cayman"; - } else if (country.equals("CR") == true) { - timezone = "America/Costa_Rica"; - } else if (country.equals("DM") == true) { - timezone = "America/Dominica"; - } else if (country.equals("SV") == true) { - timezone = "America/El_Salvador"; - } else if (country.equals("GD") == true) { - timezone = "America/Grenada"; - } else if (country.equals("FR") == true) { - timezone = "Europe/Paris"; - } else if (country.equals("GP") == true) { - timezone = "America/Guadeloupe"; - } else if (country.equals("GT") == true) { - timezone = "America/Guatemala"; - } else if (country.equals("GY") == true) { - timezone = "America/Guyana"; - } else if (country.equals("CU") == true) { - timezone = "America/Havana"; - } else if (country.equals("JM") == true) { - timezone = "America/Jamaica"; - } else if (country.equals("BO") == true) { - timezone = "America/La_Paz"; - } else if (country.equals("PE") == true) { - timezone = "America/Lima"; - } else if (country.equals("NI") == true) { - timezone = "America/Managua"; - } else if (country.equals("MQ") == true) { - timezone = "America/Martinique"; - } else if (country.equals("UY") == true) { - timezone = "America/Montevideo"; - } else if (country.equals("MS") == true) { - timezone = "America/Montserrat"; - } else if (country.equals("BS") == true) { - timezone = "America/Nassau"; - } else if (country.equals("PA") == true) { - timezone = "America/Panama"; - } else if (country.equals("SR") == true) { - timezone = "America/Paramaribo"; - } else if (country.equals("PR") == true) { - timezone = "America/Puerto_Rico"; - } else if (country.equals("KN") == true) { - timezone = "America/St_Kitts"; - } else if (country.equals("LC") == true) { - timezone = "America/St_Lucia"; - } else if (country.equals("VC") == true) { - timezone = "America/St_Vincent"; - } else if (country.equals("HN") == true) { - timezone = "America/Tegucigalpa"; - } else if (country.equals("YE") == true) { - timezone = "Asia/Aden"; - } else if (country.equals("JO") == true) { - timezone = "Asia/Amman"; - } else if (country.equals("TM") == true) { - timezone = "Asia/Ashgabat"; - } else if (country.equals("IQ") == true) { - timezone = "Asia/Baghdad"; - } else if (country.equals("BH") == true) { - timezone = "Asia/Bahrain"; - } else if (country.equals("AZ") == true) { - timezone = "Asia/Baku"; - } else if (country.equals("TH") == true) { - timezone = "Asia/Bangkok"; - } else if (country.equals("LB") == true) { - timezone = "Asia/Beirut"; - } else if (country.equals("KG") == true) { - timezone = "Asia/Bishkek"; - } else if (country.equals("BN") == true) { - timezone = "Asia/Brunei"; - } else if (country.equals("IN") == true) { - timezone = "Asia/Calcutta"; - } else if (country.equals("MN") == true) { - timezone = "Asia/Choibalsan"; - } else if (country.equals("LK") == true) { - timezone = "Asia/Colombo"; - } else if (country.equals("BD") == true) { - timezone = "Asia/Dhaka"; - } else if (country.equals("AE") == true) { - timezone = "Asia/Dubai"; - } else if (country.equals("TJ") == true) { - timezone = "Asia/Dushanbe"; - } else if (country.equals("HK") == true) { - timezone = "Asia/Hong_Kong"; - } else if (country.equals("TR") == true) { - timezone = "Asia/Istanbul"; - } else if (country.equals("IL") == true) { - timezone = "Asia/Jerusalem"; - } else if (country.equals("AF") == true) { - timezone = "Asia/Kabul"; - } else if (country.equals("PK") == true) { - timezone = "Asia/Karachi"; - } else if (country.equals("NP") == true) { - timezone = "Asia/Katmandu"; - } else if (country.equals("KW") == true) { - timezone = "Asia/Kuwait"; - } else if (country.equals("MO") == true) { - timezone = "Asia/Macao"; - } else if (country.equals("PH") == true) { - timezone = "Asia/Manila"; - } else if (country.equals("OM") == true) { - timezone = "Asia/Muscat"; - } else if (country.equals("CY") == true) { - timezone = "Asia/Nicosia"; - } else if (country.equals("KP") == true) { - timezone = "Asia/Pyongyang"; - } else if (country.equals("QA") == true) { - timezone = "Asia/Qatar"; - } else if (country.equals("MM") == true) { - timezone = "Asia/Rangoon"; - } else if (country.equals("SA") == true) { - timezone = "Asia/Riyadh"; - } else if (country.equals("KR") == true) { - timezone = "Asia/Seoul"; - } else if (country.equals("SG") == true) { - timezone = "Asia/Singapore"; - } else if (country.equals("TW") == true) { - timezone = "Asia/Taipei"; - } else if (country.equals("GE") == true) { - timezone = "Asia/Tbilisi"; - } else if (country.equals("BT") == true) { - timezone = "Asia/Thimphu"; - } else if (country.equals("JP") == true) { - timezone = "Asia/Tokyo"; - } else if (country.equals("LA") == true) { - timezone = "Asia/Vientiane"; - } else if (country.equals("AM") == true) { - timezone = "Asia/Yerevan"; - } else if (country.equals("BM") == true) { - timezone = "Atlantic/Bermuda"; - } else if (country.equals("CV") == true) { - timezone = "Atlantic/Cape_Verde"; - } else if (country.equals("FO") == true) { - timezone = "Atlantic/Faeroe"; - } else if (country.equals("IS") == true) { - timezone = "Atlantic/Reykjavik"; - } else if (country.equals("GS") == true) { - timezone = "Atlantic/South_Georgia"; - } else if (country.equals("SH") == true) { - timezone = "Atlantic/St_Helena"; - } else if (country.equals("CL") == true) { - timezone = "Chile/Continental"; - } else if (country.equals("NL") == true) { - timezone = "Europe/Amsterdam"; - } else if (country.equals("AD") == true) { - timezone = "Europe/Andorra"; - } else if (country.equals("GR") == true) { - timezone = "Europe/Athens"; - } else if (country.equals("YU") == true) { - timezone = "Europe/Belgrade"; - } else if (country.equals("DE") == true) { - timezone = "Europe/Berlin"; - } else if (country.equals("SK") == true) { - timezone = "Europe/Bratislava"; - } else if (country.equals("BE") == true) { - timezone = "Europe/Brussels"; - } else if (country.equals("RO") == true) { - timezone = "Europe/Bucharest"; - } else if (country.equals("HU") == true) { - timezone = "Europe/Budapest"; - } else if (country.equals("DK") == true) { - timezone = "Europe/Copenhagen"; - } else if (country.equals("IE") == true) { - timezone = "Europe/Dublin"; - } else if (country.equals("GI") == true) { - timezone = "Europe/Gibraltar"; - } else if (country.equals("FI") == true) { - timezone = "Europe/Helsinki"; - } else if (country.equals("SI") == true) { - timezone = "Europe/Ljubljana"; - } else if (country.equals("GB") == true) { - timezone = "Europe/London"; - } else if (country.equals("LU") == true) { - timezone = "Europe/Luxembourg"; - } else if (country.equals("MT") == true) { - timezone = "Europe/Malta"; - } else if (country.equals("BY") == true) { - timezone = "Europe/Minsk"; - } else if (country.equals("MC") == true) { - timezone = "Europe/Monaco"; - } else if (country.equals("NO") == true) { - timezone = "Europe/Oslo"; - } else if (country.equals("CZ") == true) { - timezone = "Europe/Prague"; - } else if (country.equals("LV") == true) { - timezone = "Europe/Riga"; - } else if (country.equals("IT") == true) { - timezone = "Europe/Rome"; - } else if (country.equals("SM") == true) { - timezone = "Europe/San_Marino"; - } else if (country.equals("BA") == true) { - timezone = "Europe/Sarajevo"; - } else if (country.equals("MK") == true) { - timezone = "Europe/Skopje"; - } else if (country.equals("BG") == true) { - timezone = "Europe/Sofia"; - } else if (country.equals("SE") == true) { - timezone = "Europe/Stockholm"; - } else if (country.equals("EE") == true) { - timezone = "Europe/Tallinn"; - } else if (country.equals("AL") == true) { - timezone = "Europe/Tirane"; - } else if (country.equals("LI") == true) { - timezone = "Europe/Vaduz"; - } else if (country.equals("VA") == true) { - timezone = "Europe/Vatican"; - } else if (country.equals("AT") == true) { - timezone = "Europe/Vienna"; - } else if (country.equals("LT") == true) { - timezone = "Europe/Vilnius"; - } else if (country.equals("PL") == true) { - timezone = "Europe/Warsaw"; - } else if (country.equals("HR") == true) { - timezone = "Europe/Zagreb"; - } else if (country.equals("IR") == true) { - timezone = "Asia/Tehran"; - } else if (country.equals("MG") == true) { - timezone = "Indian/Antananarivo"; - } else if (country.equals("CX") == true) { - timezone = "Indian/Christmas"; - } else if (country.equals("CC") == true) { - timezone = "Indian/Cocos"; - } else if (country.equals("KM") == true) { - timezone = "Indian/Comoro"; - } else if (country.equals("MV") == true) { - timezone = "Indian/Maldives"; - } else if (country.equals("MU") == true) { - timezone = "Indian/Mauritius"; - } else if (country.equals("YT") == true) { - timezone = "Indian/Mayotte"; - } else if (country.equals("RE") == true) { - timezone = "Indian/Reunion"; - } else if (country.equals("FJ") == true) { - timezone = "Pacific/Fiji"; - } else if (country.equals("TV") == true) { - timezone = "Pacific/Funafuti"; - } else if (country.equals("GU") == true) { - timezone = "Pacific/Guam"; - } else if (country.equals("NR") == true) { - timezone = "Pacific/Nauru"; - } else if (country.equals("NU") == true) { - timezone = "Pacific/Niue"; - } else if (country.equals("NF") == true) { - timezone = "Pacific/Norfolk"; - } else if (country.equals("PW") == true) { - timezone = "Pacific/Palau"; - } else if (country.equals("PN") == true) { - timezone = "Pacific/Pitcairn"; - } else if (country.equals("CK") == true) { - timezone = "Pacific/Rarotonga"; - } else if (country.equals("WS") == true) { - timezone = "Pacific/Samoa"; - } else if (country.equals("KI") == true) { - timezone = "Pacific/Tarawa"; - } else if (country.equals("TO") == true) { - timezone = "Pacific/Tongatapu"; - } else if (country.equals("WF") == true) { - timezone = "Pacific/Wallis"; - } else if (country.equals("TZ") == true) { - timezone = "Africa/Dar_es_Salaam"; - } else if (country.equals("VN") == true) { - timezone = "Asia/Phnom_Penh"; - } else if (country.equals("KH") == true) { - timezone = "Asia/Phnom_Penh"; - } else if (country.equals("CM") == true) { - timezone = "Africa/Lagos"; - } else if (country.equals("DO") == true) { - timezone = "America/Santo_Domingo"; - } else if (country.equals("ET") == true) { - timezone = "Africa/Addis_Ababa"; - } else if (country.equals("FX") == true) { - timezone = "Europe/Paris"; - } else if (country.equals("HT") == true) { - timezone = "America/Port-au-Prince"; - } else if (country.equals("CH") == true) { - timezone = "Europe/Zurich"; - } else if (country.equals("AN") == true) { - timezone = "America/Curacao"; - } else if (country.equals("BJ") == true) { - timezone = "Africa/Porto-Novo"; - } else if (country.equals("EH") == true) { - timezone = "Africa/El_Aaiun"; - } else if (country.equals("FK") == true) { - timezone = "Atlantic/Stanley"; - } else if (country.equals("GF") == true) { - timezone = "America/Cayenne"; - } else if (country.equals("IO") == true) { - timezone = "Indian/Chagos"; - } else if (country.equals("MD") == true) { - timezone = "Europe/Chisinau"; - } else if (country.equals("MP") == true) { - timezone = "Pacific/Saipan"; - } else if (country.equals("MW") == true) { - timezone = "Africa/Blantyre"; - } else if (country.equals("NA") == true) { - timezone = "Africa/Windhoek"; - } else if (country.equals("NC") == true) { - timezone = "Pacific/Noumea"; - } else if (country.equals("PG") == true) { - timezone = "Pacific/Port_Moresby"; - } else if (country.equals("PM") == true) { - timezone = "America/Miquelon"; - } else if (country.equals("PS") == true) { - timezone = "Asia/Gaza"; - } else if (country.equals("PY") == true) { - timezone = "America/Asuncion"; - } else if (country.equals("SB") == true) { - timezone = "Pacific/Guadalcanal"; - } else if (country.equals("SC") == true) { - timezone = "Indian/Mahe"; - } else if (country.equals("SJ") == true) { - timezone = "Arctic/Longyearbyen"; - } else if (country.equals("SY") == true) { - timezone = "Asia/Damascus"; - } else if (country.equals("TC") == true) { - timezone = "America/Grand_Turk"; - } else if (country.equals("TF") == true) { - timezone = "Indian/Kerguelen"; - } else if (country.equals("TK") == true) { - timezone = "Pacific/Fakaofo"; - } else if (country.equals("TT") == true) { - timezone = "America/Port_of_Spain"; - } else if (country.equals("VG") == true) { - timezone = "America/Tortola"; - } else if (country.equals("VI") == true) { - timezone = "America/St_Thomas"; - } else if (country.equals("VU") == true) { - timezone = "Pacific/Efate"; - } else if (country.equals("RS") == true) { - timezone = "Europe/Belgrade"; - } else if (country.equals("ME") == true) { - timezone = "Europe/Podgorica"; - } else if (country.equals("AX") == true) { - timezone = "Europe/Mariehamn"; - } else if (country.equals("GG") == true) { - timezone = "Europe/Guernsey"; - } else if (country.equals("IM") == true) { - timezone = "Europe/Isle_of_Man"; - } else if (country.equals("JE") == true) { - timezone = "Europe/Jersey"; - } else if (country.equals("BL") == true) { - timezone = "America/St_Barthelemy"; - } else if (country.equals("MF") == true) { - timezone = "America/Marigot"; - } else if (country.equals("AR") == true) { - if (region.equals("01") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("02") == true) { - timezone = "America/Argentina/Catamarca"; - } else if (region.equals("03") == true) { - timezone = "America/Argentina/Tucuman"; - } else if (region.equals("04") == true) { - timezone = "America/Argentina/Rio_Gallegos"; - } else if (region.equals("05") == true) { - timezone = "America/Argentina/Cordoba"; - } else if (region.equals("06") == true) { - timezone = "America/Argentina/Tucuman"; - } else if (region.equals("07") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("08") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("09") == true) { - timezone = "America/Argentina/Tucuman"; - } else if (region.equals("10") == true) { - timezone = "America/Argentina/Jujuy"; - } else if (region.equals("11") == true) { - timezone = "America/Argentina/San_Luis"; - } else if (region.equals("12") == true) { - timezone = "America/Argentina/La_Rioja"; - } else if (region.equals("13") == true) { - timezone = "America/Argentina/Mendoza"; - } else if (region.equals("14") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("15") == true) { - timezone = "America/Argentina/San_Luis"; - } else if (region.equals("16") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("17") == true) { - timezone = "America/Argentina/Salta"; - } else if (region.equals("18") == true) { - timezone = "America/Argentina/San_Juan"; - } else if (region.equals("19") == true) { - timezone = "America/Argentina/San_Luis"; - } else if (region.equals("20") == true) { - timezone = "America/Argentina/Rio_Gallegos"; - } else if (region.equals("21") == true) { - timezone = "America/Argentina/Buenos_Aires"; - } else if (region.equals("22") == true) { - timezone = "America/Argentina/Catamarca"; - } else if (region.equals("23") == true) { - timezone = "America/Argentina/Ushuaia"; - } else if (region.equals("24") == true) { - timezone = "America/Argentina/Tucuman"; - } - } else if (country.equals("BR") == true) { - if (region.equals("01") == true) { - timezone = "America/Rio_Branco"; - } else if (region.equals("02") == true) { - timezone = "America/Maceio"; - } else if (region.equals("03") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("04") == true) { - timezone = "America/Manaus"; - } else if (region.equals("05") == true) { - timezone = "America/Bahia"; - } else if (region.equals("06") == true) { - timezone = "America/Fortaleza"; - } else if (region.equals("07") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("08") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("11") == true) { - timezone = "America/Campo_Grande"; - } else if (region.equals("13") == true) { - timezone = "America/Belem"; - } else if (region.equals("14") == true) { - timezone = "America/Cuiaba"; - } else if (region.equals("15") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("16") == true) { - timezone = "America/Belem"; - } else if (region.equals("17") == true) { - timezone = "America/Recife"; - } else if (region.equals("18") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("20") == true) { - timezone = "America/Fortaleza"; - } else if (region.equals("21") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("22") == true) { - timezone = "America/Recife"; - } else if (region.equals("23") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("24") == true) { - timezone = "America/Porto_Velho"; - } else if (region.equals("25") == true) { - timezone = "America/Boa_Vista"; - } else if (region.equals("26") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("27") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("28") == true) { - timezone = "America/Maceio"; - } else if (region.equals("29") == true) { - timezone = "America/Sao_Paulo"; - } else if (region.equals("30") == true) { - timezone = "America/Recife"; - } else if (region.equals("31") == true) { - timezone = "America/Araguaina"; - } - } else if (country.equals("CD") == true) { - if (region.equals("02") == true) { - timezone = "Africa/Kinshasa"; - } else if (region.equals("05") == true) { - timezone = "Africa/Lubumbashi"; - } else if (region.equals("06") == true) { - timezone = "Africa/Kinshasa"; - } else if (region.equals("08") == true) { - timezone = "Africa/Kinshasa"; - } else if (region.equals("10") == true) { - timezone = "Africa/Lubumbashi"; - } else if (region.equals("11") == true) { - timezone = "Africa/Lubumbashi"; - } else if (region.equals("12") == true) { - timezone = "Africa/Lubumbashi"; - } - } else if (country.equals("CN") == true) { - if (region.equals("01") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("02") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("03") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("04") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("05") == true) { - timezone = "Asia/Harbin"; - } else if (region.equals("06") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("07") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("08") == true) { - timezone = "Asia/Harbin"; - } else if (region.equals("09") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("10") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("11") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("12") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("13") == true) { - timezone = "Asia/Urumqi"; - } else if (region.equals("14") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("15") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("16") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("18") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("19") == true) { - timezone = "Asia/Harbin"; - } else if (region.equals("20") == true) { - timezone = "Asia/Harbin"; - } else if (region.equals("21") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("22") == true) { - timezone = "Asia/Harbin"; - } else if (region.equals("23") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("24") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("25") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("26") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("28") == true) { - timezone = "Asia/Shanghai"; - } else if (region.equals("29") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("30") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("31") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("32") == true) { - timezone = "Asia/Chongqing"; - } else if (region.equals("33") == true) { - timezone = "Asia/Chongqing"; - } - } else if (country.equals("EC") == true) { - if (region.equals("01") == true) { - timezone = "Pacific/Galapagos"; - } else if (region.equals("02") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("03") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("04") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("05") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("06") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("07") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("08") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("09") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("10") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("11") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("12") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("13") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("14") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("15") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("17") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("18") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("19") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("20") == true) { - timezone = "America/Guayaquil"; - } else if (region.equals("22") == true) { - timezone = "America/Guayaquil"; - } - } else if (country.equals("ES") == true) { - if (region.equals("07") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("27") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("29") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("31") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("32") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("34") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("39") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("51") == true) { - timezone = "Africa/Ceuta"; - } else if (region.equals("52") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("53") == true) { - timezone = "Atlantic/Canary"; - } else if (region.equals("54") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("55") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("56") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("57") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("58") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("59") == true) { - timezone = "Europe/Madrid"; - } else if (region.equals("60") == true) { - timezone = "Europe/Madrid"; - } - } else if (country.equals("GL") == true) { - if (region.equals("01") == true) { - timezone = "America/Thule"; - } else if (region.equals("02") == true) { - timezone = "America/Godthab"; - } else if (region.equals("03") == true) { - timezone = "America/Godthab"; - } - } else if (country.equals("ID") == true) { - if (region.equals("01") == true) { - timezone = "Asia/Pontianak"; - } else if (region.equals("02") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("03") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("04") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("05") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("06") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("07") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("08") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("09") == true) { - timezone = "Asia/Jayapura"; - } else if (region.equals("10") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("11") == true) { - timezone = "Asia/Pontianak"; - } else if (region.equals("12") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("13") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("14") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("15") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("16") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("17") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("18") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("19") == true) { - timezone = "Asia/Pontianak"; - } else if (region.equals("20") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("21") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("22") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("23") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("24") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("25") == true) { - timezone = "Asia/Pontianak"; - } else if (region.equals("26") == true) { - timezone = "Asia/Pontianak"; - } else if (region.equals("30") == true) { - timezone = "Asia/Jakarta"; - } else if (region.equals("31") == true) { - timezone = "Asia/Makassar"; - } else if (region.equals("33") == true) { - timezone = "Asia/Jakarta"; - } - } else if (country.equals("KZ") == true) { - if (region.equals("01") == true) { - timezone = "Asia/Almaty"; - } else if (region.equals("02") == true) { - timezone = "Asia/Almaty"; - } else if (region.equals("03") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("04") == true) { - timezone = "Asia/Aqtobe"; - } else if (region.equals("05") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("06") == true) { - timezone = "Asia/Aqtau"; - } else if (region.equals("07") == true) { - timezone = "Asia/Oral"; - } else if (region.equals("08") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("09") == true) { - timezone = "Asia/Aqtau"; - } else if (region.equals("10") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("11") == true) { - timezone = "Asia/Almaty"; - } else if (region.equals("12") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("13") == true) { - timezone = "Asia/Aqtobe"; - } else if (region.equals("14") == true) { - timezone = "Asia/Qyzylorda"; - } else if (region.equals("15") == true) { - timezone = "Asia/Almaty"; - } else if (region.equals("16") == true) { - timezone = "Asia/Aqtobe"; - } else if (region.equals("17") == true) { - timezone = "Asia/Almaty"; - } - } else if (country.equals("MX") == true) { - if (region.equals("01") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("02") == true) { - timezone = "America/Tijuana"; - } else if (region.equals("03") == true) { - timezone = "America/Hermosillo"; - } else if (region.equals("04") == true) { - timezone = "America/Merida"; - } else if (region.equals("05") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("06") == true) { - timezone = "America/Chihuahua"; - } else if (region.equals("07") == true) { - timezone = "America/Monterrey"; - } else if (region.equals("08") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("09") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("10") == true) { - timezone = "America/Mazatlan"; - } else if (region.equals("11") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("12") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("13") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("14") == true) { - timezone = "America/Mazatlan"; - } else if (region.equals("15") == true) { - timezone = "America/Chihuahua"; - } else if (region.equals("16") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("17") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("18") == true) { - timezone = "America/Mazatlan"; - } else if (region.equals("19") == true) { - timezone = "America/Monterrey"; - } else if (region.equals("20") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("21") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("22") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("23") == true) { - timezone = "America/Cancun"; - } else if (region.equals("24") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("25") == true) { - timezone = "America/Mazatlan"; - } else if (region.equals("26") == true) { - timezone = "America/Hermosillo"; - } else if (region.equals("27") == true) { - timezone = "America/Merida"; - } else if (region.equals("28") == true) { - timezone = "America/Monterrey"; - } else if (region.equals("29") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("30") == true) { - timezone = "America/Mexico_City"; - } else if (region.equals("31") == true) { - timezone = "America/Merida"; - } else if (region.equals("32") == true) { - timezone = "America/Monterrey"; - } - } else if (country.equals("MY") == true) { - if (region.equals("01") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("02") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("03") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("04") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("05") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("06") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("07") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("08") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("09") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("11") == true) { - timezone = "Asia/Kuching"; - } else if (region.equals("12") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("13") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("14") == true) { - timezone = "Asia/Kuala_Lumpur"; - } else if (region.equals("15") == true) { - timezone = "Asia/Kuching"; - } else if (region.equals("16") == true) { - timezone = "Asia/Kuching"; - } - } else if (country.equals("NZ") == true) { - if (region.equals("85") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("E7") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("E8") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("E9") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F1") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F2") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F3") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F4") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F5") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F7") == true) { - timezone = "Pacific/Chatham"; - } else if (region.equals("F8") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("F9") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("G1") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("G2") == true) { - timezone = "Pacific/Auckland"; - } else if (region.equals("G3") == true) { - timezone = "Pacific/Auckland"; - } - } else if (country.equals("PT") == true) { - if (region.equals("02") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("03") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("04") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("05") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("06") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("07") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("08") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("09") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("10") == true) { - timezone = "Atlantic/Madeira"; - } else if (region.equals("11") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("13") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("14") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("16") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("17") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("18") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("19") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("20") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("21") == true) { - timezone = "Europe/Lisbon"; - } else if (region.equals("22") == true) { - timezone = "Europe/Lisbon"; - } - } else if (country.equals("RU") == true) { - if (region.equals("01") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("02") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("03") == true) { - timezone = "Asia/Novokuznetsk"; - } else if (region.equals("04") == true) { - timezone = "Asia/Novosibirsk"; - } else if (region.equals("05") == true) { - timezone = "Asia/Vladivostok"; - } else if (region.equals("06") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("07") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("08") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("09") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("10") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("11") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("13") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("14") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("15") == true) { - timezone = "Asia/Anadyr"; - } else if (region.equals("16") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("17") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("18") == true) { - timezone = "Asia/Krasnoyarsk"; - } else if (region.equals("20") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("21") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("22") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("23") == true) { - timezone = "Europe/Kaliningrad"; - } else if (region.equals("24") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("25") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("26") == true) { - timezone = "Asia/Kamchatka"; - } else if (region.equals("27") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("28") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("29") == true) { - timezone = "Asia/Novokuznetsk"; - } else if (region.equals("30") == true) { - timezone = "Asia/Vladivostok"; - } else if (region.equals("31") == true) { - timezone = "Asia/Krasnoyarsk"; - } else if (region.equals("32") == true) { - timezone = "Asia/Omsk"; - } else if (region.equals("33") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("34") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("35") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("36") == true) { - timezone = "Asia/Anadyr"; - } else if (region.equals("37") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("38") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("39") == true) { - timezone = "Asia/Krasnoyarsk"; - } else if (region.equals("40") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("41") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("42") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("43") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("44") == true) { - timezone = "Asia/Magadan"; - } else if (region.equals("45") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("46") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("47") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("48") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("49") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("50") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("51") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("52") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("53") == true) { - timezone = "Asia/Novosibirsk"; - } else if (region.equals("54") == true) { - timezone = "Asia/Omsk"; - } else if (region.equals("55") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("56") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("57") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("58") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("59") == true) { - timezone = "Asia/Vladivostok"; - } else if (region.equals("60") == true) { - timezone = "Europe/Kaliningrad"; - } else if (region.equals("61") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("62") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("63") == true) { - timezone = "Asia/Yakutsk"; - } else if (region.equals("64") == true) { - timezone = "Asia/Sakhalin"; - } else if (region.equals("65") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("66") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("67") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("68") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("69") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("70") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("71") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("72") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("73") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("74") == true) { - timezone = "Asia/Krasnoyarsk"; - } else if (region.equals("75") == true) { - timezone = "Asia/Novosibirsk"; - } else if (region.equals("76") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("77") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("78") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("79") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("80") == true) { - timezone = "Asia/Yekaterinburg"; - } else if (region.equals("81") == true) { - timezone = "Europe/Samara"; - } else if (region.equals("82") == true) { - timezone = "Asia/Irkutsk"; - } else if (region.equals("83") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("84") == true) { - timezone = "Europe/Volgograd"; - } else if (region.equals("85") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("86") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("87") == true) { - timezone = "Asia/Novosibirsk"; - } else if (region.equals("88") == true) { - timezone = "Europe/Moscow"; - } else if (region.equals("89") == true) { - timezone = "Asia/Vladivostok"; - } - } else if (country.equals("UA") == true) { - if (region.equals("01") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("02") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("03") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("04") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("05") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("06") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("07") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("08") == true) { - timezone = "Europe/Simferopol"; - } else if (region.equals("09") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("10") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("11") == true) { - timezone = "Europe/Simferopol"; - } else if (region.equals("13") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("14") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("15") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("16") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("17") == true) { - timezone = "Europe/Simferopol"; - } else if (region.equals("18") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("19") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("20") == true) { - timezone = "Europe/Simferopol"; - } else if (region.equals("21") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("22") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("23") == true) { - timezone = "Europe/Kiev"; - } else if (region.equals("24") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("25") == true) { - timezone = "Europe/Uzhgorod"; - } else if (region.equals("26") == true) { - timezone = "Europe/Zaporozhye"; - } else if (region.equals("27") == true) { - timezone = "Europe/Kiev"; - } - } else if (country.equals("UZ") == true) { - if (region.equals("01") == true) { - timezone = "Asia/Tashkent"; - } else if (region.equals("02") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("03") == true) { - timezone = "Asia/Tashkent"; - } else if (region.equals("06") == true) { - timezone = "Asia/Tashkent"; - } else if (region.equals("07") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("08") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("09") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("10") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("12") == true) { - timezone = "Asia/Samarkand"; - } else if (region.equals("13") == true) { - timezone = "Asia/Tashkent"; - } else if (region.equals("14") == true) { - timezone = "Asia/Tashkent"; - } - } else if (country.equals("TL") == true) { - timezone = "Asia/Dili"; - } else if (country.equals("PF") == true) { - timezone = "Pacific/Marquesas"; - } - return timezone; - } -} diff --git a/src/main/java/com/sun/mail/auth/MD4.java b/src/main/java/com/sun/mail/auth/MD4.java deleted file mode 100644 index a3cfcecb..00000000 --- a/src/main/java/com/sun/mail/auth/MD4.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -/* - * Copied from OpenJDK with permission. - */ - -package com.sun.mail.auth; - -import java.security.*; - -//import static sun.security.provider.ByteArrayAccess.*; - -/** - * The MD4 class is used to compute an MD4 message digest over a given - * buffer of bytes. It is an implementation of the RSA Data Security Inc - * MD4 algorithim as described in internet RFC 1320. - * - * @author Andreas Sterbenz - * @author Bill Shannon (adapted for JavaMail) - */ -public final class MD4 { - - // state of this object - private final int[] state; - // temporary buffer, used by implCompress() - private final int[] x; - - // size of the input to the compression function in bytes - private static final int blockSize = 64; - - // buffer to store partial blocks, blockSize bytes large - private final byte[] buffer = new byte[blockSize]; - // offset into buffer - private int bufOfs; - - // number of bytes processed so far. - // also used as a flag to indicate reset status - // -1: need to call engineReset() before next call to update() - // 0: is already reset - private long bytesProcessed; - - // rotation constants - private static final int S11 = 3; - private static final int S12 = 7; - private static final int S13 = 11; - private static final int S14 = 19; - private static final int S21 = 3; - private static final int S22 = 5; - private static final int S23 = 9; - private static final int S24 = 13; - private static final int S31 = 3; - private static final int S32 = 9; - private static final int S33 = 11; - private static final int S34 = 15; - - private static final byte[] padding; - - static { - padding = new byte[136]; - padding[0] = (byte)0x80; - } - - // Standard constructor, creates a new MD4 instance. - public MD4() { - state = new int[4]; - x = new int[16]; - implReset(); - } - - /** - * Compute and return the message digest of the input byte array. - */ - public byte[] digest(byte[] in) { - implReset(); - engineUpdate(in, 0, in.length); - byte[] out = new byte[16]; - implDigest(out, 0); - return out; - } - - /** - * Reset the state of this object. - */ - private void implReset() { - // Load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; - bufOfs = 0; - bytesProcessed = 0; - } - - /** - * Perform the final computations, any buffered bytes are added - * to the digest, the count is added to the digest, and the resulting - * digest is stored. - */ - private void implDigest(byte[] out, int ofs) { - long bitsProcessed = bytesProcessed << 3; - - int index = (int)bytesProcessed & 0x3f; - int padLen = (index < 56) ? (56 - index) : (120 - index); - engineUpdate(padding, 0, padLen); - - //i2bLittle4((int)bitsProcessed, buffer, 56); - //i2bLittle4((int)(bitsProcessed >>> 32), buffer, 60); - buffer[56] = (byte)bitsProcessed; - buffer[57] = (byte)(bitsProcessed>>8); - buffer[58] = (byte)(bitsProcessed>>16); - buffer[59] = (byte)(bitsProcessed>>24); - buffer[60] = (byte)(bitsProcessed>>32); - buffer[61] = (byte)(bitsProcessed>>40); - buffer[62] = (byte)(bitsProcessed>>48); - buffer[63] = (byte)(bitsProcessed>>56); - implCompress(buffer, 0); - - //i2bLittle(state, 0, out, ofs, 16); - for (int i = 0; i < state.length; i++) { - int x = state[i]; - out[ofs++] = (byte)x; - out[ofs++] = (byte)(x>>8); - out[ofs++] = (byte)(x>>16); - out[ofs++] = (byte)(x>>24); - } - } - - private void engineUpdate(byte[] b, int ofs, int len) { - if (len == 0) { - return; - } - if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) { - throw new ArrayIndexOutOfBoundsException(); - } - if (bytesProcessed < 0) { - implReset(); - } - bytesProcessed += len; - // if buffer is not empty, we need to fill it before proceeding - if (bufOfs != 0) { - int n = Math.min(len, blockSize - bufOfs); - System.arraycopy(b, ofs, buffer, bufOfs, n); - bufOfs += n; - ofs += n; - len -= n; - if (bufOfs >= blockSize) { - // compress completed block now - implCompress(buffer, 0); - bufOfs = 0; - } - } - // compress complete blocks - while (len >= blockSize) { - implCompress(b, ofs); - len -= blockSize; - ofs += blockSize; - } - // copy remainder to buffer - if (len > 0) { - System.arraycopy(b, ofs, buffer, 0, len); - bufOfs = len; - } - } - - private static int FF(int a, int b, int c, int d, int x, int s) { - a += ((b & c) | ((~b) & d)) + x; - return ((a << s) | (a >>> (32 - s))); - } - - private static int GG(int a, int b, int c, int d, int x, int s) { - a += ((b & c) | (b & d) | (c & d)) + x + 0x5a827999; - return ((a << s) | (a >>> (32 - s))); - } - - private static int HH(int a, int b, int c, int d, int x, int s) { - a += ((b ^ c) ^ d) + x + 0x6ed9eba1; - return ((a << s) | (a >>> (32 - s))); - } - - /** - * This is where the functions come together as the generic MD4 - * transformation operation. It consumes 64 - * bytes from the buffer, beginning at the specified offset. - */ - private void implCompress(byte[] buf, int ofs) { - //b2iLittle64(buf, ofs, x); - for (int xfs = 0; xfs < x.length; xfs++) { - x[xfs] = (buf[ofs] & 0xff) | ((buf[ofs+1] & 0xff) << 8) | - ((buf[ofs+2] & 0xff) << 16) | ((buf[ofs+3] & 0xff) << 24); - ofs += 4; - } - - int a = state[0]; - int b = state[1]; - int c = state[2]; - int d = state[3]; - - /* Round 1 */ - a = FF (a, b, c, d, x[ 0], S11); /* 1 */ - d = FF (d, a, b, c, x[ 1], S12); /* 2 */ - c = FF (c, d, a, b, x[ 2], S13); /* 3 */ - b = FF (b, c, d, a, x[ 3], S14); /* 4 */ - a = FF (a, b, c, d, x[ 4], S11); /* 5 */ - d = FF (d, a, b, c, x[ 5], S12); /* 6 */ - c = FF (c, d, a, b, x[ 6], S13); /* 7 */ - b = FF (b, c, d, a, x[ 7], S14); /* 8 */ - a = FF (a, b, c, d, x[ 8], S11); /* 9 */ - d = FF (d, a, b, c, x[ 9], S12); /* 10 */ - c = FF (c, d, a, b, x[10], S13); /* 11 */ - b = FF (b, c, d, a, x[11], S14); /* 12 */ - a = FF (a, b, c, d, x[12], S11); /* 13 */ - d = FF (d, a, b, c, x[13], S12); /* 14 */ - c = FF (c, d, a, b, x[14], S13); /* 15 */ - b = FF (b, c, d, a, x[15], S14); /* 16 */ - - /* Round 2 */ - a = GG (a, b, c, d, x[ 0], S21); /* 17 */ - d = GG (d, a, b, c, x[ 4], S22); /* 18 */ - c = GG (c, d, a, b, x[ 8], S23); /* 19 */ - b = GG (b, c, d, a, x[12], S24); /* 20 */ - a = GG (a, b, c, d, x[ 1], S21); /* 21 */ - d = GG (d, a, b, c, x[ 5], S22); /* 22 */ - c = GG (c, d, a, b, x[ 9], S23); /* 23 */ - b = GG (b, c, d, a, x[13], S24); /* 24 */ - a = GG (a, b, c, d, x[ 2], S21); /* 25 */ - d = GG (d, a, b, c, x[ 6], S22); /* 26 */ - c = GG (c, d, a, b, x[10], S23); /* 27 */ - b = GG (b, c, d, a, x[14], S24); /* 28 */ - a = GG (a, b, c, d, x[ 3], S21); /* 29 */ - d = GG (d, a, b, c, x[ 7], S22); /* 30 */ - c = GG (c, d, a, b, x[11], S23); /* 31 */ - b = GG (b, c, d, a, x[15], S24); /* 32 */ - - /* Round 3 */ - a = HH (a, b, c, d, x[ 0], S31); /* 33 */ - d = HH (d, a, b, c, x[ 8], S32); /* 34 */ - c = HH (c, d, a, b, x[ 4], S33); /* 35 */ - b = HH (b, c, d, a, x[12], S34); /* 36 */ - a = HH (a, b, c, d, x[ 2], S31); /* 37 */ - d = HH (d, a, b, c, x[10], S32); /* 38 */ - c = HH (c, d, a, b, x[ 6], S33); /* 39 */ - b = HH (b, c, d, a, x[14], S34); /* 40 */ - a = HH (a, b, c, d, x[ 1], S31); /* 41 */ - d = HH (d, a, b, c, x[ 9], S32); /* 42 */ - c = HH (c, d, a, b, x[ 5], S33); /* 43 */ - b = HH (b, c, d, a, x[13], S34); /* 44 */ - a = HH (a, b, c, d, x[ 3], S31); /* 45 */ - d = HH (d, a, b, c, x[11], S32); /* 46 */ - c = HH (c, d, a, b, x[ 7], S33); /* 47 */ - b = HH (b, c, d, a, x[15], S34); /* 48 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - } -} diff --git a/src/main/java/com/sun/mail/auth/Ntlm.java b/src/main/java/com/sun/mail/auth/Ntlm.java deleted file mode 100644 index 0d3d70b5..00000000 --- a/src/main/java/com/sun/mail/auth/Ntlm.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2005-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -/* - * Copied from OpenJDK with permission. - */ - -package com.sun.mail.auth; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.io.PrintStream; -import java.security.GeneralSecurityException; -import java.security.NoSuchAlgorithmException; -import java.util.Locale; -import java.util.logging.Level; -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESKeySpec; - -import com.sun.mail.util.BASE64DecoderStream; -import com.sun.mail.util.BASE64EncoderStream; -import com.sun.mail.util.MailLogger; - - -/** - * NTLMAuthentication: - * - * @author Michael McMahon - * @author Bill Shannon (adapted for JavaMail) - */ -public class Ntlm { - - private byte[] type1; - private byte[] type3; - - private SecretKeyFactory fac; - private Cipher cipher; - private MD4 md4; - private String hostname; - private String ntdomain; - private String username; - private String password; - - private MailLogger logger; - - private void init0() { - type1 = new byte[256]; - type3 = new byte[256]; - System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,1}, 0, - type1, 0, 9); - type1[12] = (byte) 3; - type1[13] = (byte) 0xb2; - type1[28] = (byte) 0x20; - System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,3}, 0, - type3, 0, 9); - type3[12] = (byte) 0x18; - type3[14] = (byte) 0x18; - type3[20] = (byte) 0x18; - type3[22] = (byte) 0x18; - type3[32] = (byte) 0x40; - type3[60] = (byte) 1; - type3[61] = (byte) 0x82; - - try { - fac = SecretKeyFactory.getInstance("DES"); - cipher = Cipher.getInstance("DES/ECB/NoPadding"); - md4 = new MD4(); - } catch (NoSuchPaddingException e) { - assert false; - } catch (NoSuchAlgorithmException e) { - assert false; - } - }; - - /** - * Create an NTLM authenticator. - * Username may be specified as domain\\username in the Authenticator. - * If this notation is not used, then the domain will be taken - * from the ntdomain parameter. - */ - public Ntlm(String ntdomain, String hostname, String username, - String password, MailLogger logger) { - int i = hostname.indexOf('.'); - if (i != -1) { - hostname = hostname.substring(0, i); - } - i = username.indexOf('\\'); - if (i != -1) { - ntdomain = username.substring(0, i).toUpperCase(); - username = username.substring(i+1); - } else if (ntdomain == null) { - ntdomain = ""; - } - this.ntdomain = ntdomain; - this.hostname = hostname; - this.username = username; - this.password = password; - this.logger = logger.getLogger(this.getClass(), "DEBUG NTLM"); - init0(); - } - - private void copybytes(byte[] dest, int destpos, String src, String enc) { - try { - byte[] x = src.getBytes(enc); - System.arraycopy(x, 0, dest, destpos, x.length); - } catch (UnsupportedEncodingException e) { - assert false; - } - } - - public String generateType1Msg(int flags) { - // XXX - should set "flags" in generated message - int dlen = ntdomain.length(); - type1[16]= (byte) (dlen % 256); - type1[17]= (byte) (dlen / 256); - type1[18] = type1[16]; - type1[19] = type1[17]; - if (dlen == 0) - type1[13] &= ~0x10; - - int hlen = hostname.length(); - type1[24]= (byte) (hlen % 256); - type1[25]= (byte) (hlen / 256); - type1[26] = type1[24]; - type1[27] = type1[25]; - - copybytes(type1, 32, hostname, "iso-8859-1"); - copybytes(type1, hlen+32, ntdomain, "iso-8859-1"); - type1[20] = (byte) ((hlen+32) % 256); - type1[21] = (byte) ((hlen+32) / 256); - - byte[] msg = new byte[32 + hlen + dlen]; - System.arraycopy(type1, 0, msg, 0, 32 + hlen + dlen); - if (logger.isLoggable(Level.FINE)) - logger.fine("type 1 message: " + toHex(msg)); - - String result = null; - try { - result = new String(BASE64EncoderStream.encode(msg), "iso-8859-1"); - } catch (UnsupportedEncodingException e) { - assert false; - } - return result; - } - - /** - * Convert a 7 byte array to an 8 byte array (for a des key with parity). - * Input starts at offset off. - */ - private byte[] makeDesKey(byte[] input, int off) { - int[] in = new int[input.length]; - for (int i = 0; i < in.length; i++) { - in[i] = input[i] < 0 ? input[i] + 256: input[i]; - } - byte[] out = new byte[8]; - out[0] = (byte)in[off+0]; - out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1)); - out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); - out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); - out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); - out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); - out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); - out[7] = (byte)((in[off+6] << 1) & 0xFF); - return out; - } - - private byte[] calcLMHash() throws GeneralSecurityException { - byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; - byte[] pwb = null; - try { - pwb = password.toUpperCase(Locale.ENGLISH).getBytes("iso-8859-1"); - } catch (UnsupportedEncodingException ex) { - // should never happen - assert false; - } - byte[] pwb1 = new byte[14]; - int len = password.length(); - if (len > 14) - len = 14; - System.arraycopy(pwb, 0, pwb1, 0, len); /* Zero padded */ - - DESKeySpec dks1 = new DESKeySpec(makeDesKey(pwb1, 0)); - DESKeySpec dks2 = new DESKeySpec(makeDesKey(pwb1, 7)); - - SecretKey key1 = fac.generateSecret(dks1); - SecretKey key2 = fac.generateSecret(dks2); - cipher.init(Cipher.ENCRYPT_MODE, key1); - byte[] out1 = cipher.doFinal(magic, 0, 8); - cipher.init(Cipher.ENCRYPT_MODE, key2); - byte[] out2 = cipher.doFinal(magic, 0, 8); - - byte[] result = new byte [21]; - System.arraycopy(out1, 0, result, 0, 8); - System.arraycopy(out2, 0, result, 8, 8); - return result; - } - - private byte[] calcNTHash() throws GeneralSecurityException { - byte[] pw = null; - try { - pw = password.getBytes("UnicodeLittleUnmarked"); - } catch (UnsupportedEncodingException e) { - assert false; - } - byte[] out = md4.digest(pw); - byte[] result = new byte[21]; - System.arraycopy(out, 0, result, 0, 16); - return result; - } - - /* - * Key is a 21 byte array. Split it into 3 7 byte chunks, - * convert each to 8 byte DES keys, encrypt the text arg with - * each key and return the three results in a sequential []. - */ - private byte[] calcResponse(byte[] key, byte[] text) - throws GeneralSecurityException { - assert key.length == 21; - DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0)); - DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7)); - DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14)); - SecretKey key1 = fac.generateSecret(dks1); - SecretKey key2 = fac.generateSecret(dks2); - SecretKey key3 = fac.generateSecret(dks3); - cipher.init(Cipher.ENCRYPT_MODE, key1); - byte[] out1 = cipher.doFinal(text, 0, 8); - cipher.init(Cipher.ENCRYPT_MODE, key2); - byte[] out2 = cipher.doFinal(text, 0, 8); - cipher.init(Cipher.ENCRYPT_MODE, key3); - byte[] out3 = cipher.doFinal(text, 0, 8); - byte[] result = new byte[24]; - System.arraycopy(out1, 0, result, 0, 8); - System.arraycopy(out2, 0, result, 8, 8); - System.arraycopy(out3, 0, result, 16, 8); - return result; - } - - public String generateType3Msg(String challenge) { - try { - /* First decode the type2 message to get the server nonce */ - /* nonce is located at type2[24] for 8 bytes */ - - byte[] type2 = null; - try { - type2 = BASE64DecoderStream.decode(challenge.getBytes("us-ascii")); - } catch (UnsupportedEncodingException ex) { - // should never happen - assert false; - } - byte[] nonce = new byte[8]; - System.arraycopy(type2, 24, nonce, 0, 8); - - int ulen = username.length()*2; - type3[36] = type3[38] = (byte) (ulen % 256); - type3[37] = type3[39] = (byte) (ulen / 256); - int dlen = ntdomain.length()*2; - type3[28] = type3[30] = (byte) (dlen % 256); - type3[29] = type3[31] = (byte) (dlen / 256); - int hlen = hostname.length()*2; - type3[44] = type3[46] = (byte) (hlen % 256); - type3[45] = type3[47] = (byte) (hlen / 256); - - int l = 64; - copybytes(type3, l, ntdomain, "UnicodeLittleUnmarked"); - type3[32] = (byte) (l % 256); - type3[33] = (byte) (l / 256); - l += dlen; - copybytes(type3, l, username, "UnicodeLittleUnmarked"); - type3[40] = (byte) (l % 256); - type3[41] = (byte) (l / 256); - l += ulen; - copybytes(type3, l, hostname, "UnicodeLittleUnmarked"); - type3[48] = (byte) (l % 256); - type3[49] = (byte) (l / 256); - l += hlen; - - byte[] lmhash = calcLMHash(); - byte[] lmresponse = calcResponse(lmhash, nonce); - byte[] nthash = calcNTHash(); - byte[] ntresponse = calcResponse(nthash, nonce); - System.arraycopy(lmresponse, 0, type3, l, 24); - type3[16] = (byte) (l % 256); - type3[17] = (byte) (l / 256); - l += 24; - System.arraycopy(ntresponse, 0, type3, l, 24); - type3[24] = (byte) (l % 256); - type3[25] = (byte) (l / 256); - l += 24; - type3[56] = (byte) (l % 256); - type3[57] = (byte) (l / 256); - - byte[] msg = new byte[l]; - System.arraycopy(type3, 0, msg, 0, l); - if (logger.isLoggable(Level.FINE)) - logger.fine("type 3 message: " + toHex(msg)); - - String result = null; - try { - result = new String(BASE64EncoderStream.encode(msg), "iso-8859-1"); - } catch (UnsupportedEncodingException e) { - assert false; - } - return result; - - } catch (GeneralSecurityException ex) { - // should never happen - logger.log(Level.FINE, "GeneralSecurityException", ex); - return ""; // will fail later - } - } - - private static char[] hex = - { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; - - private static String toHex(byte[] b) { - StringBuffer sb = new StringBuffer(b.length * 3); - for (int i = 0; i < b.length; i++) - sb.append(hex[(b[i]>>4)&0xF]).append(hex[b[i]&0xF]).append(' '); - return sb.toString(); - } -} diff --git a/src/main/java/com/sun/mail/handlers/image_gif.java b/src/main/java/com/sun/mail/handlers/image_gif.java deleted file mode 100644 index 1686130b..00000000 --- a/src/main/java/com/sun/mail/handlers/image_gif.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.io.*; -import java.awt.*; -import java.awt.datatransfer.DataFlavor; -import javax.activation.*; -import javax.mail.internet.*; - -/** - * DataContentHandler for image/gif. - */ -public class image_gif implements DataContentHandler { - private static ActivationDataFlavor myDF = new ActivationDataFlavor( - java.awt.Image.class, - "image/gif", - "GIF Image"); - - protected ActivationDataFlavor getDF() { - return myDF; - } - - /** - * Return the DataFlavors for this DataContentHandler. - * - * @return The DataFlavors - */ - public DataFlavor[] getTransferDataFlavors() { // throws Exception; - return new DataFlavor[] { getDF() }; - } - - /** - * Return the Transfer Data of type DataFlavor from InputStream. - * - * @param df The DataFlavor - * @param ds The DataSource corresponding to the data - * @return String object - */ - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - // use myDF.equals to be sure to get ActivationDataFlavor.equals, - // which properly ignores Content-Type parameters in comparison - if (getDF().equals(df)) - return getContent(ds); - else - return null; - } - - public Object getContent(DataSource ds) throws IOException { - InputStream is = ds.getInputStream(); - int pos = 0; - int count; - byte buf[] = new byte[1024]; - - while ((count = is.read(buf, pos, buf.length - pos)) != -1) { - pos += count; - if (pos >= buf.length) { - int size = buf.length; - if (size < 256*1024) - size += size; - else - size += 256*1024; - byte tbuf[] = new byte[size]; - System.arraycopy(buf, 0, tbuf, 0, pos); - buf = tbuf; - } - } - Toolkit tk = Toolkit.getDefaultToolkit(); - return tk.createImage(buf, 0, pos); - } - - /** - * Write the object to the output stream, using the specified MIME type. - */ - public void writeTo(Object obj, String type, OutputStream os) - throws IOException { - if (!(obj instanceof Image)) - throw new IOException("\"" + getDF().getMimeType() + - "\" DataContentHandler requires Image object, " + - "was given object of type " + obj.getClass().toString()); - - throw new IOException(getDF().getMimeType() + " encoding not supported"); - } -} diff --git a/src/main/java/com/sun/mail/handlers/image_jpeg.java b/src/main/java/com/sun/mail/handlers/image_jpeg.java deleted file mode 100644 index 6aa1f94c..00000000 --- a/src/main/java/com/sun/mail/handlers/image_jpeg.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.awt.datatransfer.DataFlavor; -import javax.activation.*; - -/** - * DataContentHandler for image/jpeg. - */ -public class image_jpeg extends image_gif { - private static ActivationDataFlavor myDF = new ActivationDataFlavor( - java.awt.Image.class, - "image/jpeg", - "JPEG Image"); - - protected ActivationDataFlavor getDF() { - return myDF; - } -} diff --git a/src/main/java/com/sun/mail/handlers/message_rfc822.java b/src/main/java/com/sun/mail/handlers/message_rfc822.java deleted file mode 100644 index 25ced6ae..00000000 --- a/src/main/java/com/sun/mail/handlers/message_rfc822.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.io.*; -import java.util.Properties; -import java.awt.datatransfer.DataFlavor; -import javax.activation.*; -import javax.mail.*; -import javax.mail.internet.*; - - -/** - * @author Christopher Cotton - */ - - -public class message_rfc822 implements DataContentHandler { - - ActivationDataFlavor ourDataFlavor = new ActivationDataFlavor( - javax.mail.Message.class, - "message/rfc822", - "Message"); - - /** - * return the DataFlavors for this DataContentHandler - * @return The DataFlavors. - */ - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { ourDataFlavor }; - } - - /** - * return the Transfer Data of type DataFlavor from InputStream - * @param df The DataFlavor. - * @param ds The DataSource corresponding to the data - * @return a Message object - */ - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - // make sure we can handle this DataFlavor - if (ourDataFlavor.equals(df)) - return getContent(ds); - else - return null; - } - - /** - * Return the content. - */ - public Object getContent(DataSource ds) throws IOException { - // create a new MimeMessage - try { - Session session; - if (ds instanceof MessageAware) { - MessageContext mc = ((MessageAware)ds).getMessageContext(); - session = mc.getSession(); - } else { - // Hopefully a rare case. Also hopefully the application - // has created a default Session that can just be returned - // here. If not, the one we create here is better than - // nothing, but overall not a really good answer. - session = Session.getDefaultInstance(new Properties(), null); - } - return new MimeMessage(session, ds.getInputStream()); - } catch (MessagingException me) { - throw new IOException("Exception creating MimeMessage in " + - "message/rfc822 DataContentHandler: " + me.toString()); - } - } - - /** - * construct an object from a byte stream - * (similar semantically to previous method, we are deciding - * which one to support) - */ - public void writeTo(Object obj, String mimeType, OutputStream os) - throws IOException { - // if the object is a message, we know how to write that out - if (obj instanceof Message) { - Message m = (Message)obj; - try { - m.writeTo(os); - } catch (MessagingException me) { - throw new IOException(me.toString()); - } - - } else { - throw new IOException("unsupported object"); - } - } -} diff --git a/src/main/java/com/sun/mail/handlers/multipart_mixed.java b/src/main/java/com/sun/mail/handlers/multipart_mixed.java deleted file mode 100644 index ea4bd79a..00000000 --- a/src/main/java/com/sun/mail/handlers/multipart_mixed.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.io.*; -import java.awt.datatransfer.DataFlavor; -import javax.activation.*; -import javax.mail.MessagingException; -import javax.mail.internet.*; - - -public class multipart_mixed implements DataContentHandler { - private ActivationDataFlavor myDF = new ActivationDataFlavor( - javax.mail.internet.MimeMultipart.class, - "multipart/mixed", - "Multipart"); - - /** - * Return the DataFlavors for this DataContentHandler. - * - * @return The DataFlavors - */ - public DataFlavor[] getTransferDataFlavors() { // throws Exception; - return new DataFlavor[] { myDF }; - } - - /** - * Return the Transfer Data of type DataFlavor from InputStream. - * - * @param df The DataFlavor - * @param ds The DataSource corresponding to the data - * @return String object - */ - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - // use myDF.equals to be sure to get ActivationDataFlavor.equals, - // which properly ignores Content-Type parameters in comparison - if (myDF.equals(df)) - return getContent(ds); - else - return null; - } - - /** - * Return the content. - */ - public Object getContent(DataSource ds) throws IOException { - try { - return new MimeMultipart(ds); - } catch (MessagingException e) { - IOException ioex = - new IOException("Exception while constructing MimeMultipart"); - ioex.initCause(e); - throw ioex; - } - } - - /** - * Write the object to the output stream, using the specific MIME type. - */ - public void writeTo(Object obj, String mimeType, OutputStream os) - throws IOException { - if (obj instanceof MimeMultipart) { - try { - ((MimeMultipart)obj).writeTo(os); - } catch (MessagingException e) { - throw new IOException(e.toString()); - } - } - } -} diff --git a/src/main/java/com/sun/mail/handlers/text_html.java b/src/main/java/com/sun/mail/handlers/text_html.java deleted file mode 100644 index efac8f5e..00000000 --- a/src/main/java/com/sun/mail/handlers/text_html.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import javax.activation.ActivationDataFlavor; - -/** - * DataContentHandler for text/html. - * - */ -public class text_html extends text_plain { - private static ActivationDataFlavor myDF = new ActivationDataFlavor( - java.lang.String.class, - "text/html", - "HTML String"); - - protected ActivationDataFlavor getDF() { - return myDF; - } -} diff --git a/src/main/java/com/sun/mail/handlers/text_plain.java b/src/main/java/com/sun/mail/handlers/text_plain.java deleted file mode 100644 index a665c374..00000000 --- a/src/main/java/com/sun/mail/handlers/text_plain.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.io.*; -import java.awt.datatransfer.DataFlavor; -import javax.activation.*; -import javax.mail.internet.*; - -/** - * DataContentHandler for text/plain. - * - */ -public class text_plain implements DataContentHandler { - private static ActivationDataFlavor myDF = new ActivationDataFlavor( - java.lang.String.class, - "text/plain", - "Text String"); - - /** - * An OuputStream wrapper that doesn't close the underlying stream. - */ - private static class NoCloseOutputStream extends FilterOutputStream { - public NoCloseOutputStream(OutputStream os) { - super(os); - } - - public void close() { - // do nothing - } - } - - protected ActivationDataFlavor getDF() { - return myDF; - } - - /** - * Return the DataFlavors for this DataContentHandler. - * - * @return The DataFlavors - */ - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { getDF() }; - } - - /** - * Return the Transfer Data of type DataFlavor from InputStream. - * - * @param df The DataFlavor - * @param ds The DataSource corresponding to the data - * @return String object - */ - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - // use myDF.equals to be sure to get ActivationDataFlavor.equals, - // which properly ignores Content-Type parameters in comparison - if (getDF().equals(df)) - return getContent(ds); - else - return null; - } - - public Object getContent(DataSource ds) throws IOException { - String enc = null; - InputStreamReader is = null; - - try { - enc = getCharset(ds.getContentType()); - is = new InputStreamReader(ds.getInputStream(), enc); - } catch (IllegalArgumentException iex) { - /* - * An unknown charset of the form ISO-XXX-XXX will cause - * the JDK to throw an IllegalArgumentException. The - * JDK will attempt to create a classname using this string, - * but valid classnames must not contain the character '-', - * and this results in an IllegalArgumentException, rather than - * the expected UnsupportedEncodingException. Yikes. - */ - throw new UnsupportedEncodingException(enc); - } - - try { - int pos = 0; - int count; - char buf[] = new char[1024]; - - while ((count = is.read(buf, pos, buf.length - pos)) != -1) { - pos += count; - if (pos >= buf.length) { - int size = buf.length; - if (size < 256*1024) - size += size; - else - size += 256*1024; - char tbuf[] = new char[size]; - System.arraycopy(buf, 0, tbuf, 0, pos); - buf = tbuf; - } - } - return new String(buf, 0, pos); - } finally { - try { - is.close(); - } catch (IOException ex) { } - } - } - - /** - * Write the object to the output stream, using the specified MIME type. - */ - public void writeTo(Object obj, String type, OutputStream os) - throws IOException { - if (!(obj instanceof String)) - throw new IOException("\"" + getDF().getMimeType() + - "\" DataContentHandler requires String object, " + - "was given object of type " + obj.getClass().toString()); - - String enc = null; - OutputStreamWriter osw = null; - - try { - enc = getCharset(type); - osw = new OutputStreamWriter(new NoCloseOutputStream(os), enc); - } catch (IllegalArgumentException iex) { - /* - * An unknown charset of the form ISO-XXX-XXX will cause - * the JDK to throw an IllegalArgumentException. The - * JDK will attempt to create a classname using this string, - * but valid classnames must not contain the character '-', - * and this results in an IllegalArgumentException, rather than - * the expected UnsupportedEncodingException. Yikes. - */ - throw new UnsupportedEncodingException(enc); - } - - String s = (String)obj; - osw.write(s, 0, s.length()); - /* - * Have to call osw.close() instead of osw.flush() because - * some charset converts, such as the iso-2022-jp converter, - * don't output the "shift out" sequence unless they're closed. - * The NoCloseOutputStream wrapper prevents the underlying - * stream from being closed. - */ - osw.close(); - } - - private String getCharset(String type) { - try { - ContentType ct = new ContentType(type); - String charset = ct.getParameter("charset"); - if (charset == null) - // If the charset parameter is absent, use US-ASCII. - charset = "us-ascii"; - return MimeUtility.javaCharset(charset); - } catch (Exception ex) { - return null; - } - } -} diff --git a/src/main/java/com/sun/mail/handlers/text_xml.java b/src/main/java/com/sun/mail/handlers/text_xml.java deleted file mode 100644 index 05e7dd15..00000000 --- a/src/main/java/com/sun/mail/handlers/text_xml.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.handlers; - -import java.awt.datatransfer.DataFlavor; -import java.io.IOException; -import java.io.OutputStream; - -import javax.activation.ActivationDataFlavor; -import javax.activation.DataContentHandler; -import javax.activation.DataSource; -import javax.mail.internet.ContentType; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * DataContentHandler for text/xml. - * - * @author Anil Vijendran - * @author Bill Shannon - */ -public class text_xml extends text_plain { - - private final DataFlavor[] flavors; - - public text_xml() { - flavors = new DataFlavor[] { - new ActivationDataFlavor(String.class, "text/xml", "XML String"), - new ActivationDataFlavor(String.class, "application/xml", - "XML String"), - new ActivationDataFlavor(StreamSource.class, "text/xml", "XML"), - new ActivationDataFlavor(StreamSource.class, "application/xml", - "XML") - }; - } - - /** - * Return the DataFlavors for this DataContentHandler. - * - * @return the DataFlavors - */ - public DataFlavor[] getTransferDataFlavors() { // throws Exception; - return (DataFlavor[])flavors.clone(); - } - - /** - * Return the Transfer Data of type DataFlavor from InputStream. - * - * @param df the DataFlavor - * @param ds the InputStream corresponding to the data - * @return the constructed Object - */ - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - - for (int i = 0; i < flavors.length; i++) { - DataFlavor aFlavor = flavors[i]; - if (aFlavor.equals(df)) { - if (aFlavor.getRepresentationClass() == String.class) - return super.getContent(ds); - else if (aFlavor.getRepresentationClass() == StreamSource.class) - return new StreamSource(ds.getInputStream()); - else - return null; // XXX - should never happen - } - } - return null; - } - - /** - */ - public void writeTo(Object obj, String mimeType, OutputStream os) - throws IOException { - if (!isXmlType(mimeType)) - throw new IOException( - "Invalid content type \"" + mimeType + "\" for text/xml DCH"); - if (obj instanceof String) { - super.writeTo(obj, mimeType, os); - return; - } - if (!(obj instanceof DataSource || obj instanceof Source)) { - throw new IOException("Invalid Object type = "+obj.getClass()+ - ". XmlDCH can only convert DataSource or Source to XML."); - } - - try { - Transformer transformer = - TransformerFactory.newInstance().newTransformer(); - StreamResult result = new StreamResult(os); - if (obj instanceof DataSource) { - // Streaming transform applies only to - // javax.xml.transform.StreamSource - transformer.transform( - new StreamSource(((DataSource)obj).getInputStream()), - result); - } else { - transformer.transform((Source)obj, result); - } - } catch (Exception ex) { - throw new IOException( - "Unable to run the JAXP transformer on a stream " - + ex.getMessage()); - } - } - - private boolean isXmlType(String type) { - try { - ContentType ct = new ContentType(type); - return ct.getSubType().equals("xml") && - (ct.getPrimaryType().equals("text") || - ct.getPrimaryType().equals("application")); - } catch (Exception ex) { - return false; - } - } -} diff --git a/src/main/java/com/sun/mail/iap/Argument.java b/src/main/java/com/sun/mail/iap/Argument.java deleted file mode 100644 index 2d8a244b..00000000 --- a/src/main/java/com/sun/mail/iap/Argument.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.util.Vector; -import java.io.*; -import com.sun.mail.util.*; - -/** - * @author John Mani - */ - -public class Argument { - protected Vector items; - - /** - * Constructor - */ - public Argument() { - items = new Vector(1); - } - - /** - * append the given Argument to this Argument. All items - * from the source argument are copied into this destination - * argument. - */ - public void append(Argument arg) { - items.ensureCapacity(items.size() + arg.items.size()); - for (int i=0; i < arg.items.size(); i++) - items.addElement(arg.items.elementAt(i)); - } - - /** - * Write out given string as an ASTRING, depending on the type - * of the characters inside the string. The string should - * contain only ASCII characters.

    - * - * XXX: Hmm .. this should really be called writeASCII() - * - * @param s String to write out - */ - public void writeString(String s) { - items.addElement(new AString(ASCIIUtility.getBytes(s))); - } - - /** - * Convert the given string into bytes in the specified - * charset, and write the bytes out as an ASTRING - */ - public void writeString(String s, String charset) - throws UnsupportedEncodingException { - if (charset == null) // convenience - writeString(s); - else - items.addElement(new AString(s.getBytes(charset))); - } - - /** - * Write out given byte[] as a Literal. - * @param b byte[] to write out - */ - public void writeBytes(byte[] b) { - items.addElement(b); - } - - /** - * Write out given ByteArrayOutputStream as a Literal. - * @param b ByteArrayOutputStream to be written out. - */ - public void writeBytes(ByteArrayOutputStream b) { - items.addElement(b); - } - - /** - * Write out given data as a literal. - * @param b Literal representing data to be written out. - */ - public void writeBytes(Literal b) { - items.addElement(b); - } - - /** - * Write out given string as an Atom. Note that an Atom can contain only - * certain US-ASCII characters. No validation is done on the characters - * in the string. - * @param s String - */ - public void writeAtom(String s) { - items.addElement(new Atom(s)); - } - - /** - * Write out number. - * @param i number - */ - public void writeNumber(int i) { - items.addElement(new Integer(i)); - } - - /** - * Write out number. - * @param i number - */ - public void writeNumber(long i) { - items.addElement(new Long(i)); - } - - /** - * Write out as parenthesised list. - */ - public void writeArgument(Argument c) { - items.addElement(c); - } - - /* - * Write out all the buffered items into the output stream. - */ - public void write(Protocol protocol) - throws IOException, ProtocolException { - int size = items != null ? items.size() : 0; - DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); - - for (int i=0; i < size; i++) { - if (i > 0) // write delimiter if not the first item - os.write(' '); - - Object o = items.elementAt(i); - if (o instanceof Atom) { - os.writeBytes(((Atom)o).string); - } else if (o instanceof Number) { - os.writeBytes(((Number)o).toString()); - } else if (o instanceof AString) { - astring(((AString)o).bytes, protocol); - } else if (o instanceof byte[]) { - literal((byte[])o, protocol); - } else if (o instanceof ByteArrayOutputStream) { - literal((ByteArrayOutputStream)o, protocol); - } else if (o instanceof Literal) { - literal((Literal)o, protocol); - } else if (o instanceof Argument) { - os.write('('); // open parans - ((Argument)o).write(protocol); - os.write(')'); // close parans - } - } - } - - /** - * Write out given String as either an Atom, QuotedString or Literal - */ - private void astring(byte[] bytes, Protocol protocol) - throws IOException, ProtocolException { - DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); - int len = bytes.length; - - // If length is greater than 1024 bytes, send as literal - if (len > 1024) { - literal(bytes, protocol); - return; - } - - // if 0 length, send as quoted-string - boolean quote = len == 0 ? true: false; - boolean escape = false; - - byte b; - for (int i = 0; i < len; i++) { - b = bytes[i]; - if (b == '\0' || b == '\r' || b == '\n' || ((b & 0xff) > 0177)) { - // NUL, CR or LF means the bytes need to be sent as literals - literal(bytes, protocol); - return; - } - if (b == '*' || b == '%' || b == '(' || b == ')' || b == '{' || - b == '"' || b == '\\' || ((b & 0xff) <= ' ')) { - quote = true; - if (b == '"' || b == '\\') // need to escape these characters - escape = true; - } - } - - if (quote) // start quote - os.write('"'); - - if (escape) { - // already quoted - for (int i = 0; i < len; i++) { - b = bytes[i]; - if (b == '"' || b == '\\') - os.write('\\'); - os.write(b); - } - } else - os.write(bytes); - - - if (quote) // end quote - os.write('"'); - } - - /** - * Write out given byte[] as a literal - */ - private void literal(byte[] b, Protocol protocol) - throws IOException, ProtocolException { - startLiteral(protocol, b.length).write(b); - } - - /** - * Write out given ByteArrayOutputStream as a literal. - */ - private void literal(ByteArrayOutputStream b, Protocol protocol) - throws IOException, ProtocolException { - b.writeTo(startLiteral(protocol, b.size())); - } - - /** - * Write out given Literal as a literal. - */ - private void literal(Literal b, Protocol protocol) - throws IOException, ProtocolException { - b.writeTo(startLiteral(protocol, b.size())); - } - - private OutputStream startLiteral(Protocol protocol, int size) - throws IOException, ProtocolException { - DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); - boolean nonSync = protocol.supportsNonSyncLiterals(); - - os.write('{'); - os.writeBytes(Integer.toString(size)); - if (nonSync) // server supports non-sync literals - os.writeBytes("+}\r\n"); - else - os.writeBytes("}\r\n"); - os.flush(); - - // If we are using synchronized literals, wait for the server's - // continuation signal - if (!nonSync) { - for (; ;) { - Response r = protocol.readResponse(); - if (r.isContinuation()) - break; - if (r.isTagged()) - throw new LiteralException(r); - // XXX - throw away untagged responses; - // violates IMAP spec, hope no servers do this - } - } - return os; - } -} - -class Atom { - String string; - - Atom(String s) { - string = s; - } -} - -class AString { - byte[] bytes; - - AString(byte[] b) { - bytes = b; - } -} diff --git a/src/main/java/com/sun/mail/iap/BadCommandException.java b/src/main/java/com/sun/mail/iap/BadCommandException.java deleted file mode 100644 index 132b5919..00000000 --- a/src/main/java/com/sun/mail/iap/BadCommandException.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author John Mani - */ - -public class BadCommandException extends ProtocolException { - - private static final long serialVersionUID = 5769722539397237515L; - - /** - * Constructs an BadCommandException with no detail message. - */ - public BadCommandException() { - super(); - } - - /** - * Constructs an BadCommandException with the specified detail message. - * @param s the detail message - */ - public BadCommandException(String s) { - super(s); - } - - /** - * Constructs an BadCommandException with the specified Response. - * @param r the Response - */ - public BadCommandException(Response r) { - super(r); - } -} diff --git a/src/main/java/com/sun/mail/iap/ByteArray.java b/src/main/java/com/sun/mail/iap/ByteArray.java deleted file mode 100644 index 2712c6d0..00000000 --- a/src/main/java/com/sun/mail/iap/ByteArray.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.io.ByteArrayInputStream; - -/** - * A simple wrapper around a byte array, with a start position and - * count of bytes. - * - * @author John Mani - */ - -public class ByteArray { - private byte[] bytes; // the byte array - private int start; // start position - private int count; // count of bytes - - /** - * Constructor - */ - public ByteArray(byte[] b, int start, int count) { - bytes = b; - this.start = start; - this.count = count; - } - - /** - * Constructor that creates a byte array of the specified size. - * - * @since JavaMail 1.4.1 - */ - public ByteArray(int size) { - this(new byte[size], 0, size); - } - - /** - * Returns the internal byte array. Note that this is a live - * reference to the actual data, not a copy. - */ - public byte[] getBytes() { - return bytes; - } - - /** - * Returns a new byte array that is a copy of the data. - */ - public byte[] getNewBytes() { - byte[] b = new byte[count]; - System.arraycopy(bytes, start, b, 0, count); - return b; - } - - /** - * Returns the start position - */ - public int getStart() { - return start; - } - - /** - * Returns the count of bytes - */ - public int getCount() { - return count; - } - - /** - * Set the count of bytes. - * - * @since JavaMail 1.4.1 - */ - public void setCount(int count) { - this.count = count; - } - - /** - * Returns a ByteArrayInputStream. - */ - public ByteArrayInputStream toByteArrayInputStream() { - return new ByteArrayInputStream(bytes, start, count); - } - - /** - * Grow the byte array by incr bytes. - * - * @since JavaMail 1.4.1 - */ - public void grow(int incr) { - byte[] nbuf = new byte[bytes.length + incr]; - System.arraycopy(bytes, 0, nbuf, 0, bytes.length); - bytes = nbuf; - } -} diff --git a/src/main/java/com/sun/mail/iap/CommandFailedException.java b/src/main/java/com/sun/mail/iap/CommandFailedException.java deleted file mode 100644 index 45b55c30..00000000 --- a/src/main/java/com/sun/mail/iap/CommandFailedException.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author John Mani - */ - -public class CommandFailedException extends ProtocolException { - - private static final long serialVersionUID = 793932807880443631L; - - /** - * Constructs an CommandFailedException with no detail message. - */ - public CommandFailedException() { - super(); - } - - /** - * Constructs an CommandFailedException with the specified detail message. - * @param s the detail message - */ - public CommandFailedException(String s) { - super(s); - } - - /** - * Constructs an CommandFailedException with the specified Response. - * @param r the Response. - */ - public CommandFailedException(Response r) { - super(r); - } -} diff --git a/src/main/java/com/sun/mail/iap/ConnectionException.java b/src/main/java/com/sun/mail/iap/ConnectionException.java deleted file mode 100644 index dca420c9..00000000 --- a/src/main/java/com/sun/mail/iap/ConnectionException.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author John Mani - */ - -public class ConnectionException extends ProtocolException { - private transient Protocol p; - - private static final long serialVersionUID = 5749739604257464727L; - - /** - * Constructs an ConnectionException with no detail message. - */ - public ConnectionException() { - super(); - } - - /** - * Constructs an ConnectionException with the specified detail message. - * @param s the detail message - */ - public ConnectionException(String s) { - super(s); - } - - /** - * Constructs an ConnectionException with the specified Response. - * @param r the Response - */ - public ConnectionException(Protocol p, Response r) { - super(r); - this.p = p; - } - - public Protocol getProtocol() { - return p; - } -} diff --git a/src/main/java/com/sun/mail/iap/Literal.java b/src/main/java/com/sun/mail/iap/Literal.java deleted file mode 100644 index d964061e..00000000 --- a/src/main/java/com/sun/mail/iap/Literal.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.io.*; - -/** - * An interface for objects that provide data dynamically for use in - * a literal protocol element. - * - * @author Bill Shannon - */ - -public interface Literal { - /** - * Return the size of the data. - */ - public int size(); - - /** - * Write the data to the OutputStream. - */ - public void writeTo(OutputStream os) throws IOException; -} diff --git a/src/main/java/com/sun/mail/iap/LiteralException.java b/src/main/java/com/sun/mail/iap/LiteralException.java deleted file mode 100644 index 110d45ac..00000000 --- a/src/main/java/com/sun/mail/iap/LiteralException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author Bill Shannon - */ - -public class LiteralException extends ProtocolException { - - private static final long serialVersionUID = -6919179828339609913L; - - /** - * Constructs a LiteralException with the specified Response object. - */ - public LiteralException(Response r) { - super(r.toString()); - response = r; - } -} diff --git a/src/main/java/com/sun/mail/iap/ParsingException.java b/src/main/java/com/sun/mail/iap/ParsingException.java deleted file mode 100644 index 6e73f211..00000000 --- a/src/main/java/com/sun/mail/iap/ParsingException.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author John Mani - */ - -public class ParsingException extends ProtocolException { - - private static final long serialVersionUID = 7756119840142724839L; - - /** - * Constructs an ParsingException with no detail message. - */ - public ParsingException() { - super(); - } - - /** - * Constructs an ParsingException with the specified detail message. - * @param s the detail message - */ - public ParsingException(String s) { - super(s); - } - - /** - * Constructs an ParsingException with the specified Response. - * @param r the Response - */ - public ParsingException(Response r) { - super(r); - } -} diff --git a/src/main/java/com/sun/mail/iap/Protocol.java b/src/main/java/com/sun/mail/iap/Protocol.java deleted file mode 100644 index f04eef87..00000000 --- a/src/main/java/com/sun/mail/iap/Protocol.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.util.Vector; -import java.util.Properties; -import java.io.*; -import java.net.*; -import java.util.logging.Level; -import javax.net.ssl.SSLSocket; -import com.sun.mail.util.*; - -/** - * General protocol handling code for IMAP-like protocols.

    - * - * The Protocol object is multithread safe. - * - * @author John Mani - * @author Max Spivak - * @author Bill Shannon - */ - -public class Protocol { - protected String host; - private Socket socket; - // in case we turn on TLS, we'll need these later - protected boolean quote; - protected MailLogger logger; - protected MailLogger traceLogger; - protected Properties props; - protected String prefix; - - private boolean connected = false; // did constructor succeed? - private TraceInputStream traceInput; // the Tracer - private volatile ResponseInputStream input; - - private TraceOutputStream traceOutput; // the Tracer - private volatile DataOutputStream output; - - private int tagCounter = 0; - - private String localHostName; - - /* - * handlers is a Vector, initialized here, - * because we depend on it always existing and depend - * on the synchronization that Vector provides. - */ - private final Vector handlers = new Vector(); // response handlers - - private volatile long timestamp; - - private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; - - /** - * Constructor.

    - * - * Opens a connection to the given host at given port. - * - * @param host host to connect to - * @param port portnumber to connect to - * @param props Properties object used by this protocol - * @param prefix Prefix to prepend to property keys - * @param isSSL use SSL? - * @param logger log messages here - */ - public Protocol(String host, int port, - Properties props, String prefix, - boolean isSSL, MailLogger logger) - throws IOException, ProtocolException { - try { - this.host = host; - this.props = props; - this.prefix = prefix; - this.logger = logger; - traceLogger = logger.getSubLogger("protocol", null); - - socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL); - quote = PropUtil.getBooleanProperty(props, - "mail.debug.quote", false); - - initStreams(); - - // Read server greeting - processGreeting(readResponse()); - - timestamp = System.currentTimeMillis(); - - connected = true; // must be last statement in constructor - } finally { - /* - * If we get here because an exception was thrown, we need - * to disconnect to avoid leaving a connected socket that - * no one will be able to use because this object was never - * completely constructed. - */ - if (!connected) - disconnect(); - } - } - - private void initStreams() throws IOException { - traceInput = new TraceInputStream(socket.getInputStream(), traceLogger); - traceInput.setQuote(quote); - input = new ResponseInputStream(traceInput); - - traceOutput = - new TraceOutputStream(socket.getOutputStream(), traceLogger); - traceOutput.setQuote(quote); - output = new DataOutputStream(new BufferedOutputStream(traceOutput)); - } - - /** - * Constructor for debugging. - */ - public Protocol(InputStream in, PrintStream out, boolean debug) - throws IOException { - this.host = "localhost"; - this.quote = false; - logger = new MailLogger(this.getClass(), "DEBUG", debug, out); - traceLogger = logger.getSubLogger("protocol", null); - - // XXX - inlined initStreams, won't allow later startTLS - traceInput = new TraceInputStream(in, traceLogger); - traceInput.setQuote(quote); - input = new ResponseInputStream(traceInput); - - traceOutput = new TraceOutputStream(out, traceLogger); - traceOutput.setQuote(quote); - output = new DataOutputStream(new BufferedOutputStream(traceOutput)); - - timestamp = System.currentTimeMillis(); - } - - /** - * Returns the timestamp. - */ - - public long getTimestamp() { - return timestamp; - } - - /** - * Adds a response handler. - */ - public void addResponseHandler(ResponseHandler h) { - handlers.addElement(h); - } - - /** - * Removed the specified response handler. - */ - public void removeResponseHandler(ResponseHandler h) { - handlers.removeElement(h); - } - - /** - * Notify response handlers - */ - public void notifyResponseHandlers(Response[] responses) { - if (handlers.size() == 0) - return; - - for (int i = 0; i < responses.length; i++) { // go thru responses - Response r = responses[i]; - - // skip responses that have already been handled - if (r == null) - continue; - - // Need to copy handlers list because handlers can be removed - // when handling a response. - Object[] h = handlers.toArray(); - - // dispatch 'em - for (int j = 0; j < h.length; j++) { - if (h[j] != null) - ((ResponseHandler)h[j]).handleResponse(r); - } - } - } - - protected void processGreeting(Response r) throws ProtocolException { - if (r.isBYE()) - throw new ConnectionException(this, r); - } - - /** - * Return the Protocol's InputStream. - */ - protected ResponseInputStream getInputStream() { - return input; - } - - /** - * Return the Protocol's OutputStream - */ - protected OutputStream getOutputStream() { - return output; - } - - /** - * Returns whether this Protocol supports non-synchronizing literals - * Default is false. Subclasses should override this if required - */ - protected synchronized boolean supportsNonSyncLiterals() { - return false; - } - - public Response readResponse() - throws IOException, ProtocolException { - return new Response(this); - } - - /** - * Return a buffer to be used to read a response. - * The default implementation returns null, which causes - * a new buffer to be allocated for every response. - * - * @since JavaMail 1.4.1 - */ - protected ByteArray getResponseBuffer() { - return null; - } - - public String writeCommand(String command, Argument args) - throws IOException, ProtocolException { - // assert Thread.holdsLock(this); - // can't assert because it's called from constructor - String tag = "A" + Integer.toString(tagCounter++, 10); // unique tag - - output.writeBytes(tag + " " + command); - - if (args != null) { - output.write(' '); - args.write(this); - } - - output.write(CRLF); - output.flush(); - return tag; - } - - /** - * Send a command to the server. Collect all responses until either - * the corresponding command completion response or a BYE response - * (indicating server failure). Return all the collected responses. - * - * @param command the command - * @param args the arguments - * @return array of Response objects returned by the server - */ - public synchronized Response[] command(String command, Argument args) { - commandStart(command); - Vector v = new Vector(); - boolean done = false; - String tag = null; - Response r = null; - - // write the command - try { - tag = writeCommand(command, args); - } catch (LiteralException lex) { - v.addElement(lex.getResponse()); - done = true; - } catch (Exception ex) { - // Convert this into a BYE response - v.addElement(Response.byeResponse(ex)); - done = true; - } - - Response byeResp = null; - while (!done) { - try { - r = readResponse(); - } catch (IOException ioex) { - if (byeResp != null) // connection closed after BYE was sent - break; - // convert this into a BYE response - r = Response.byeResponse(ioex); - } catch (ProtocolException pex) { - continue; // skip this response - } - - if (r.isBYE()) { - byeResp = r; - continue; - } - - v.addElement(r); - - // If this is a matching command completion response, we are done - if (r.isTagged() && r.getTag().equals(tag)) - done = true; - } - - if (byeResp != null) - v.addElement(byeResp); // must be last - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - timestamp = System.currentTimeMillis(); - commandEnd(); - return responses; - } - - /** - * Convenience routine to handle OK, NO, BAD and BYE responses. - */ - public void handleResult(Response response) throws ProtocolException { - if (response.isOK()) - return; - else if (response.isNO()) - throw new CommandFailedException(response); - else if (response.isBAD()) - throw new BadCommandException(response); - else if (response.isBYE()) { - disconnect(); - throw new ConnectionException(this, response); - } - } - - /** - * Convenience routine to handle simple IAP commands - * that do not have responses specific to that command. - */ - public void simpleCommand(String cmd, Argument args) - throws ProtocolException { - // Issue command - Response[] r = command(cmd, args); - - // dispatch untagged responses - notifyResponseHandlers(r); - - // Handle result of this command - handleResult(r[r.length-1]); - } - - /** - * Start TLS on the current connection. - * cmd is the command to issue to start TLS negotiation. - * If the command succeeds, we begin TLS negotiation. - * If the socket is already an SSLSocket this is a nop and the command - * is not issued. - */ - public synchronized void startTLS(String cmd) - throws IOException, ProtocolException { - if (socket instanceof SSLSocket) - return; // nothing to do - simpleCommand(cmd, null); - socket = SocketFetcher.startTLS(socket, host, props, prefix); - initStreams(); - } - - /** - * Is this connection using an SSL socket? - * - * @return true if using SSL - * @since JavaMail 1.4.6 - */ - public boolean isSSL() { - return socket instanceof SSLSocket; - } - - /** - * Disconnect. - */ - protected synchronized void disconnect() { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - // ignore it - } - socket = null; - } - } - - /** - * Get the name of the local host. - * The property .localhost overrides .localaddress, - * which overrides what InetAddress would tell us. - */ - protected synchronized String getLocalHost() { - // get our hostname and cache it for future use - if (localHostName == null || localHostName.length() <= 0) - localHostName = - props.getProperty(prefix + ".localhost"); - if (localHostName == null || localHostName.length() <= 0) - localHostName = - props.getProperty(prefix + ".localaddress"); - try { - if (localHostName == null || localHostName.length() <= 0) { - InetAddress localHost = InetAddress.getLocalHost(); - localHostName = localHost.getCanonicalHostName(); - // if we can't get our name, use local address literal - if (localHostName == null) - // XXX - not correct for IPv6 - localHostName = "[" + localHost.getHostAddress() + "]"; - } - } catch (UnknownHostException uhex) { - } - - // last chance, try to get our address from our socket - if (localHostName == null || localHostName.length() <= 0) { - if (socket != null && socket.isBound()) { - InetAddress localHost = socket.getLocalAddress(); - localHostName = localHost.getCanonicalHostName(); - // if we can't get our name, use local address literal - if (localHostName == null) - // XXX - not correct for IPv6 - localHostName = "[" + localHost.getHostAddress() + "]"; - } - } - return localHostName; - } - - /** - * Is protocol tracing enabled? - */ - protected boolean isTracing() { - return traceLogger.isLoggable(Level.FINEST); - } - - /** - * Temporarily turn off protocol tracing, e.g., to prevent - * tracing the authentication sequence, including the password. - */ - protected void suspendTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(false); - traceOutput.setTrace(false); - } - } - - /** - * Resume protocol tracing, if it was enabled to begin with. - */ - protected void resumeTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(true); - traceOutput.setTrace(true); - } - } - - /** - * Finalizer. - */ - protected void finalize() throws Throwable { - super.finalize(); - disconnect(); - } - - /* - * Probe points for GlassFish monitoring. - */ - private void commandStart(String command) { } - private void commandEnd() { } -} diff --git a/src/main/java/com/sun/mail/iap/ProtocolException.java b/src/main/java/com/sun/mail/iap/ProtocolException.java deleted file mode 100644 index 19ec0920..00000000 --- a/src/main/java/com/sun/mail/iap/ProtocolException.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * @author John Mani - */ - -public class ProtocolException extends Exception { - protected transient Response response = null; - - private static final long serialVersionUID = -4360500807971797439L; - - /** - * Constructs a ProtocolException with no detail message. - */ - public ProtocolException() { - super(); - } - - /** - * Constructs a ProtocolException with the specified detail message. - * @param message the detail message - */ - public ProtocolException(String message) { - super(message); - } - - /** - * Constructs a ProtocolException with the specified detail message - * and cause. - * @param message the detail message - * @param cause the cause - */ - public ProtocolException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a ProtocolException with the specified Response object. - */ - public ProtocolException(Response r) { - super(r.toString()); - response = r; - } - - /** - * Return the offending Response object. - */ - public Response getResponse() { - return response; - } -} diff --git a/src/main/java/com/sun/mail/iap/Response.java b/src/main/java/com/sun/mail/iap/Response.java deleted file mode 100644 index 5ef9e6f7..00000000 --- a/src/main/java/com/sun/mail/iap/Response.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.io.*; -import java.util.*; -import com.sun.mail.util.*; - -/** - * This class represents a response obtained from the input stream - * of an IMAP server. - * - * @author John Mani - */ - -public class Response { - protected int index; // internal index (updated during the parse) - protected int pindex; // index after parse, for reset - protected int size; // number of valid bytes in our buffer - protected byte[] buffer = null; - protected int type = 0; - protected String tag = null; - - private static final int increment = 100; - - // The first and second bits indicate whether this response - // is a Continuation, Tagged or Untagged - public final static int TAG_MASK = 0x03; - public final static int CONTINUATION = 0x01; - public final static int TAGGED = 0x02; - public final static int UNTAGGED = 0x03; - - // The third, fourth and fifth bits indicate whether this response - // is an OK, NO, BAD or BYE response - public final static int TYPE_MASK = 0x1C; - public final static int OK = 0x04; - public final static int NO = 0x08; - public final static int BAD = 0x0C; - public final static int BYE = 0x10; - - // The sixth bit indicates whether a BYE response is synthetic or real - public final static int SYNTHETIC = 0x20; - - public Response(String s) { - buffer = ASCIIUtility.getBytes(s); - size = buffer.length; - parse(); - } - - /** - * Read a new Response from the given Protocol - * @param p the Protocol object - */ - public Response(Protocol p) throws IOException, ProtocolException { - // read one response into 'buffer' - ByteArray ba = p.getResponseBuffer(); - ByteArray response = p.getInputStream().readResponse(ba); - buffer = response.getBytes(); - size = response.getCount() - 2; // Skip the terminating CRLF - - parse(); - } - - /** - * Copy constructor. - */ - public Response(Response r) { - index = r.index; - size = r.size; - buffer = r.buffer; - type = r.type; - tag = r.tag; - } - - /** - * Return a Response object that looks like a BYE protocol response. - * Include the details of the exception in the response string. - */ - public static Response byeResponse(Exception ex) { - String err = "* BYE JavaMail Exception: " + ex.toString(); - err = err.replace('\r', ' ').replace('\n', ' '); - Response r = new Response(err); - r.type |= SYNTHETIC; - return r; - } - - private void parse() { - index = 0; // position internal index at start - - if (size == 0) // empty line - return; - if (buffer[index] == '+') { // Continuation statement - type |= CONTINUATION; - index += 1; // Position beyond the '+' - return; // return - } else if (buffer[index] == '*') { // Untagged statement - type |= UNTAGGED; - index += 1; // Position beyond the '*' - } else { // Tagged statement - type |= TAGGED; - tag = readAtom(); // read the TAG, index positioned beyond tag - if (tag == null) - tag = ""; // avoid possible NPE - } - - int mark = index; // mark - String s = readAtom(); // updates index - if (s == null) - s = ""; // avoid possible NPE - if (s.equalsIgnoreCase("OK")) - type |= OK; - else if (s.equalsIgnoreCase("NO")) - type |= NO; - else if (s.equalsIgnoreCase("BAD")) - type |= BAD; - else if (s.equalsIgnoreCase("BYE")) - type |= BYE; - else - index = mark; // reset - - pindex = index; - return; - } - - public void skipSpaces() { - while (index < size && buffer[index] == ' ') - index++; - } - - /** - * Skip to the next space, for use in error recovery while parsing. - */ - public void skipToken() { - while (index < size && buffer[index] != ' ') - index++; - } - - public void skip(int count) { - index += count; - } - - public byte peekByte() { - if (index < size) - return buffer[index]; - else - return 0; // XXX - how else to signal error? - } - - /** - * Return the next byte from this Statement. - * @return the next byte. - */ - public byte readByte() { - if (index < size) - return buffer[index++]; - else - return 0; // XXX - how else to signal error? - } - - /** - * Extract an ATOM, starting at the current position. Updates - * the internal index to beyond the Atom. - * @return an Atom - */ - public String readAtom() { - return readAtom('\0'); - } - - /** - * Extract an ATOM, but stop at the additional delimiter - * (if not NUL). Used to parse a response code inside []. - */ - public String readAtom(char delim) { - skipSpaces(); - - if (index >= size) // already at end of response - return null; - - /* - * An ATOM is any CHAR delimited by : - * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\' - */ - byte b; - int start = index; - while (index < size && ((b = buffer[index]) > ' ') && - b != '(' && b != ')' && b != '%' && b != '*' && - b != '"' && b != '\\' && b != 0x7f && - (delim == '\0' || b != delim)) - index++; - - return ASCIIUtility.toString(buffer, start, index); - } - - /** - * Read a string as an arbitrary sequence of characters, - * stopping at the delimiter Used to read part of a - * response code inside []. - */ - public String readString(char delim) { - skipSpaces(); - - if (index >= size) // already at end of response - return null; - - int start = index; - while (index < size && buffer[index] != delim) - index++; - - return ASCIIUtility.toString(buffer, start, index); - } - - public String[] readStringList() { - return readStringList(false); - } - - public String[] readAtomStringList() { - return readStringList(true); - } - - private String[] readStringList(boolean atom) { - skipSpaces(); - - if (buffer[index] != '(') // not what we expected - return null; - index++; // skip '(' - - Vector v = new Vector(); - do { - v.addElement(atom ? readAtomString() : readString()); - } while (buffer[index++] != ')'); - - int size = v.size(); - if (size > 0) { - String[] s = new String[size]; - v.copyInto(s); - return s; - } else // empty list - return null; - } - - /** - * Extract an integer, starting at the current position. Updates the - * internal index to beyond the number. Returns -1 if a number was - * not found. - * - * @return a number - */ - public int readNumber() { - // Skip leading spaces - skipSpaces(); - - int start = index; - while (index < size && Character.isDigit((char)buffer[index])) - index++; - - if (index > start) { - try { - return ASCIIUtility.parseInt(buffer, start, index); - } catch (NumberFormatException nex) { } - } - - return -1; - } - - /** - * Extract a long number, starting at the current position. Updates the - * internal index to beyond the number. Returns -1 if a long number - * was not found. - * - * @return a long - */ - public long readLong() { - // Skip leading spaces - skipSpaces(); - - int start = index; - while (index < size && Character.isDigit((char)buffer[index])) - index++; - - if (index > start) { - try { - return ASCIIUtility.parseLong(buffer, start, index); - } catch (NumberFormatException nex) { } - } - - return -1; - } - - /** - * Extract a NSTRING, starting at the current position. Return it as - * a String. The sequence 'NIL' is returned as null - * - * NSTRING := QuotedString | Literal | "NIL" - * - * @return a String - */ - public String readString() { - return (String)parseString(false, true); - } - - /** - * Extract a NSTRING, starting at the current position. Return it as - * a ByteArrayInputStream. The sequence 'NIL' is returned as null - * - * NSTRING := QuotedString | Literal | "NIL" - * - * @return a ByteArrayInputStream - */ - public ByteArrayInputStream readBytes() { - ByteArray ba = readByteArray(); - if (ba != null) - return ba.toByteArrayInputStream(); - else - return null; - } - - /** - * Extract a NSTRING, starting at the current position. Return it as - * a ByteArray. The sequence 'NIL' is returned as null - * - * NSTRING := QuotedString | Literal | "NIL" - * - * @return a ByteArray - */ - public ByteArray readByteArray() { - /* - * Special case, return the data after the continuation uninterpreted. - * It's usually a challenge for an AUTHENTICATE command. - */ - if (isContinuation()) { - skipSpaces(); - return new ByteArray(buffer, index, size - index); - } - return (ByteArray)parseString(false, false); - } - - /** - * Extract an ASTRING, starting at the current position - * and return as a String. An ASTRING can be a QuotedString, a - * Literal or an Atom - * - * Any errors in parsing returns null - * - * ASTRING := QuotedString | Literal | Atom - * - * @return a String - */ - public String readAtomString() { - return (String)parseString(true, true); - } - - /** - * Generic parsing routine that can parse out a Quoted-String, - * Literal or Atom and return the parsed token as a String - * or a ByteArray. Errors or NIL data will return null. - */ - private Object parseString(boolean parseAtoms, boolean returnString) { - byte b; - - // Skip leading spaces - skipSpaces(); - - b = buffer[index]; - if (b == '"') { // QuotedString - index++; // skip the quote - int start = index; - int copyto = index; - - while (index < size && (b = buffer[index]) != '"') { - if (b == '\\') // skip escaped byte - index++; - if (index != copyto) { // only copy if we need to - // Beware: this is a destructive copy. I'm - // pretty sure this is OK, but ... ;> - buffer[copyto] = buffer[index]; - } - copyto++; - index++; - } - if (index >= size) { - // didn't find terminating quote, something is seriously wrong - //throw new ArrayIndexOutOfBoundsException( - // "index = " + index + ", size = " + size); - return null; - } else - index++; // skip past the terminating quote - - if (returnString) - return ASCIIUtility.toString(buffer, start, copyto); - else - return new ByteArray(buffer, start, copyto-start); - } else if (b == '{') { // Literal - int start = ++index; // note the start position - - while (buffer[index] != '}') - index++; - - int count = 0; - try { - count = ASCIIUtility.parseInt(buffer, start, index); - } catch (NumberFormatException nex) { - // throw new ParsingException(); - return null; - } - - start = index + 3; // skip "}\r\n" - index = start + count; // position index to beyond the literal - - if (returnString) // return as String - return ASCIIUtility.toString(buffer, start, start + count); - else - return new ByteArray(buffer, start, count); - } else if (parseAtoms) { // parse as an ATOM - int start = index; // track this, so that we can use to - // creating ByteArrayInputStream below. - String s = readAtom(); - if (returnString) - return s; - else // *very* unlikely - return new ByteArray(buffer, start, index); - } else if (b == 'N' || b == 'n') { // the only valid value is 'NIL' - index += 3; // skip past NIL - return null; - } - return null; // Error - } - - public int getType() { - return type; - } - - public boolean isContinuation() { - return ((type & TAG_MASK) == CONTINUATION); - } - - public boolean isTagged() { - return ((type & TAG_MASK) == TAGGED); - } - - public boolean isUnTagged() { - return ((type & TAG_MASK) == UNTAGGED); - } - - public boolean isOK() { - return ((type & TYPE_MASK) == OK); - } - - public boolean isNO() { - return ((type & TYPE_MASK) == NO); - } - - public boolean isBAD() { - return ((type & TYPE_MASK) == BAD); - } - - public boolean isBYE() { - return ((type & TYPE_MASK) == BYE); - } - - public boolean isSynthetic() { - return ((type & SYNTHETIC) == SYNTHETIC); - } - - /** - * Return the tag, if this is a tagged statement. - * @return tag of this tagged statement - */ - public String getTag() { - return tag; - } - - /** - * Return the rest of the response as a string, usually used to - * return the arbitrary message text after a NO response. - */ - public String getRest() { - skipSpaces(); - return ASCIIUtility.toString(buffer, index, size); - } - - /** - * Reset pointer to beginning of response. - */ - public void reset() { - index = pindex; - } - - public String toString() { - return ASCIIUtility.toString(buffer, 0, size); - } - -} diff --git a/src/main/java/com/sun/mail/iap/ResponseHandler.java b/src/main/java/com/sun/mail/iap/ResponseHandler.java deleted file mode 100644 index 0b3685af..00000000 --- a/src/main/java/com/sun/mail/iap/ResponseHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -/** - * This class - * - * @author John Mani - */ - -public interface ResponseHandler { - public void handleResponse(Response r); -} diff --git a/src/main/java/com/sun/mail/iap/ResponseInputStream.java b/src/main/java/com/sun/mail/iap/ResponseInputStream.java deleted file mode 100644 index ad1ad7bf..00000000 --- a/src/main/java/com/sun/mail/iap/ResponseInputStream.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.iap; - -import java.io.*; -import com.sun.mail.iap.ByteArray; -import com.sun.mail.util.ASCIIUtility; - -/** - * - * Inputstream that is used to read a Response. - * - * @author Arun Krishnan - * @author Bill Shannon - */ - -public class ResponseInputStream { - - private static final int minIncrement = 256; - private static final int maxIncrement = 256 * 1024; - private static final int incrementSlop = 16; - - // where we read from - private BufferedInputStream bin; - - /** - * Constructor. - */ - public ResponseInputStream(InputStream in) { - bin = new BufferedInputStream(in, 2 * 1024); - } - - /** - * Read a Response from the InputStream. - * @return ByteArray that contains the Response - */ - public ByteArray readResponse() throws IOException { - return readResponse(null); - } - - /** - * Read a Response from the InputStream. - * @return ByteArray that contains the Response - */ - public ByteArray readResponse(ByteArray ba) throws IOException { - if (ba == null) - ba = new ByteArray(new byte[128], 0, 128); - - byte[] buffer = ba.getBytes(); - int idx = 0; - for (;;) { // read until CRLF with no preceeding literal - // XXX - b needs to be an int, to handle bytes with value 0xff - int b = 0; - boolean gotCRLF=false; - - // Read a CRLF terminated line from the InputStream - while (!gotCRLF && - ((b = bin.read()) != -1)) { - switch (b) { - case '\n': - if ((idx > 0) && buffer[idx-1] == '\r') - gotCRLF = true; - default: - if (idx >= buffer.length) { - int incr = buffer.length; - if (incr > maxIncrement) - incr = maxIncrement; - ba.grow(incr); - buffer = ba.getBytes(); - } - buffer[idx++] = (byte)b; - } - } - - if (b == -1) - throw new IOException("Connection dropped by server?"); - - // Now lets check for literals : {}CRLF - // Note: index needs to >= 5 for the above sequence to occur - if (idx < 5 || buffer[idx-3] != '}') - break; - - int i; - // look for left curly - for (i = idx - 4; i >= 0; i--) - if (buffer[i] == '{') - break; - - if (i < 0) // Nope, not a literal ? - break; - - int count = 0; - // OK, handle the literal .. - try { - count = ASCIIUtility.parseInt(buffer, i+1, idx-3); - } catch (NumberFormatException e) { - break; - } - - // Now read 'count' bytes. (Note: count could be 0) - if (count > 0) { - int avail = buffer.length - idx; // available space in buffer - if (count + incrementSlop > avail) { - // need count-avail more bytes - ba.grow(minIncrement > count + incrementSlop - avail ? - minIncrement : count + incrementSlop - avail); - buffer = ba.getBytes(); - } - - /* - * read() might not return all the bytes in one shot, - * so call repeatedly till we are done - */ - int actual; - while (count > 0) { - actual = bin.read(buffer, idx, count); - count -= actual; - idx += actual; - } - } - // back to top of loop to read until CRLF - } - ba.setCount(idx); - return ba; - } -} diff --git a/src/main/java/com/sun/mail/imap/ACL.java b/src/main/java/com/sun/mail/imap/ACL.java deleted file mode 100644 index 25e94afc..00000000 --- a/src/main/java/com/sun/mail/imap/ACL.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.util.*; - -/** - * An access control list entry for a particular authentication identifier - * (user or group). Associates a set of Rights with the identifier. - * See RFC 2086. - *

    - * - * @author Bill Shannon - */ - -public class ACL implements Cloneable { - - private String name; - private Rights rights; - - /** - * Construct an ACL entry for the given identifier and with no rights. - * - * @param name the identifier name - */ - public ACL(String name) { - this.name = name; - this.rights = new Rights(); - } - - /** - * Construct an ACL entry for the given identifier with the given rights. - * - * @param name the identifier name - * @param rights the rights - */ - public ACL(String name, Rights rights) { - this.name = name; - this.rights = rights; - } - - /** - * Get the identifier name for this ACL entry. - * - * @return the identifier name - */ - public String getName() { - return name; - } - - /** - * Set the rights associated with this ACL entry. - * - * @param rights the rights - */ - public void setRights(Rights rights) { - this.rights = rights; - } - - /** - * Get the rights associated with this ACL entry. - * Returns the actual Rights object referenced by this ACL; - * modifications to the Rights object will effect this ACL. - * - * @return the rights - */ - public Rights getRights() { - return rights; - } - - /** - * Clone this ACL entry. - */ - public Object clone() throws CloneNotSupportedException { - ACL acl = (ACL)super.clone(); - acl.rights = (Rights)this.rights.clone(); - return acl; - } -} diff --git a/src/main/java/com/sun/mail/imap/AppendUID.java b/src/main/java/com/sun/mail/imap/AppendUID.java deleted file mode 100644 index 27de5e77..00000000 --- a/src/main/java/com/sun/mail/imap/AppendUID.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import com.sun.mail.iap.*; - -/** - * Information from the APPENDUID response code - * defined by the UIDPLUS extension - - * RFC 2359. - * - * @author Bill Shannon - */ - -public class AppendUID { - public long uidvalidity = -1; - public long uid = -1; - - public AppendUID(long uidvalidity, long uid) { - this.uidvalidity = uidvalidity; - this.uid = uid; - } -} diff --git a/src/main/java/com/sun/mail/imap/DefaultFolder.java b/src/main/java/com/sun/mail/imap/DefaultFolder.java deleted file mode 100644 index 4d394f29..00000000 --- a/src/main/java/com/sun/mail/imap/DefaultFolder.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import javax.mail.*; -import javax.mail.internet.*; -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; - -/** - * The default IMAP folder (root of the naming hierarchy). - * - * @author John Mani - */ - -public class DefaultFolder extends IMAPFolder { - - protected DefaultFolder(IMAPStore store) { - super("", UNKNOWN_SEPARATOR, store, null); - exists = true; // of course - type = HOLDS_FOLDERS; // obviously - } - - public synchronized String getName() { - return fullName; - } - - public Folder getParent() { - return null; - } - - public synchronized Folder[] list(final String pattern) - throws MessagingException { - ListInfo[] li = null; - - li = (ListInfo[])doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - return p.list("", pattern); - } - }); - - if (li == null) - return new Folder[0]; - - IMAPFolder[] folders = new IMAPFolder[li.length]; - for (int i = 0; i < folders.length; i++) - folders[i] = ((IMAPStore)store).newIMAPFolder(li[i]); - return folders; - } - - public synchronized Folder[] listSubscribed(final String pattern) - throws MessagingException { - ListInfo[] li = null; - - li = (ListInfo[])doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - return p.lsub("", pattern); - } - }); - - if (li == null) - return new Folder[0]; - - IMAPFolder[] folders = new IMAPFolder[li.length]; - for (int i = 0; i < folders.length; i++) - folders[i] = ((IMAPStore)store).newIMAPFolder(li[i]); - return folders; - } - - public boolean hasNewMessages() throws MessagingException { - // Not applicable on DefaultFolder - return false; - } - - public Folder getFolder(String name) throws MessagingException { - return ((IMAPStore)store).newIMAPFolder(name, UNKNOWN_SEPARATOR); - } - - public boolean delete(boolean recurse) throws MessagingException { - // Not applicable on DefaultFolder - throw new MethodNotSupportedException("Cannot delete Default Folder"); - } - - public boolean renameTo(Folder f) throws MessagingException { - // Not applicable on DefaultFolder - throw new MethodNotSupportedException("Cannot rename Default Folder"); - } - - public void appendMessages(Message[] msgs) throws MessagingException { - // Not applicable on DefaultFolder - throw new MethodNotSupportedException("Cannot append to Default Folder"); - } - - public Message[] expunge() throws MessagingException { - // Not applicable on DefaultFolder - throw new MethodNotSupportedException("Cannot expunge Default Folder"); - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPBodyPart.java b/src/main/java/com/sun/mail/imap/IMAPBodyPart.java deleted file mode 100644 index 7f0e0cf6..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPBodyPart.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.io.*; - -import java.util.Enumeration; -import javax.mail.*; -import javax.mail.internet.*; -import javax.activation.*; - -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; - -/** - * An IMAP body part. - * - * @author John Mani - * @author Bill Shannon - */ - -public class IMAPBodyPart extends MimeBodyPart implements ReadableMime { - private IMAPMessage message; - private BODYSTRUCTURE bs; - private String sectionId; - - // processed values .. - private String type; - private String description; - - private boolean headersLoaded = false; - - private static final boolean decodeFileName = - PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false); - - protected IMAPBodyPart(BODYSTRUCTURE bs, String sid, IMAPMessage message) { - super(); - this.bs = bs; - this.sectionId = sid; - this.message = message; - // generate content-type - ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams); - type = ct.toString(); - } - - /* Override this method to make it a no-op, rather than throw - * an IllegalWriteException. This will permit IMAPBodyParts to - * be inserted in newly crafted MimeMessages, especially when - * forwarding or replying to messages. - */ - protected void updateHeaders() { - return; - } - - public int getSize() throws MessagingException { - return bs.size; - } - - public int getLineCount() throws MessagingException { - return bs.lines; - } - - public String getContentType() throws MessagingException { - return type; - } - - public String getDisposition() throws MessagingException { - return bs.disposition; - } - - public void setDisposition(String disposition) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public String getEncoding() throws MessagingException { - return bs.encoding; - } - - public String getContentID() throws MessagingException { - return bs.id; - } - - public String getContentMD5() throws MessagingException { - return bs.md5; - } - - public void setContentMD5(String md5) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public String getDescription() throws MessagingException { - if (description != null) // cached value ? - return description; - - if (bs.description == null) - return null; - - try { - description = MimeUtility.decodeText(bs.description); - } catch (UnsupportedEncodingException ex) { - description = bs.description; - } - - return description; - } - - public void setDescription(String description, String charset) - throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public String getFileName() throws MessagingException { - String filename = null; - if (bs.dParams != null) - filename = bs.dParams.get("filename"); - if (filename == null && bs.cParams != null) - filename = bs.cParams.get("name"); - if (decodeFileName && filename != null) { - try { - filename = MimeUtility.decodeText(filename); - } catch (UnsupportedEncodingException ex) { - throw new MessagingException("Can't decode filename", ex); - } - } - return filename; - } - - public void setFileName(String filename) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - protected InputStream getContentStream() throws MessagingException { - InputStream is = null; - boolean pk = message.getPeek(); // acquire outside of message cache lock - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(message.getMessageCacheLock()) { - try { - IMAPProtocol p = message.getProtocol(); - - // Check whether this message is expunged - message.checkExpunged(); - - if (p.isREV1() && (message.getFetchBlockSize() != -1)) - return new IMAPInputStream(message, sectionId, - message.ignoreBodyStructureSize() ? -1 : bs.size, pk); - - // Else, vanila IMAP4, no partial fetch - - int seqnum = message.getSequenceNumber(); - BODY b; - if (pk) - b = p.peekBody(seqnum, sectionId); - else - b = p.fetchBody(seqnum, sectionId); - if (b != null) - is = b.getByteArrayInputStream(); - } catch (ConnectionException cex) { - throw new FolderClosedException( - message.getFolder(), cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - if (is == null) - throw new MessagingException("No content"); - else - return is; - } - - /** - * Return the MIME format stream of headers for this body part. - */ - private InputStream getHeaderStream() throws MessagingException { - if (!message.isREV1()) - loadHeaders(); // will be needed below - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(message.getMessageCacheLock()) { - try { - IMAPProtocol p = message.getProtocol(); - - // Check whether this message got expunged - message.checkExpunged(); - - if (p.isREV1()) { - int seqnum = message.getSequenceNumber(); - BODY b = p.peekBody(seqnum, sectionId + ".MIME"); - - if (b == null) - throw new MessagingException("Failed to fetch headers"); - - ByteArrayInputStream bis = b.getByteArrayInputStream(); - if (bis == null) - throw new MessagingException("Failed to fetch headers"); - return bis; - - } else { - // Can't read it from server, have to fake it - SharedByteArrayOutputStream bos = - new SharedByteArrayOutputStream(0); - LineOutputStream los = new LineOutputStream(bos); - - try { - // Write out the header - Enumeration hdrLines = super.getAllHeaderLines(); - while (hdrLines.hasMoreElements()) - los.writeln((String)hdrLines.nextElement()); - - // The CRLF separator between header and content - los.writeln(); - } catch (IOException ioex) { - // should never happen - } finally { - try { - los.close(); - } catch (IOException cex) { } - } - return bos.toStream(); - } - } catch (ConnectionException cex) { - throw new FolderClosedException( - message.getFolder(), cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } - - /** - * Return the MIME format stream corresponding to this message part. - * - * @return the MIME format stream - * @since JavaMail 1.4.5 - */ - public InputStream getMimeStream() throws MessagingException { - /* - * The IMAP protocol doesn't support returning the entire - * part content in one operation so we have to fake it by - * concatenating the header stream and the content stream. - */ - return new SequenceInputStream(getHeaderStream(), getContentStream()); - } - - public synchronized DataHandler getDataHandler() - throws MessagingException { - if (dh == null) { - if (bs.isMulti()) - dh = new DataHandler( - new IMAPMultipartDataSource( - this, bs.bodies, sectionId, message) - ); - else if (bs.isNested() && message.isREV1() && bs.envelope != null) - dh = new DataHandler( - new IMAPNestedMessage(message, - bs.bodies[0], - bs.envelope, - sectionId), - type - ); - } - - return super.getDataHandler(); - } - - public void setDataHandler(DataHandler content) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public void setContent(Object o, String type) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public void setContent(Multipart mp) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public String[] getHeader(String name) throws MessagingException { - loadHeaders(); - return super.getHeader(name); - } - - public void setHeader(String name, String value) - throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public void addHeader(String name, String value) - throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public void removeHeader(String name) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public Enumeration getAllHeaders() throws MessagingException { - loadHeaders(); - return super.getAllHeaders(); - } - - public Enumeration getMatchingHeaders(String[] names) - throws MessagingException { - loadHeaders(); - return super.getMatchingHeaders(names); - } - - public Enumeration getNonMatchingHeaders(String[] names) - throws MessagingException { - loadHeaders(); - return super.getNonMatchingHeaders(names); - } - - public void addHeaderLine(String line) throws MessagingException { - throw new IllegalWriteException("IMAPBodyPart is read-only"); - } - - public Enumeration getAllHeaderLines() throws MessagingException { - loadHeaders(); - return super.getAllHeaderLines(); - } - - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException { - loadHeaders(); - return super.getMatchingHeaderLines(names); - } - - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException { - loadHeaders(); - return super.getNonMatchingHeaderLines(names); - } - - private synchronized void loadHeaders() throws MessagingException { - if (headersLoaded) - return; - - // "headers" should never be null since it's set in the constructor. - // If something did go wrong this will fix it, but is an unsynchronized - // assignment of "headers". - if (headers == null) - headers = new InternetHeaders(); - - // load headers - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(message.getMessageCacheLock()) { - try { - IMAPProtocol p = message.getProtocol(); - - // Check whether this message got expunged - message.checkExpunged(); - - if (p.isREV1()) { - int seqnum = message.getSequenceNumber(); - BODY b = p.peekBody(seqnum, sectionId + ".MIME"); - - if (b == null) - throw new MessagingException("Failed to fetch headers"); - - ByteArrayInputStream bis = b.getByteArrayInputStream(); - if (bis == null) - throw new MessagingException("Failed to fetch headers"); - - headers.load(bis); - - } else { - - // RFC 1730 does not provide for fetching BodyPart headers - // So, just dump the RFC1730 BODYSTRUCTURE into the - // headerStore - - // Content-Type - headers.addHeader("Content-Type", type); - // Content-Transfer-Encoding - headers.addHeader("Content-Transfer-Encoding", bs.encoding); - // Content-Description - if (bs.description != null) - headers.addHeader("Content-Description", - bs.description); - // Content-ID - if (bs.id != null) - headers.addHeader("Content-ID", bs.id); - // Content-MD5 - if (bs.md5 != null) - headers.addHeader("Content-MD5", bs.md5); - } - } catch (ConnectionException cex) { - throw new FolderClosedException( - message.getFolder(), cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - headersLoaded = true; - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPFolder.java b/src/main/java/com/sun/mail/imap/IMAPFolder.java deleted file mode 100644 index 5711243b..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPFolder.java +++ /dev/null @@ -1,3127 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.util.Date; -import java.util.Vector; -import java.util.Hashtable; -import java.util.NoSuchElementException; -import java.util.logging.Level; -import java.io.*; - -import javax.mail.*; -import javax.mail.event.*; -import javax.mail.internet.*; -import javax.mail.search.*; - -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; - -/** - * This class implements an IMAP folder.

    - * - * A closed IMAPFolder object shares a protocol connection with its IMAPStore - * object. When the folder is opened, it gets its own protocol connection.

    - * - * Applications that need to make use of IMAP-specific features may cast - * a Folder object to an IMAPFolder object and - * use the methods on this class. The {@link #getQuota getQuota} and - * {@link #setQuota setQuota} methods support the IMAP QUOTA extension. - * Refer to RFC 2087 - * for more information.

    - * - * The {@link #getACL getACL}, {@link #addACL addACL}, - * {@link #removeACL removeACL}, {@link #addRights addRights}, - * {@link #removeRights removeRights}, {@link #listRights listRights}, and - * {@link #myRights myRights} methods support the IMAP ACL extension. - * Refer to RFC 2086 - * for more information.

    - * - * The {@link #getSortedMessages getSortedMessages} - * methods support the IMAP SORT extension. - * Refer to RFC 5256 - * for more information.

    - * - * The {@link #doCommand doCommand} method and - * {@link IMAPFolder.ProtocolCommand IMAPFolder.ProtocolCommand} - * interface support use of arbitrary IMAP protocol commands.

    - * - * See the com.sun.mail.imap package - * documentation for further information on the IMAP protocol provider.

    - * - * WARNING: The APIs unique to this class should be - * considered EXPERIMENTAL. They may be changed in the - * future in ways that are incompatible with applications using the - * current APIs. - * - * @author John Mani - * @author Bill Shannon - * @author Jim Glennon - */ - -/* - * The folder object itself serves as a lock for the folder's state - * EXCEPT for the message cache (see below), typically by using - * synchronized methods. When checking that a folder is open or - * closed, the folder's lock must be held. It's important that the - * folder's lock is acquired before the messageCacheLock (see below). - * Thus, the locking hierarchy is that the folder lock, while optional, - * must be acquired before the messageCacheLock, if it's acquired at - * all. Be especially careful of callbacks that occur while holding - * the messageCacheLock into (e.g.) superclass Folder methods that are - * synchronized. Note that methods in IMAPMessage will acquire the - * messageCacheLock without acquiring the folder lock.

    - * - * When a folder is opened, it creates a messageCache (a Vector) of - * empty IMAPMessage objects. Each Message has a messageNumber - which - * is its index into the messageCache, and a sequenceNumber - which is - * its IMAP sequence-number. All operations on a Message which involve - * communication with the server, use the message's sequenceNumber.

    - * - * The most important thing to note here is that the server can send - * unsolicited EXPUNGE notifications as part of the responses for "most" - * commands. Refer RFC2060, sections 5.3 & 5.5 for gory details. Also, - * the server sends these notifications AFTER the message has been - * expunged. And once a message is expunged, the sequence-numbers of - * those messages after the expunged one are renumbered. This essentially - * means that the mapping between *any* Message and its sequence-number - * can change in the period when a IMAP command is issued and its responses - * are processed. Hence we impose a strict locking model as follows:

    - * - * We define one mutex per folder - this is just a Java Object (named - * messageCacheLock). Any time a command is to be issued to the IMAP - * server (i.e., anytime the corresponding IMAPProtocol method is - * invoked), follow the below style: - * - * synchronized (messageCacheLock) { // ACQUIRE LOCK - * issue command () - * - * // The response processing is typically done within - * // the handleResponse() callback. A few commands (Fetch, - * // Expunge) return *all* responses and hence their - * // processing is done here itself. Now, as part of the - * // processing unsolicited EXPUNGE responses, we renumber - * // the necessary sequence-numbers. Thus the renumbering - * // happens within this critical-region, surrounded by - * // locks. - * process responses () - * } // RELEASE LOCK - * - * This technique is used both by methods in IMAPFolder and by methods - * in IMAPMessage and other classes that operate on data in the folder. - * Note that holding the messageCacheLock has the side effect of - * preventing the folder from being closed, and thus ensuring that the - * folder's protocol object is still valid. The protocol object should - * only be accessed while holding the messageCacheLock (except for calls - * to IMAPProtocol.isREV1(), which don't need to be protected because it - * doesn't access the server). - * - * Note that interactions with the Store's protocol connection do - * not have to be protected as above, since the Store's protocol is - * never in a "meaningful" SELECT-ed state. - */ - -public class IMAPFolder extends Folder implements UIDFolder, ResponseHandler { - - protected String fullName; // full name - protected String name; // name - protected int type; // folder type. - protected char separator; // separator - protected Flags availableFlags; // available flags - protected Flags permanentFlags; // permanent flags - protected volatile boolean exists; // whether this folder really exists ? - protected boolean isNamespace = false; // folder is a namespace name - protected volatile String[] attributes;// name attributes from LIST response - - protected volatile IMAPProtocol protocol; // this folder's protocol object - protected MessageCache messageCache;// message cache - // accessor lock for message cache - protected final Object messageCacheLock = new Object(); - - protected Hashtable uidTable; // UID->Message hashtable - - /* An IMAP delimiter is a 7bit US-ASCII character. (except NUL). - * We use '\uffff' (a non 7bit character) to indicate that we havent - * yet determined what the separator character is. - * We use '\u0000' (NUL) to indicate that no separator character - * exists, i.e., a flat hierarchy - */ - static final protected char UNKNOWN_SEPARATOR = '\uffff'; - - private volatile boolean opened = false; // is this folder opened ? - - /* This field tracks the state of this folder. If the folder is closed - * due to external causes (i.e, not thru the close() method), then - * this field will remain false. If the folder is closed thru the - * close() method, then this field is set to true. - * - * If reallyClosed is false, then a FolderClosedException is - * generated when a method is invoked on any Messaging object - * owned by this folder. If reallyClosed is true, then the - * IllegalStateException runtime exception is thrown. - */ - private boolean reallyClosed = true; - - /* - * The idleState field supports the IDLE command. - * Normally when executing an IMAP command we hold the - * messageCacheLock and often the folder lock (see above). - * While executing the IDLE command we can't hold either - * of these locks or it would prevent other threads from - * entering Folder methods even far enough to check whether - * an IDLE command is in progress. We need to check before - * issuing another command so that we can abort the IDLE - * command. - * - * The idleState field is protected by the messageCacheLock. - * The RUNNING state is the normal state and means no IDLE - * command is in progress. The IDLE state means we've issued - * an IDLE command and are reading responses. The ABORTING - * state means we've sent the DONE continuation command and - * are waiting for the thread running the IDLE command to - * break out of its read loop. - * - * When an IDLE command is in progress, the thread calling - * the idle method will be reading from the IMAP connection - * while holding neither the folder lock nor the messageCacheLock. - * It's obviously critical that no other thread try to send a - * command or read from the connection while in this state. - * However, other threads can send the DONE continuation - * command that will cause the server to break out of the IDLE - * loop and send the ending tag response to the IDLE command. - * The thread in the idle method that's reading the responses - * from the IDLE command will see this ending response and - * complete the idle method, setting the idleState field back - * to RUNNING, and notifying any threads waiting to use the - * connection. - * - * All uses of the IMAP connection (IMAPProtocol object) must - * be done while holding the messageCacheLock and must be - * preceeded by a check to make sure an IDLE command is not - * running, and abort the IDLE command if necessary. While - * waiting for the IDLE command to complete, these other threads - * will give up the messageCacheLock, but might still be holding - * the folder lock. This check is done by the getProtocol() - * method, resulting in a typical usage pattern of: - * - * synchronized (messageCacheLock) { - * IMAPProtocol p = getProtocol(); // may block waiting for IDLE - * // ... use protocol - * } - */ - private static final int RUNNING = 0; // not doing IDLE command - private static final int IDLE = 1; // IDLE command in effect - private static final int ABORTING = 2; // IDLE command aborting - private int idleState = RUNNING; - - private volatile int total = -1; // total number of messages in the - // message cache - private volatile int recent = -1; // number of recent messages - private int realTotal = -1; // total number of messages on - // the server - private long uidvalidity = -1; // UIDValidity - private long uidnext = -1; // UIDNext - private boolean doExpungeNotification = true; // used in expunge handler - - private Status cachedStatus = null; - private long cachedStatusTime = 0; - - private boolean hasMessageCountListener = false; // optimize notification - - protected MailLogger logger; - private MailLogger connectionPoolLogger; - - /** - * A fetch profile item for fetching headers. - * This inner class extends the FetchProfile.Item - * class to add new FetchProfile item types, specific to IMAPFolders. - * - * @see FetchProfile - */ - public static class FetchProfileItem extends FetchProfile.Item { - protected FetchProfileItem(String name) { - super(name); - } - - /** - * HEADERS is a fetch profile item that can be included in a - * FetchProfile during a fetch request to a Folder. - * This item indicates that the headers for messages in the specified - * range are desired to be prefetched.

    - * - * An example of how a client uses this is below:

    - *

    -	 *
    -	 * 	FetchProfile fp = new FetchProfile();
    -	 *	fp.add(IMAPFolder.FetchProfileItem.HEADERS);
    -	 *	folder.fetch(msgs, fp);
    -	 *
    -	 * 

    - */ - public static final FetchProfileItem HEADERS = - new FetchProfileItem("HEADERS"); - - /** - * SIZE is a fetch profile item that can be included in a - * FetchProfile during a fetch request to a Folder. - * This item indicates that the sizes of the messages in the specified - * range are desired to be prefetched.

    - * - * SIZE was moved to FetchProfile.Item in JavaMail 1.5. - * - * @deprecated - */ - public static final FetchProfileItem SIZE = - new FetchProfileItem("SIZE"); - } - - /** - * Constructor used to create a possibly non-existent folder. - * - * @param fullName fullname of this folder - * @param separator the default separator character for this - * folder's namespace - * @param store the Store - */ - protected IMAPFolder(String fullName, char separator, IMAPStore store, - Boolean isNamespace) { - super(store); - if (fullName == null) - throw new NullPointerException("Folder name is null"); - this.fullName = fullName; - this.separator = separator; - logger = new MailLogger(this.getClass(), - "DEBUG IMAP", store.getSession()); - connectionPoolLogger = ((IMAPStore)store).getConnectionPoolLogger(); - - /* - * Work around apparent bug in Exchange. Exchange - * will return a name of "Public Folders/" from - * LIST "%". - * - * If name has one separator, and it's at the end, - * assume this is a namespace name and treat it - * accordingly. Usually this will happen as a result - * of the list method, but this also allows getFolder - * to work with namespace names. - */ - this.isNamespace = false; - if (separator != UNKNOWN_SEPARATOR && separator != '\0') { - int i = this.fullName.indexOf(separator); - if (i > 0 && i == this.fullName.length() - 1) { - this.fullName = this.fullName.substring(0, i); - this.isNamespace = true; - } - } - - // if we were given a value, override default chosen above - if (isNamespace != null) - this.isNamespace = isNamespace.booleanValue(); - } - - /** - * Constructor used to create an existing folder. - */ - protected IMAPFolder(ListInfo li, IMAPStore store) { - this(li.name, li.separator, store, null); - - if (li.hasInferiors) - type |= HOLDS_FOLDERS; - if (li.canOpen) - type |= HOLDS_MESSAGES; - exists = true; - attributes = li.attrs; - } - - /* - * Ensure that this folder exists. If 'exists' has been set to true, - * we don't attempt to validate it with the server again. Note that - * this can result in a possible loss of sync with the server. - * ASSERT: Must be called with this folder's synchronization lock held. - */ - protected void checkExists() throws MessagingException { - // If the boolean field 'exists' is false, check with the - // server by invoking exists() .. - if (!exists && !exists()) - throw new FolderNotFoundException( - this, fullName + " not found"); - } - - /* - * Ensure the folder is closed. - * ASSERT: Must be called with this folder's synchronization lock held. - */ - protected void checkClosed() { - if (opened) - throw new IllegalStateException( - "This operation is not allowed on an open folder" - ); - } - - /* - * Ensure the folder is open. - * ASSERT: Must be called with this folder's synchronization lock held. - */ - protected void checkOpened() throws FolderClosedException { - assert Thread.holdsLock(this); - if (!opened) { - if (reallyClosed) - throw new IllegalStateException( - "This operation is not allowed on a closed folder" - ); - else // Folder was closed "implicitly" - throw new FolderClosedException(this, - "Lost folder connection to server" - ); - } - } - - /* - * Check that the given message number is within the range - * of messages present in this folder. If the message - * number is out of range, we ping the server to obtain any - * pending new message notifications from the server. - */ - protected void checkRange(int msgno) throws MessagingException { - if (msgno < 1) // message-numbers start at 1 - throw new IndexOutOfBoundsException("message number < 1"); - - if (msgno <= total) - return; - - // Out of range, let's ping the server and see if - // the server has more messages for us. - - synchronized(messageCacheLock) { // Acquire lock - try { - keepConnectionAlive(false); - } catch (ConnectionException cex) { - // Oops, lost connection - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } // Release lock - - if (msgno > total) // Still out of range ? Throw up ... - throw new IndexOutOfBoundsException(msgno + " > " + total); - } - - /* - * Check whether the given flags are supported by this server, - * and also verify that the folder allows setting flags. - */ - private void checkFlags(Flags flags) throws MessagingException { - assert Thread.holdsLock(this); - if (mode != READ_WRITE) - throw new IllegalStateException( - "Cannot change flags on READ_ONLY folder: " + fullName - ); - /* - if (!availableFlags.contains(flags)) - throw new MessagingException( - "These flags are not supported by this implementation" - ); - */ - } - - /** - * Get the name of this folder. - */ - public synchronized String getName() { - /* Return the last component of this Folder's full name. - * Folder components are delimited by the separator character. - */ - if (name == null) { - try { - name = fullName.substring( - fullName.lastIndexOf(getSeparator()) + 1 - ); - } catch (MessagingException mex) { } - } - return name; - } - - /** - * Get the fullname of this folder. - */ - public synchronized String getFullName() { - return fullName; - } - - /** - * Get this folder's parent. - */ - public synchronized Folder getParent() throws MessagingException { - char c = getSeparator(); - int index; - if ((index = fullName.lastIndexOf(c)) != -1) - return ((IMAPStore)store).newIMAPFolder( - fullName.substring(0, index), c); - else - return new DefaultFolder((IMAPStore)store); - } - - /** - * Check whether this folder really exists on the server. - */ - public synchronized boolean exists() throws MessagingException { - // Check whether this folder exists .. - ListInfo[] li = null; - final String lname; - if (isNamespace && separator != '\0') - lname = fullName + separator; - else - lname = fullName; - - li = (ListInfo[])doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - return p.list("", lname); - } - }); - - if (li != null) { - int i = findName(li, lname); - fullName = li[i].name; - separator = li[i].separator; - int len = fullName.length(); - if (separator != '\0' && len > 0 && - fullName.charAt(len - 1) == separator) { - fullName = fullName.substring(0, len - 1); - } - type = 0; - if (li[i].hasInferiors) - type |= HOLDS_FOLDERS; - if (li[i].canOpen) - type |= HOLDS_MESSAGES; - exists = true; - attributes = li[i].attrs; - } else { - exists = opened; - attributes = null; - } - - return exists; - } - - /** - * Which entry in li matches lname? - * If the name contains wildcards, more than one entry may be - * returned. - */ - private int findName(ListInfo[] li, String lname) { - int i; - // if the name contains a wildcard, there might be more than one - for (i = 0; i < li.length; i++) { - if (li[i].name.equals(lname)) - break; - } - if (i >= li.length) { // nothing matched exactly - // XXX - possibly should fail? But what if server - // is case insensitive and returns the preferred - // case of the name here? - i = 0; // use first one - } - return i; - } - - /** - * List all subfolders matching the specified pattern. - */ - public Folder[] list(String pattern) throws MessagingException { - return doList(pattern, false); - } - - /** - * List all subscribed subfolders matching the specified pattern. - */ - public Folder[] listSubscribed(String pattern) throws MessagingException { - return doList(pattern, true); - } - - private synchronized Folder[] doList(final String pattern, - final boolean subscribed) throws MessagingException { - checkExists(); // insure that this folder does exist. - - // Why waste a roundtrip to the server? - if (attributes != null && !isDirectory()) - return new Folder[0]; - - final char c = getSeparator(); - - ListInfo[] li = (ListInfo[])doCommandIgnoreFailure( - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - if (subscribed) - return p.lsub("", fullName + c + pattern); - else - return p.list("", fullName + c + pattern); - } - }); - - if (li == null) - return new Folder[0]; - - /* - * The UW based IMAP4 servers (e.g. SIMS2.0) include - * current folder (terminated with the separator), when - * the LIST pattern is '%' or '*'. i.e, - * returns "mail/" as the first LIST response. - * - * Doesn't make sense to include the current folder in this - * case, so we filter it out. Note that I'm assuming that - * the offending response is the *first* one, my experiments - * with the UW & SIMS2.0 servers indicate that .. - */ - int start = 0; - // Check the first LIST response. - if (li.length > 0 && li[0].name.equals(fullName + c)) - start = 1; // start from index = 1 - - IMAPFolder[] folders = new IMAPFolder[li.length - start]; - IMAPStore st = (IMAPStore)store; - for (int i = start; i < li.length; i++) - folders[i-start] = st.newIMAPFolder(li[i]); - return folders; - } - - /** - * Get the separator character. - */ - public synchronized char getSeparator() throws MessagingException { - if (separator == UNKNOWN_SEPARATOR) { - ListInfo[] li = null; - - li = (ListInfo[])doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - // REV1 allows the following LIST format to obtain - // the hierarchy delimiter of non-existent folders - if (p.isREV1()) // IMAP4rev1 - return p.list(fullName, ""); - else // IMAP4, note that this folder must exist for this - // to work :( - return p.list("", fullName); - } - }); - - if (li != null) - separator = li[0].separator; - else - separator = '/'; // punt ! - } - return separator; - } - - /** - * Get the type of this folder. - */ - public synchronized int getType() throws MessagingException { - if (opened) { - // never throw FolderNotFoundException if folder is open - if (attributes == null) - exists(); // try to fetch attributes - } else { - checkExists(); - } - return type; - } - - /** - * Check whether this folder is subscribed.

    - */ - public synchronized boolean isSubscribed() { - ListInfo[] li = null; - final String lname; - if (isNamespace && separator != '\0') - lname = fullName + separator; - else - lname = fullName; - - try { - li = (ListInfo[])doProtocolCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.lsub("", lname); - } - }); - } catch (ProtocolException pex) { - } - - if (li != null) { - int i = findName(li, lname); - return li[i].canOpen; - } else - return false; - } - - /** - * Subscribe/Unsubscribe this folder. - */ - public synchronized void setSubscribed(final boolean subscribe) - throws MessagingException { - doCommandIgnoreFailure(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - if (subscribe) - p.subscribe(fullName); - else - p.unsubscribe(fullName); - return null; - } - }); - } - - /** - * Create this folder, with the specified type. - */ - public synchronized boolean create(final int type) - throws MessagingException { - - char c = 0; - if ((type & HOLDS_MESSAGES) == 0) // only holds folders - c = getSeparator(); - final char sep = c; - Object ret = doCommandIgnoreFailure(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - if ((type & HOLDS_MESSAGES) == 0) // only holds folders - p.create(fullName + sep); - else { - p.create(fullName); - - // Certain IMAP servers do not allow creation of folders - // that can contain messages *and* subfolders. So, if we - // were asked to create such a folder, we should verify - // that we could indeed do so. - if ((type & HOLDS_FOLDERS) != 0) { - // we want to hold subfolders and messages. Check - // whether we could create such a folder. - ListInfo[] li = p.list("", fullName); - if (li != null && !li[0].hasInferiors) { - // Hmm ..the new folder - // doesn't support Inferiors ? Fail - p.delete(fullName); - throw new ProtocolException("Unsupported type"); - } - } - } - return Boolean.TRUE; - } - }); - - if (ret == null) - return false; // CREATE failure, maybe this - // folder already exists ? - - // exists = true; - // this.type = type; - boolean retb = exists(); // set exists, type, and attributes - if (retb) // Notify listeners on self and our Store - notifyFolderListeners(FolderEvent.CREATED); - return retb; - } - - /** - * Check whether this folder has new messages. - */ - public synchronized boolean hasNewMessages() throws MessagingException { - if (opened) { // If we are open, we already have this information - // Folder is open, make sure information is up to date - synchronized(messageCacheLock) { - // tickle the folder and store connections. - try { - keepConnectionAlive(true); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - return recent > 0 ? true : false; - } - } - - // First, the cheap way - use LIST and look for the \Marked - // or \Unmarked tag - - ListInfo[] li = null; - final String lname; - if (isNamespace && separator != '\0') - lname = fullName + separator; - else - lname = fullName; - li = (ListInfo[])doCommandIgnoreFailure(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - return p.list("", lname); - } - }); - - // if folder doesn't exist, throw exception - if (li == null) - throw new FolderNotFoundException(this, fullName + " not found"); - - int i = findName(li, lname); - if (li[i].changeState == ListInfo.CHANGED) - return true; - else if (li[i].changeState == ListInfo.UNCHANGED) - return false; - - // LIST didn't work. Try the hard way, using STATUS - try { - Status status = getStatus(); - if (status.recent > 0) - return true; - else - return false; - } catch (BadCommandException bex) { - // Probably doesn't support STATUS, tough luck. - return false; - } catch (ConnectionException cex) { - throw new StoreClosedException(store, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - /** - * Get the named subfolder.

    - */ - public synchronized Folder getFolder(String name) - throws MessagingException { - // If we know that this folder is *not* a directory, don't - // send the request to the server at all ... - if (attributes != null && !isDirectory()) - throw new MessagingException("Cannot contain subfolders"); - - char c = getSeparator(); - return ((IMAPStore)store).newIMAPFolder(fullName + c + name, c); - } - - /** - * Delete this folder. - */ - public synchronized boolean delete(boolean recurse) - throws MessagingException { - checkClosed(); // insure that this folder is closed. - - if (recurse) { - // Delete all subfolders. - Folder[] f = list(); - for (int i = 0; i < f.length; i++) - f[i].delete(recurse); // ignore intermediate failures - } - - // Attempt to delete this folder - - Object ret = doCommandIgnoreFailure(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - p.delete(fullName); - return Boolean.TRUE; - } - }); - - if (ret == null) - // Non-existent folder/No permission ?? - return false; - - // DELETE succeeded. - exists = false; - attributes = null; - - // Notify listeners on self and our Store - notifyFolderListeners(FolderEvent.DELETED); - return true; - } - - /** - * Rename this folder.

    - */ - public synchronized boolean renameTo(final Folder f) - throws MessagingException { - checkClosed(); // insure that we are closed. - checkExists(); - if (f.getStore() != store) - throw new MessagingException("Can't rename across Stores"); - - - Object ret = doCommandIgnoreFailure(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) throws ProtocolException { - p.rename(fullName, f.getFullName()); - return Boolean.TRUE; - } - }); - - if (ret == null) - return false; - - exists = false; - attributes = null; - notifyFolderRenamedListeners(f); - return true; - } - - /** - * Open this folder in the given mode. - */ - public synchronized void open(int mode) throws MessagingException { - checkClosed(); // insure that we are not already open - - MailboxInfo mi = null; - // Request store for our own protocol connection. - protocol = ((IMAPStore)store).getProtocol(this); - - synchronized(messageCacheLock) { // Acquire messageCacheLock - - /* - * Add response handler right away so we get any alerts or - * notifications that occur during the SELECT or EXAMINE. - * Have to be sure to remove it if we fail to open the - * folder. - */ - protocol.addResponseHandler(this); - - try { - if (mode == READ_ONLY) - mi = protocol.examine(fullName); - else - mi = protocol.select(fullName); - } catch (CommandFailedException cex) { - /* - * Handle SELECT or EXAMINE failure. - * Try to figure out why the operation failed so we can - * report a more reasonable exception. - * - * Will use our existing protocol object. - */ - try { - checkExists(); // throw exception if folder doesn't exist - - if ((type & HOLDS_MESSAGES) == 0) - throw new MessagingException( - "folder cannot contain messages"); - throw new MessagingException(cex.getMessage(), cex); - - } finally { - // folder not open, don't keep this information - exists = false; - attributes = null; - type = 0; - // connection still good, return it - releaseProtocol(true); - } - // NOTREACHED - } catch (ProtocolException pex) { - // got a BAD or a BYE; connection may be bad, close it - try { - protocol.logout(); - } catch (ProtocolException pex2) { - // ignore - } finally { - releaseProtocol(false); - throw new MessagingException(pex.getMessage(), pex); - } - } - - if (mi.mode != mode) { - if (mode == READ_WRITE && mi.mode == READ_ONLY && - ((IMAPStore)store).allowReadOnlySelect()) { - ; // all ok, allow it - } else { // otherwise, it's an error - try { - // close mailbox and return connection - protocol.close(); - releaseProtocol(true); - } catch (ProtocolException pex) { - // something went wrong, close connection - try { - protocol.logout(); - } catch (ProtocolException pex2) { - // ignore - } finally { - releaseProtocol(false); - } - } finally { - throw new ReadOnlyFolderException(this, - "Cannot open in desired mode"); - } - - } - } - - // Initialize stuff. - opened = true; - reallyClosed = false; - this.mode = mi.mode; - availableFlags = mi.availableFlags; - permanentFlags = mi.permanentFlags; - total = realTotal = mi.total; - recent = mi.recent; - uidvalidity = mi.uidvalidity; - uidnext = mi.uidnext; - - // Create the message cache of appropriate size - messageCache = new MessageCache(this, (IMAPStore)store, total); - - } // Release lock - - exists = true; // if we opened it, it must exist - attributes = null; // but we don't yet know its attributes - type = HOLDS_MESSAGES; // lacking more info, we know at least this much - - // notify listeners - notifyConnectionListeners(ConnectionEvent.OPENED); - } - - /** - * Prefetch attributes, based on the given FetchProfile. - */ - public synchronized void fetch(Message[] msgs, FetchProfile fp) - throws MessagingException { - checkOpened(); - - StringBuffer command = new StringBuffer(); - boolean first = true; - boolean allHeaders = false; - - if (fp.contains(FetchProfile.Item.ENVELOPE)) { - command.append(getEnvelopeCommand()); - first = false; - } - if (fp.contains(FetchProfile.Item.FLAGS)) { - command.append(first ? "FLAGS" : " FLAGS"); - first = false; - } - if (fp.contains(FetchProfile.Item.CONTENT_INFO)) { - command.append(first ? "BODYSTRUCTURE" : " BODYSTRUCTURE"); - first = false; - } - if (fp.contains(UIDFolder.FetchProfileItem.UID)) { - command.append(first ? "UID" : " UID"); - first = false; - } - if (fp.contains(IMAPFolder.FetchProfileItem.HEADERS)) { - allHeaders = true; - if (protocol.isREV1()) - command.append(first ? - "BODY.PEEK[HEADER]" : " BODY.PEEK[HEADER]"); - else - command.append(first ? "RFC822.HEADER" : " RFC822.HEADER"); - first = false; - } - if (fp.contains(FetchProfile.Item.SIZE) || - fp.contains(IMAPFolder.FetchProfileItem.SIZE)) { - command.append(first ? "RFC822.SIZE" : " RFC822.SIZE"); - first = false; - } - - // if we're not fetching all headers, fetch individual headers - String[] hdrs = null; - if (!allHeaders) { - hdrs = fp.getHeaderNames(); - if (hdrs.length > 0) { - if (!first) - command.append(" "); - command.append(createHeaderCommand(hdrs)); - } - } - - /* - * Add any additional extension fetch items. - */ - FetchItem[] fitems = protocol.getFetchItems(); - for (int i = 0; i < fitems.length; i++) { - if (fp.contains(fitems[i].getFetchProfileItem())) { - if (command.length() != 0) - command.append(" "); - command.append(fitems[i].getName()); - } - } - - Utility.Condition condition = - new IMAPMessage.FetchProfileCondition(fp, fitems); - - // Acquire the Folder's MessageCacheLock. - synchronized(messageCacheLock) { - - // Apply the test, and get the sequence-number set for - // the messages that need to be prefetched. - MessageSet[] msgsets = Utility.toMessageSet(msgs, condition); - - if (msgsets == null) - // We already have what we need. - return; - - Response[] r = null; - Vector v = new Vector(); // to collect non-FETCH responses & - // unsolicited FETCH FLAG responses - try { - r = getProtocol().fetch(msgsets, command.toString()); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (CommandFailedException cfx) { - // Ignore these, as per RFC 2180 - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - - if (r == null) - return; - - for (int i = 0; i < r.length; i++) { - if (r[i] == null) - continue; - if (!(r[i] instanceof FetchResponse)) { - v.addElement(r[i]); // Unsolicited Non-FETCH response - continue; - } - - // Got a FetchResponse. - FetchResponse f = (FetchResponse)r[i]; - // Get the corresponding message. - IMAPMessage msg = getMessageBySeqNumber(f.getNumber()); - - int count = f.getItemCount(); - boolean unsolicitedFlags = false; - - for (int j = 0; j < count; j++) { - Item item = f.getItem(j); - // Check for the FLAGS item - if (item instanceof Flags && - (!fp.contains(FetchProfile.Item.FLAGS) || - msg == null)) { - // Ok, Unsolicited FLAGS update. - unsolicitedFlags = true; - } else if (msg != null) - msg.handleFetchItem(item, hdrs, allHeaders); - } - if (msg != null) - msg.handleExtensionFetchItems(f.getExtensionItems()); - - // If this response contains any unsolicited FLAGS - // add it to the unsolicited response vector - if (unsolicitedFlags) - v.addElement(f); - } - - // Dispatch any unsolicited responses - int size = v.size(); - if (size != 0) { - Response[] responses = new Response[size]; - v.copyInto(responses); - handleResponses(responses); - } - - } // Release messageCacheLock - } - - /** - * Return the IMAP FETCH items to request in order to load - * all the "envelope" data. Subclasses can override this - * method to fetch more data when FetchProfile.Item.ENVELOPE - * is requested. - * - * @since JavaMail 1.4.6 - */ - protected String getEnvelopeCommand() { - return IMAPMessage.EnvelopeCmd; - } - - /** - * Create a new IMAPMessage object to represent the given message number. - * Subclasses of IMAPFolder may override this method to create a - * subclass of IMAPMessage. - * - * @since JavaMail 1.4.6 - */ - protected IMAPMessage newIMAPMessage(int msgnum) { - return new IMAPMessage(this, msgnum); - } - - /** - * Create the appropriate IMAP FETCH command items to fetch the - * requested headers. - */ - private String createHeaderCommand(String[] hdrs) { - StringBuffer sb; - - if (protocol.isREV1()) - sb = new StringBuffer("BODY.PEEK[HEADER.FIELDS ("); - else - sb = new StringBuffer("RFC822.HEADER.LINES ("); - - for (int i = 0; i < hdrs.length; i++) { - if (i > 0) - sb.append(" "); - sb.append(hdrs[i]); - } - - if (protocol.isREV1()) - sb.append(")]"); - else - sb.append(")"); - - return sb.toString(); - } - - /** - * Set the specified flags for the given array of messages. - */ - public synchronized void setFlags(Message[] msgs, Flags flag, boolean value) - throws MessagingException { - checkOpened(); - checkFlags(flag); // validate flags - - if (msgs.length == 0) // boundary condition - return; - - synchronized(messageCacheLock) { - try { - IMAPProtocol p = getProtocol(); - MessageSet[] ms = Utility.toMessageSet(msgs, null); - if (ms == null) - throw new MessageRemovedException( - "Messages have been removed"); - p.storeFlags(ms, flag, value); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } - - /** - * Set the specified flags for the given range of message numbers. - */ - public synchronized void setFlags(int start, int end, - Flags flag, boolean value) throws MessagingException { - checkOpened(); - Message[] msgs = new Message[end - start + 1]; - int i = 0; - for (int n = start; n <= end; n++) - msgs[i++] = getMessage(n); - setFlags(msgs, flag, value); - } - - /** - * Set the specified flags for the given array of message numbers. - */ - public synchronized void setFlags(int[] msgnums, Flags flag, boolean value) - throws MessagingException { - checkOpened(); - Message[] msgs = new Message[msgnums.length]; - for (int i = 0; i < msgnums.length; i++) - msgs[i] = getMessage(msgnums[i]); - setFlags(msgs, flag, value); - } - - /** - * Close this folder. - */ - public synchronized void close(boolean expunge) throws MessagingException { - close(expunge, false); - } - - /** - * Close this folder without waiting for the server. - */ - public synchronized void forceClose() throws MessagingException { - close(false, true); - } - - /* - * Common close method. - */ - private void close(boolean expunge, boolean force) - throws MessagingException { - assert Thread.holdsLock(this); - synchronized(messageCacheLock) { - /* - * If we already know we're closed, this is illegal. - * Can't use checkOpened() because if we were forcibly - * closed asynchronously we just want to complete the - * closing here. - */ - if (!opened && reallyClosed) - throw new IllegalStateException( - "This operation is not allowed on a closed folder" - ); - - reallyClosed = true; // Ok, lets reset - - // Maybe this folder is already closed, or maybe another - // thread which had the messageCacheLock earlier, found - // that our server connection is dead and cleaned up - // everything .. - if (!opened) - return; - - try { - waitIfIdle(); - if (force) { - logger.log(Level.FINE, "forcing folder {0} to close", - fullName); - if (protocol != null) - protocol.disconnect(); - } else if (((IMAPStore)store).isConnectionPoolFull()) { - // If the connection pool is full, logout the connection - logger.fine( - "pool is full, not adding an Authenticated connection"); - - // If the expunge flag is set, close the folder first. - if (expunge && protocol != null) - protocol.close(); - - if (protocol != null) - protocol.logout(); - } else { - // If the expunge flag is set or we're open read-only we - // can just close the folder, otherwise open it read-only - // before closing, or unselect it if supported. - if (!expunge && mode == READ_WRITE) { - try { - if (protocol != null && - protocol.hasCapability("UNSELECT")) - protocol.unselect(); - else { - if (protocol != null) { - MailboxInfo mi = protocol.examine(fullName); - if (protocol != null) // XXX - unnecessary? - protocol.close(); - } - } - } catch (ProtocolException pex2) { - if (protocol != null) - protocol.disconnect(); - } - } else { - if (protocol != null) - protocol.close(); - } - } - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - // cleanup if we haven't already - if (opened) - cleanup(true); - } - } - } - - // NOTE: this method can currently be invoked from close() or - // from handleResponses(). Both invocations are conditional, - // based on the "opened" flag, so we are sure that multiple - // Connection.CLOSED events are not generated. Also both - // invocations are from within messageCacheLock-ed areas. - private void cleanup(boolean returnToPool) { - assert Thread.holdsLock(messageCacheLock); - releaseProtocol(returnToPool); - messageCache = null; - uidTable = null; - exists = false; // to force a recheck in exists(). - attributes = null; - opened = false; - idleState = RUNNING; // just in case - notifyConnectionListeners(ConnectionEvent.CLOSED); - } - - /** - * Check whether this connection is really open. - */ - public synchronized boolean isOpen() { - synchronized(messageCacheLock) { - // Probe the connection to make sure its really open. - if (opened) { - try { - keepConnectionAlive(false); - } catch (ProtocolException pex) { } - } - } - - return opened; - } - - /** - * Return the permanent flags supported by the server. - */ - public synchronized Flags getPermanentFlags() { - if (permanentFlags == null) - return null; - return (Flags)(permanentFlags.clone()); - } - - /** - * Get the total message count. - */ - public synchronized int getMessageCount() throws MessagingException { - if (!opened) { - checkExists(); - // If this folder is not yet open, we use STATUS to - // get the total message count - try { - Status status = getStatus(); - return status.total; - } catch (BadCommandException bex) { - // doesn't support STATUS, probably vanilla IMAP4 .. - // lets try EXAMINE - IMAPProtocol p = null; - - try { - p = getStoreProtocol(); // XXX - MailboxInfo minfo = p.examine(fullName); - p.close(); - return minfo.total; - } catch (ProtocolException pex) { - // Give up. - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - } catch (ConnectionException cex) { - throw new StoreClosedException(store, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - // Folder is open, we know what the total message count is .. - synchronized(messageCacheLock) { - // tickle the folder and store connections. - try { - keepConnectionAlive(true); - return total; - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } - - /** - * Get the new message count. - */ - public synchronized int getNewMessageCount() - throws MessagingException { - if (!opened) { - checkExists(); - // If this folder is not yet open, we use STATUS to - // get the new message count - try { - Status status = getStatus(); - return status.recent; - } catch (BadCommandException bex) { - // doesn't support STATUS, probably vanilla IMAP4 .. - // lets try EXAMINE - IMAPProtocol p = null; - - try { - p = getStoreProtocol(); // XXX - MailboxInfo minfo = p.examine(fullName); - p.close(); - return minfo.recent; - } catch (ProtocolException pex) { - // Give up. - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - } catch (ConnectionException cex) { - throw new StoreClosedException(store, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - // Folder is open, we know what the new message count is .. - synchronized(messageCacheLock) { - // tickle the folder and store connections. - try { - keepConnectionAlive(true); - return recent; - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } - - /** - * Get the unread message count. - */ - public synchronized int getUnreadMessageCount() - throws MessagingException { - if (!opened) { - checkExists(); - // If this folder is not yet open, we use STATUS to - // get the unseen message count - try { - Status status = getStatus(); - return status.unseen; - } catch (BadCommandException bex) { - // doesn't support STATUS, probably vanilla IMAP4 .. - // Could EXAMINE, SEARCH for UNREAD messages and - // return the count .. bah, not worth it. - return -1; - } catch (ConnectionException cex) { - throw new StoreClosedException(store, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - // if opened, issue server-side search for messages that do - // *not* have the SEEN flag. - Flags f = new Flags(); - f.add(Flags.Flag.SEEN); - try { - synchronized(messageCacheLock) { - int[] matches = getProtocol().search(new FlagTerm(f, false)); - return matches.length; // NOTE: 'matches' is never null - } - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // Shouldn't happen - throw new MessagingException(pex.getMessage(), pex); - } - } - - /** - * Get the deleted message count. - */ - public synchronized int getDeletedMessageCount() - throws MessagingException { - if (!opened) { - checkExists(); - // no way to do this on closed folders - return -1; - } - - // if opened, issue server-side search for messages that do - // have the DELETED flag. - Flags f = new Flags(); - f.add(Flags.Flag.DELETED); - try { - synchronized(messageCacheLock) { - int[] matches = getProtocol().search(new FlagTerm(f, true)); - return matches.length; // NOTE: 'matches' is never null - } - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // Shouldn't happen - throw new MessagingException(pex.getMessage(), pex); - } - } - - /* - * Get results of STATUS command for this folder, checking cache first. - * ASSERT: Must be called with this folder's synchronization lock held. - * ASSERT: The folder must be closed. - */ - private Status getStatus() throws ProtocolException { - int statusCacheTimeout = ((IMAPStore)store).getStatusCacheTimeout(); - - // if allowed to cache and our cache is still valid, return it - if (statusCacheTimeout > 0 && cachedStatus != null && - System.currentTimeMillis() - cachedStatusTime < statusCacheTimeout) - return cachedStatus; - - IMAPProtocol p = null; - - try { - p = getStoreProtocol(); // XXX - Status s = p.status(fullName, null); - // if allowed to cache, do so - if (statusCacheTimeout > 0) { - cachedStatus = s; - cachedStatusTime = System.currentTimeMillis(); - } - return s; - } finally { - releaseStoreProtocol(p); - } - } - - /** - * Get the specified message. - */ - public synchronized Message getMessage(int msgnum) - throws MessagingException { - checkOpened(); - checkRange(msgnum); - - return messageCache.getMessage(msgnum); - } - - /** - * Append the given messages into this folder. - */ - public synchronized void appendMessages(Message[] msgs) - throws MessagingException { - checkExists(); // verify that self exists - - // XXX - have to verify that messages are in a different - // store (if any) than target folder, otherwise could - // deadlock trying to fetch messages on the same connection - // we're using for the append. - - int maxsize = ((IMAPStore)store).getAppendBufferSize(); - - for (int i = 0; i < msgs.length; i++) { - final Message m = msgs[i]; - Date d = m.getReceivedDate(); // retain dates - if (d == null) - d = m.getSentDate(); - final Date dd = d; - final Flags f = m.getFlags(); - - final MessageLiteral mos; - try { - // if we know the message is too big, don't buffer any of it - mos = new MessageLiteral(m, - m.getSize() > maxsize ? 0 : maxsize); - } catch (IOException ex) { - throw new MessagingException( - "IOException while appending messages", ex); - } catch (MessageRemovedException mrex) { - continue; // just skip this expunged message - } - - doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - p.append(fullName, f, dd, mos); - return null; - } - }); - } - } - - /** - * Append the given messages into this folder. - * Return array of AppendUID objects containing - * UIDs of these messages in the destination folder. - * Each element of the returned array corresponds to - * an element of the msgs array. A null - * element means the server didn't return UID information - * for the appended message.

    - * - * Depends on the APPENDUID response code defined by the - * UIDPLUS extension - - * RFC 2359. - * - * @since JavaMail 1.4 - */ - public synchronized AppendUID[] appendUIDMessages(Message[] msgs) - throws MessagingException { - checkExists(); // verify that self exists - - // XXX - have to verify that messages are in a different - // store (if any) than target folder, otherwise could - // deadlock trying to fetch messages on the same connection - // we're using for the append. - - int maxsize = ((IMAPStore)store).getAppendBufferSize(); - - AppendUID[] uids = new AppendUID[msgs.length]; - for (int i = 0; i < msgs.length; i++) { - final Message m = msgs[i]; - final MessageLiteral mos; - - try { - // if we know the message is too big, don't buffer any of it - mos = new MessageLiteral(m, - m.getSize() > maxsize ? 0 : maxsize); - } catch (IOException ex) { - throw new MessagingException( - "IOException while appending messages", ex); - } catch (MessageRemovedException mrex) { - continue; // just skip this expunged message - } - - Date d = m.getReceivedDate(); // retain dates - if (d == null) - d = m.getSentDate(); - final Date dd = d; - final Flags f = m.getFlags(); - AppendUID auid = (AppendUID)doCommand(new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.appenduid(fullName, f, dd, mos); - } - }); - uids[i] = auid; - } - return uids; - } - - /** - * Append the given messages into this folder. - * Return array of Message objects representing - * the messages in the destination folder. Note - * that the folder must be open. - * Each element of the returned array corresponds to - * an element of the msgs array. A null - * element means the server didn't return UID information - * for the appended message.

    - * - * Depends on the APPENDUID response code defined by the - * UIDPLUS extension - - * RFC 2359. - * - * @since JavaMail 1.4 - */ - public synchronized Message[] addMessages(Message[] msgs) - throws MessagingException { - checkOpened(); - Message[] rmsgs = new MimeMessage[msgs.length]; - AppendUID[] uids = appendUIDMessages(msgs); - for (int i = 0; i < uids.length; i++) { - AppendUID auid = uids[i]; - if (auid != null) { - if (auid.uidvalidity == uidvalidity) { - try { - rmsgs[i] = getMessageByUID(auid.uid); - } catch (MessagingException mex) { - // ignore errors at this stage - } - } - } - } - return rmsgs; - } - - /** - * Copy the specified messages from this folder, to the - * specified destination. - */ - public synchronized void copyMessages(Message[] msgs, Folder folder) - throws MessagingException { - checkOpened(); - - if (msgs.length == 0) // boundary condition - return; - - // If the destination belongs to our same store, optimize - if (folder.getStore() == store) { - synchronized(messageCacheLock) { - try { - IMAPProtocol p = getProtocol(); - MessageSet[] ms = Utility.toMessageSet(msgs, null); - if (ms == null) - throw new MessageRemovedException( - "Messages have been removed"); - p.copy(ms, folder.getFullName()); - } catch (CommandFailedException cfx) { - if (cfx.getMessage().indexOf("TRYCREATE") != -1) - throw new FolderNotFoundException( - folder, - folder.getFullName() + " does not exist" - ); - else - throw new MessagingException(cfx.getMessage(), cfx); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } else // destination is a different store. - super.copyMessages(msgs, folder); - } - - /** - * Expunge all messages marked as DELETED. - */ - public synchronized Message[] expunge() throws MessagingException { - return expunge(null); - } - - /** - * Expunge the indicated messages, which must have been marked as DELETED. - * - * Depends on the UIDPLUS extension - - * RFC 2359. - */ - public synchronized Message[] expunge(Message[] msgs) - throws MessagingException { - checkOpened(); - - if (msgs != null) { - // call fetch to make sure we have all the UIDs - FetchProfile fp = new FetchProfile(); - fp.add(UIDFolder.FetchProfileItem.UID); - fetch(msgs, fp); - } - - IMAPMessage[] rmsgs; - synchronized(messageCacheLock) { - doExpungeNotification = false; // We do this ourselves later - try { - IMAPProtocol p = getProtocol(); - if (msgs != null) - p.uidexpunge(Utility.toUIDSet(msgs)); - else - p.expunge(); - } catch (CommandFailedException cfx) { - // expunge not allowed, perhaps due to a permission problem? - if (mode != READ_WRITE) - throw new IllegalStateException( - "Cannot expunge READ_ONLY folder: " + fullName); - else - throw new MessagingException(cfx.getMessage(), cfx); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // Bad bad server .. - throw new MessagingException(pex.getMessage(), pex); - } finally { - doExpungeNotification = true; - } - - // Cleanup expunged messages and sync messageCache with reality. - if (msgs != null) - rmsgs = messageCache.removeExpungedMessages(msgs); - else - rmsgs = messageCache.removeExpungedMessages(); - if (uidTable != null) { - for (int i = 0; i < rmsgs.length; i++) { - IMAPMessage m = rmsgs[i]; - /* remove this message from the UIDTable */ - long uid = m.getUID(); - if (uid != -1) - uidTable.remove(new Long(uid)); - } - } - - // Update 'total' - total = messageCache.size(); - } - - // Notify listeners. This time its for real, guys. - if (rmsgs.length > 0) - notifyMessageRemovedListeners(true, rmsgs); - return rmsgs; - } - - /** - * Search whole folder for messages matching the given term. - */ - public synchronized Message[] search(SearchTerm term) - throws MessagingException { - checkOpened(); - - try { - Message[] matchMsgs = null; - - synchronized(messageCacheLock) { - int[] matches = getProtocol().search(term); - if (matches != null) { - matchMsgs = new IMAPMessage[matches.length]; - // Map seq-numbers into actual Messages. - for (int i = 0; i < matches.length; i++) - matchMsgs[i] = getMessageBySeqNumber(matches[i]); - } - } - return matchMsgs; - - } catch (CommandFailedException cfx) { - // unsupported charset or search criterion - return super.search(term); - } catch (SearchException sex) { - // too complex for IMAP - return super.search(term); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // bug in our IMAP layer ? - throw new MessagingException(pex.getMessage(), pex); - } - } - - /** - * Search the folder for messages matching the given term. Returns - * array of matching messages. Returns an empty array if no matching - * messages are found. - */ - public synchronized Message[] search(SearchTerm term, Message[] msgs) - throws MessagingException { - checkOpened(); - - if (msgs.length == 0) - // need to return an empty array (not null!) - return msgs; - - try { - Message[] matchMsgs = null; - - synchronized(messageCacheLock) { - IMAPProtocol p = getProtocol(); - MessageSet[] ms = Utility.toMessageSet(msgs, null); - if (ms == null) - throw new MessageRemovedException( - "Messages have been removed"); - int[] matches = p.search(ms, term); - if (matches != null) { - matchMsgs = new IMAPMessage[matches.length]; - for (int i = 0; i < matches.length; i++) - matchMsgs[i] = getMessageBySeqNumber(matches[i]); - } - } - return matchMsgs; - - } catch (CommandFailedException cfx) { - // unsupported charset or search criterion - return super.search(term, msgs); - } catch (SearchException sex) { - // too complex for IMAP - return super.search(term, msgs); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // bug in our IMAP layer ? - throw new MessagingException(pex.getMessage(), pex); - } - } - - /** - * Sort the messages in the folder according to the sort criteria. - * The messages are returned in the sorted order, but the order of - * the messages in the folder is not changed.

    - * - * Depends on the SORT extension - - * RFC 5256. - * - * @since JavaMail 1.4.4 - */ - public synchronized Message[] getSortedMessages(SortTerm[] term) - throws MessagingException { - return getSortedMessages(term, null); - } - - /** - * Sort the messages in the folder according to the sort criteria. - * The messages are returned in the sorted order, but the order of - * the messages in the folder is not changed. Only messages matching - * the search criteria are considered.

    - * - * Depends on the SORT extension - - * RFC 5256. - * - * @since JavaMail 1.4.4 - */ - public synchronized Message[] getSortedMessages(SortTerm[] term, - SearchTerm sterm) throws MessagingException { - checkOpened(); - - try { - Message[] matchMsgs = null; - - synchronized(messageCacheLock) { - int[] matches = getProtocol().sort(term, sterm); - if (matches != null) { - matchMsgs = new IMAPMessage[matches.length]; - // Map seq-numbers into actual Messages. - for (int i = 0; i < matches.length; i++) - matchMsgs[i] = getMessageBySeqNumber(matches[i]); - } - } - return matchMsgs; - - } catch (CommandFailedException cfx) { - // unsupported charset or search criterion - throw new MessagingException(cfx.getMessage(), cfx); - } catch (SearchException sex) { - // too complex for IMAP - throw new MessagingException(sex.getMessage(), sex); - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - // bug in our IMAP layer ? - throw new MessagingException(pex.getMessage(), pex); - } - } - - /* - * Override Folder method to keep track of whether we have any - * message count listeners. Normally we won't have any, so we - * can avoid creating message objects to pass to the notify - * method. It's too hard to keep track of when all listeners - * are removed, and that's a rare case, so we don't try. - */ - public synchronized void addMessageCountListener(MessageCountListener l) { - super.addMessageCountListener(l); - hasMessageCountListener = true; - } - - /*********************************************************** - * UIDFolder interface methods - **********************************************************/ - - /** - * Returns the UIDValidity for this folder. - */ - public synchronized long getUIDValidity() throws MessagingException { - if (opened) // we already have this information - return uidvalidity; - - IMAPProtocol p = null; - Status status = null; - - try { - p = getStoreProtocol(); // XXX - String[] item = { "UIDVALIDITY" }; - status = p.status(fullName, item); - } catch (BadCommandException bex) { - // Probably a RFC1730 server - throw new MessagingException("Cannot obtain UIDValidity", bex); - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - - return status.uidvalidity; - } - - /** - * Returns the predicted UID that will be assigned to the - * next message that is appended to this folder. - * If the folder is closed, the STATUS command is used to - * retrieve this value. If the folder is open, the value - * returned from the SELECT or EXAMINE command is returned. - * Note that messages may have been appended to the folder - * while it was open and thus this value may be out of - * date.

    - * - * Servers implementing RFC2060 likely won't return this value - * when a folder is opened. Servers implementing RFC3501 - * should return this value when a folder is opened.

    - * - * @return the UIDNEXT value, or -1 if unknown - * @since JavaMail 1.3.3 - */ - // Not a UIDFolder method, but still useful - public synchronized long getUIDNext() throws MessagingException { - if (opened) // we already have this information - return uidnext; - - IMAPProtocol p = null; - Status status = null; - - try { - p = getStoreProtocol(); // XXX - String[] item = { "UIDNEXT" }; - status = p.status(fullName, item); - } catch (BadCommandException bex) { - // Probably a RFC1730 server - throw new MessagingException("Cannot obtain UIDNext", bex); - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - - return status.uidnext; - } - - /** - * Get the Message corresponding to the given UID. - * If no such message exists, null is returned. - */ - public synchronized Message getMessageByUID(long uid) - throws MessagingException { - checkOpened(); // insure folder is open - - IMAPMessage m = null; - - try { - synchronized(messageCacheLock) { - Long l = new Long(uid); - - if (uidTable != null) { - // Check in uidTable - m = (IMAPMessage)uidTable.get(l); - if (m != null) // found it - return m; - } else - uidTable = new Hashtable(); - - // Check with the server - // Issue UID FETCH command - UID u = getProtocol().fetchSequenceNumber(uid); - - if (u != null && u.seqnum <= total) { // Valid UID - m = getMessageBySeqNumber(u.seqnum); - m.setUID(u.uid); // set this message's UID .. - // .. and put this into the hashtable - uidTable.put(l, m); - } - } - } catch(ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - - return m; - } - - /** - * Get the Messages specified by the given range.

    - * Returns Message objects for all valid messages in this range. - * Returns an empty array if no messages are found. - */ - public synchronized Message[] getMessagesByUID(long start, long end) - throws MessagingException { - checkOpened(); // insure that folder is open - - Message[] msgs; // array of messages to be returned - - try { - synchronized(messageCacheLock) { - if (uidTable == null) - uidTable = new Hashtable(); - - // Issue UID FETCH for given range - UID[] ua = getProtocol().fetchSequenceNumbers(start, end); - - msgs = new Message[ua.length]; - IMAPMessage m; - // NOTE: Below must be within messageCacheLock region - for (int i = 0; i < ua.length; i++) { - m = getMessageBySeqNumber(ua[i].seqnum); - m.setUID(ua[i].uid); - msgs[i] = m; - uidTable.put(new Long(ua[i].uid), m); - } - } - } catch(ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - - return msgs; - } - - /** - * Get the Messages specified by the given array.

    - * - * uids.length() elements are returned. - * If any UID in the array is invalid, a null entry - * is returned for that element. - */ - public synchronized Message[] getMessagesByUID(long[] uids) - throws MessagingException { - checkOpened(); // insure that folder is open - - try { - synchronized(messageCacheLock) { - long[] unavailUids = uids; - if (uidTable != null) { - Vector v = new Vector(); // to collect unavailable UIDs - Long l; - for (int i = 0; i < uids.length; i++) { - if (!uidTable.containsKey(l = new Long(uids[i]))) - // This UID has not been loaded yet. - v.addElement(l); - } - - int vsize = v.size(); - unavailUids = new long[vsize]; - for (int i = 0; i < vsize; i++) - unavailUids[i] = ((Long)v.elementAt(i)).longValue(); - } else - uidTable = new Hashtable(); - - if (unavailUids.length > 0) { - // Issue UID FETCH request for given uids - UID[] ua = getProtocol().fetchSequenceNumbers(unavailUids); - IMAPMessage m; - for (int i = 0; i < ua.length; i++) { - m = getMessageBySeqNumber(ua[i].seqnum); - m.setUID(ua[i].uid); - uidTable.put(new Long(ua[i].uid), m); - } - } - - // Return array of size = uids.length - Message[] msgs = new Message[uids.length]; - for (int i = 0; i < uids.length; i++) - msgs[i] = (Message)uidTable.get(new Long(uids[i])); - return msgs; - } - } catch(ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - /** - * Get the UID for the specified message. - */ - public synchronized long getUID(Message message) - throws MessagingException { - if (message.getFolder() != this) - throw new NoSuchElementException( - "Message does not belong to this folder"); - - checkOpened(); // insure that folder is open - - IMAPMessage m = (IMAPMessage)message; - // If the message already knows its UID, great .. - long uid; - if ((uid = m.getUID()) != -1) - return uid; - - synchronized(messageCacheLock) { // Acquire Lock - try { - IMAPProtocol p = getProtocol(); - m.checkExpunged(); // insure that message is not expunged - UID u = p.fetchUID(m.getSequenceNumber()); - - if (u != null) { - uid = u.uid; - m.setUID(uid); // set message's UID - - // insert this message into uidTable - if (uidTable == null) - uidTable = new Hashtable(); - uidTable.put(new Long(uid), m); - } - } catch (ConnectionException cex) { - throw new FolderClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - return uid; - } - - /** - * Get the quotas for the quotaroot associated with this - * folder. Note that many folders may have the same quotaroot. - * Quotas are controlled on the basis of a quotaroot, not - * (necessarily) a folder. The relationship between folders - * and quotaroots depends on the IMAP server. Some servers - * might implement a single quotaroot for all folders owned by - * a user. Other servers might implement a separate quotaroot - * for each folder. A single folder can even have multiple - * quotaroots, perhaps controlling quotas for different - * resources. - * - * @return array of Quota objects for the quotaroots associated with - * this folder - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - public Quota[] getQuota() throws MessagingException { - return (Quota[])doOptionalCommand("QUOTA not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.getQuotaRoot(fullName); - } - }); - } - - /** - * Set the quotas for the quotaroot specified in the quota argument. - * Typically this will be one of the quotaroots associated with this - * folder, as obtained from the getQuota method, but it - * need not be. - * - * @param quota the quota to set - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - public void setQuota(final Quota quota) throws MessagingException { - doOptionalCommand("QUOTA not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - p.setQuota(quota); - return null; - } - }); - } - - /** - * Get the access control list entries for this folder. - * - * @return array of access control list entries - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public ACL[] getACL() throws MessagingException { - return (ACL[])doOptionalCommand("ACL not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.getACL(fullName); - } - }); - } - - /** - * Add an access control list entry to the access control list - * for this folder. - * - * @param acl the access control list entry to add - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public void addACL(ACL acl) throws MessagingException { - setACL(acl, '\0'); - } - - /** - * Remove any access control list entry for the given identifier - * from the access control list for this folder. - * - * @param name the identifier for which to remove all ACL entries - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public void removeACL(final String name) throws MessagingException { - doOptionalCommand("ACL not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - p.deleteACL(fullName, name); - return null; - } - }); - } - - /** - * Add the rights specified in the ACL to the entry for the - * identifier specified in the ACL. If an entry for the identifier - * doesn't already exist, add one. - * - * @param acl the identifer and rights to add - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public void addRights(ACL acl) throws MessagingException { - setACL(acl, '+'); - } - - /** - * Remove the rights specified in the ACL from the entry for the - * identifier specified in the ACL. - * - * @param acl the identifer and rights to remove - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public void removeRights(ACL acl) throws MessagingException { - setACL(acl, '-'); - } - - /** - * Get all the rights that may be allowed to the given identifier. - * Rights are grouped per RFC 2086 and each group is returned as an - * element of the array. The first element of the array is the set - * of rights that are always granted to the identifier. Later - * elements are rights that may be optionally granted to the - * identifier.

    - * - * Note that this method lists the rights that it is possible to - * assign to the given identifier, not the rights that are - * actually granted to the given identifier. For the latter, see - * the getACL method. - * - * @param name the identifier to list rights for - * @return array of Rights objects representing possible - * rights for the identifier - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public Rights[] listRights(final String name) throws MessagingException { - return (Rights[])doOptionalCommand("ACL not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.listRights(fullName, name); - } - }); - } - - /** - * Get the rights allowed to the currently authenticated user. - * - * @return the rights granted to the current user - * @exception MessagingException if the server doesn't support the - * ACL extension - */ - public Rights myRights() throws MessagingException { - return (Rights)doOptionalCommand("ACL not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - return p.myRights(fullName); - } - }); - } - - private void setACL(final ACL acl, final char mod) - throws MessagingException { - doOptionalCommand("ACL not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - p.setACL(fullName, mod, acl); - return null; - } - }); - } - - /** - * Get the attributes that the IMAP server returns with the - * LIST response. - * - * @since JavaMail 1.3.3 - */ - public synchronized String[] getAttributes() throws MessagingException { - checkExists(); - if (attributes == null) - exists(); // do a LIST to set the attributes - return attributes == null ? new String[0] : - (String[])(attributes.clone()); - } - - /** - * Use the IMAP IDLE command (see - * RFC 2177), - * if supported by the server, to enter idle mode so that the server - * can send unsolicited notifications of new messages arriving, etc. - * without the need for the client to constantly poll the server. - * Use an appropriate listener to be notified of new messages or - * other events. When another thread (e.g., the listener thread) - * needs to issue an IMAP comand for this folder, the idle mode will - * be terminated and this method will return. Typically the caller - * will invoke this method in a loop.

    - * - * The mail.imap.minidletime property enforces a minimum delay - * before returning from this method, to ensure that other threads - * have a chance to issue commands before the caller invokes this - * method again. The default delay is 10 milliseconds. - * - * @exception MessagingException if the server doesn't support the - * IDLE extension - * @exception IllegalStateException if the folder isn't open - * - * @since JavaMail 1.4.1 - */ - public void idle() throws MessagingException { - idle(false); - } - - /** - * Like {@link #idle}, but if once is true, abort the - * IDLE command after the first notification, to allow the caller - * to process any notification synchronously. - * - * @exception MessagingException if the server doesn't support the - * IDLE extension - * @exception IllegalStateException if the folder isn't open - * - * @since JavaMail 1.4.3 - */ - public void idle(boolean once) throws MessagingException { - // ASSERT: Must NOT be called with this folder's - // synchronization lock held. - assert !Thread.holdsLock(this); - synchronized(this) { - checkOpened(); - Boolean started = (Boolean)doOptionalCommand("IDLE not supported", - new ProtocolCommand() { - public Object doCommand(IMAPProtocol p) - throws ProtocolException { - if (idleState == RUNNING) { - p.idleStart(); - idleState = IDLE; - return Boolean.TRUE; - } else { - // some other thread must be running the IDLE - // command, we'll just wait for it to finish - // without aborting it ourselves - try { - // give up lock and wait to be not idle - messageCacheLock.wait(); - } catch (InterruptedException ex) { } - return Boolean.FALSE; - } - } - }); - if (!started.booleanValue()) - return; - } - - /* - * We gave up the folder lock so that other threads - * can get into the folder far enough to see that we're - * in IDLE and abort the IDLE. - * - * Now we read responses from the IDLE command, especially - * including unsolicited notifications from the server. - * We don't hold the messageCacheLock while reading because - * it protects the idleState and other threads need to be - * able to examine the state. - * - * We hold the messageCacheLock while processing the - * responses so that we can update the number of messages - * in the folder (for example). - */ - for (;;) { - Response r = protocol.readIdleResponse(); - try { - synchronized (messageCacheLock) { - try { - if (r == null || protocol == null || - !protocol.processIdleResponse(r)) { - idleState = RUNNING; - messageCacheLock.notifyAll(); - break; - } - } catch (ProtocolException pex) { - idleState = RUNNING; - messageCacheLock.notifyAll(); - throw pex; - } - if (once) { - if (idleState == IDLE) { - protocol.idleAbort(); - idleState = ABORTING; - } - } - } - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - - /* - * Enforce a minimum delay to give time to threads - * processing the responses that came in while we - * were idle. - */ - int minidle = ((IMAPStore)store).getMinIdleTime(); - if (minidle > 0) { - try { - Thread.sleep(minidle); - } catch (InterruptedException ex) { } - } - } - - /* - * If an IDLE command is in progress, abort it if necessary, - * and wait until it completes. - * ASSERT: Must be called with the message cache lock held. - */ - void waitIfIdle() throws ProtocolException { - assert Thread.holdsLock(messageCacheLock); - while (idleState != RUNNING) { - if (idleState == IDLE) { - protocol.idleAbort(); - idleState = ABORTING; - } - try { - // give up lock and wait to be not idle - messageCacheLock.wait(); - } catch (InterruptedException ex) { } - } - } - - /** - * The response handler. This is the callback routine that is - * invoked by the protocol layer. - */ - /* - * ASSERT: This method must be called only when holding the - * messageCacheLock. - * ASSERT: This method must *not* invoke any other method that - * might grab the 'folder' lock or 'message' lock (i.e., any - * synchronized methods on IMAPFolder or IMAPMessage) - * since that will result in violating the locking hierarchy. - */ - public void handleResponse(Response r) { - assert Thread.holdsLock(messageCacheLock); - - /* - * First, delegate possible ALERT or notification to the Store. - */ - if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) - ((IMAPStore)store).handleResponseCode(r); - - /* - * Now check whether this is a BYE or OK response and - * handle appropriately. - */ - if (r.isBYE()) { - if (opened) // XXX - accessed without holding folder lock - cleanup(false); - return; - } else if (r.isOK()) { - return; - } else if (!r.isUnTagged()) { - return; // might be a continuation for IDLE - } - - /* Now check whether this is an IMAP specific response */ - if (!(r instanceof IMAPResponse)) { - // Probably a bug in our code ! - // XXX - should be an assert - logger.fine("UNEXPECTED RESPONSE : " + r.toString()); - return; - } - - IMAPResponse ir = (IMAPResponse)r; - - if (ir.keyEquals("EXISTS")) { // EXISTS - int exists = ir.getNumber(); - if (exists <= realTotal) - // Could be the EXISTS following EXPUNGE, ignore 'em - return; - - int count = exists - realTotal; // number of new messages - Message[] msgs = new Message[count]; - - // Add 'count' new IMAPMessage objects into the messageCache - messageCache.addMessages(count, realTotal + 1); - int oldtotal = total; // used in loop below - realTotal += count; - total += count; - - // avoid instantiating Message objects if no listeners. - if (hasMessageCountListener) { - for (int i = 0; i < count; i++) - msgs[i] = messageCache.getMessage(++oldtotal); - - // Notify listeners. - notifyMessageAddedListeners(msgs); - } - - } else if (ir.keyEquals("EXPUNGE")) { - // EXPUNGE response. - - int seqnum = ir.getNumber(); - Message[] msgs = null; - if (doExpungeNotification && hasMessageCountListener) { - // save the Message object first; can't look it - // up after it's expunged - msgs = new Message[] { getMessageBySeqNumber(seqnum) }; - } - - messageCache.expungeMessage(seqnum); - - // decrement 'realTotal'; but leave 'total' unchanged - realTotal--; - - if (msgs != null) // Do the notification here. - notifyMessageRemovedListeners(false, msgs); - - } else if (ir.keyEquals("FETCH")) { - // The only unsolicited FETCH response that makes sense - // to me (for now) is FLAGS updates. Ignore any other junk. - assert ir instanceof FetchResponse : "!ir instanceof FetchResponse"; - FetchResponse f = (FetchResponse)ir; - // Get FLAGS response, if present - Flags flags = (Flags)f.getItem(Flags.class); - - if (flags != null) { - IMAPMessage msg = getMessageBySeqNumber(f.getNumber()); - if (msg != null) { // should always be true - msg._setFlags(flags); - notifyMessageChangedListeners( - MessageChangedEvent.FLAGS_CHANGED, msg); - } - } - - } else if (ir.keyEquals("RECENT")) { - // update 'recent' - recent = ir.getNumber(); - } - } - - /** - * Handle the given array of Responses. - * - * ASSERT: This method must be called only when holding the - * messageCacheLock - */ - void handleResponses(Response[] r) { - for (int i = 0; i < r.length; i++) { - if (r[i] != null) - handleResponse(r[i]); - } - } - - /** - * Get this folder's Store's protocol connection. - * - * When acquiring a store protocol object, it is important to - * use the following steps: - * - * IMAPProtocol p = null; - * try { - * p = getStoreProtocol(); - * // perform the command - * } catch (WhateverException ex) { - * // handle it - * } finally { - * releaseStoreProtocol(p); - * } - */ - protected synchronized IMAPProtocol getStoreProtocol() - throws ProtocolException { - connectionPoolLogger.fine("getStoreProtocol() borrowing a connection"); - return ((IMAPStore)store).getFolderStoreProtocol(); - } - - /** - * Throw the appropriate 'closed' exception. - */ - protected synchronized void throwClosedException(ConnectionException cex) - throws FolderClosedException, StoreClosedException { - // If it's the folder's protocol object, throw a FolderClosedException; - // otherwise, throw a StoreClosedException. - // If a command has failed because the connection is closed, - // the folder will have already been forced closed by the - // time we get here and our protocol object will have been - // released, so if we no longer have a protocol object we base - // this decision on whether we *think* the folder is open. - if ((protocol != null && cex.getProtocol() == protocol) || - (protocol == null && !reallyClosed)) - throw new FolderClosedException(this, cex.getMessage()); - else - throw new StoreClosedException(store, cex.getMessage()); - } - - /** - * Return the IMAPProtocol object for this folder.

    - * - * This method will block if necessary to wait for an IDLE - * command to finish. - * - * @return the IMAPProtocol object used when the folder is open - */ - protected IMAPProtocol getProtocol() throws ProtocolException { - assert Thread.holdsLock(messageCacheLock); - waitIfIdle(); - return protocol; - } - - /** - * A simple interface for user-defined IMAP protocol commands. - */ - public static interface ProtocolCommand { - /** - * Execute the user-defined command using the supplied IMAPProtocol - * object. - */ - public Object doCommand(IMAPProtocol protocol) throws ProtocolException; - } - - /** - * Execute a user-supplied IMAP command. The command is executed - * in the appropriate context with the necessary locks held and - * using the appropriate IMAPProtocol object.

    - * - * This method returns whatever the ProtocolCommand - * object's doCommand method returns. If the - * doCommand method throws a ConnectionException - * it is translated into a StoreClosedException or - * FolderClosedException as appropriate. If the - * doCommand method throws a ProtocolException - * it is translated into a MessagingException.

    - * - * The following example shows how to execute the IMAP NOOP command. - * Executing more complex IMAP commands requires intimate knowledge - * of the com.sun.mail.iap and - * com.sun.mail.imap.protocol packages, best acquired by - * reading the source code.

    - * - *

    -     * import com.sun.mail.iap.*;
    -     * import com.sun.mail.imap.*;
    -     * import com.sun.mail.imap.protocol.*;
    -     *
    -     * ...
    -     *
    -     * IMAPFolder f = (IMAPFolder)folder;
    -     * Object val = f.doCommand(new IMAPFolder.ProtocolCommand() {
    -     *	public Object doCommand(IMAPProtocol p)
    -     *			throws ProtocolException {
    -     *	    p.simpleCommand("NOOP", null);
    -     *	    return null;
    -     *	}
    -     * });
    -     * 
    - *

    - * - * Here's a more complex example showing how to use the proposed - * IMAP SORT extension:

    - * - *

    - * import com.sun.mail.iap.*; - * import com.sun.mail.imap.*; - * import com.sun.mail.imap.protocol.*; - * - * ... - * - * IMAPFolder f = (IMAPFolder)folder; - * Object val = f.doCommand(new IMAPFolder.ProtocolCommand() { - * public Object doCommand(IMAPProtocol p) - * throws ProtocolException { - * // Issue command - * Argument args = new Argument(); - * Argument list = new Argument(); - * list.writeString("SUBJECT"); - * args.writeArgument(list); - * args.writeString("UTF-8"); - * args.writeString("ALL"); - * Response[] r = p.command("SORT", args); - * Response response = r[r.length-1]; - * - * // Grab response - * Vector v = new Vector(); - * if (response.isOK()) { // command succesful - * for (int i = 0, len = r.length; i < len; i++) { - * if (!(r[i] instanceof IMAPResponse)) - * continue; - * - * IMAPResponse ir = (IMAPResponse)r[i]; - * if (ir.keyEquals("SORT")) { - * String num; - * while ((num = ir.readAtomString()) != null) - * System.out.println(num); - * r[i] = null; - * } - * } - * } - * - * // dispatch remaining untagged responses - * p.notifyResponseHandlers(r); - * p.handleResult(response); - * - * return null; - * } - * }); - *
    - */ - public Object doCommand(ProtocolCommand cmd) throws MessagingException { - try { - return doProtocolCommand(cmd); - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - return null; - } - - public Object doOptionalCommand(String err, ProtocolCommand cmd) - throws MessagingException { - try { - return doProtocolCommand(cmd); - } catch (BadCommandException bex) { - throw new MessagingException(err, bex); - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - return null; - } - - public Object doCommandIgnoreFailure(ProtocolCommand cmd) - throws MessagingException { - try { - return doProtocolCommand(cmd); - } catch (CommandFailedException cfx) { - return null; - } catch (ConnectionException cex) { - // Oops, the store or folder died on us. - throwClosedException(cex); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - return null; - } - - protected Object doProtocolCommand(ProtocolCommand cmd) - throws ProtocolException { - synchronized (this) { - /* - * Check whether we have a protocol object, not whether we're - * opened, to allow use of the exsting protocol object in the - * open method before the state is changed to "opened". - */ - if (protocol != null) { - synchronized (messageCacheLock) { - return cmd.doCommand(getProtocol()); - } - } - } - - // only get here if using store's connection - IMAPProtocol p = null; - - try { - p = getStoreProtocol(); - return cmd.doCommand(p); - } finally { - releaseStoreProtocol(p); - } - } - - /** - * Release the store protocol object. If we borrowed a protocol - * object from the connection pool, give it back. If we used our - * own protocol object, nothing to do. - */ - protected synchronized void releaseStoreProtocol(IMAPProtocol p) { - if (p != protocol) - ((IMAPStore)store).releaseFolderStoreProtocol(p); - else { - // XXX - should never happen - logger.fine("releasing our protocol as store protocol?"); - } - } - - /** - * Release the protocol object. - * - * ASSERT: This method must be called only when holding the - * messageCacheLock - */ - protected void releaseProtocol(boolean returnToPool) { - if (protocol != null) { - protocol.removeResponseHandler(this); - - if (returnToPool) - ((IMAPStore)store).releaseProtocol(this, protocol); - else { - protocol.disconnect(); // make sure it's disconnected - ((IMAPStore)store).releaseProtocol(this, null); - } - protocol = null; - } - } - - /** - * Issue a noop command for the connection if the connection has not been - * used in more than a second. If keepStoreAlive is true, - * also issue a noop over the store's connection. - * - * ASSERT: This method must be called only when holding the - * messageCacheLock - */ - protected void keepConnectionAlive(boolean keepStoreAlive) - throws ProtocolException { - - if (System.currentTimeMillis() - protocol.getTimestamp() > 1000) { - waitIfIdle(); - if (protocol != null) - protocol.noop(); - } - - if (keepStoreAlive && ((IMAPStore)store).hasSeparateStoreConnection()) { - IMAPProtocol p = null; - try { - p = ((IMAPStore)store).getFolderStoreProtocol(); - if (System.currentTimeMillis() - p.getTimestamp() > 1000) - p.noop(); - } finally { - ((IMAPStore)store).releaseFolderStoreProtocol(p); - } - } - } - - /** - * Get the message object for the given sequence number. If - * none found, null is returned. - * - * ASSERT: This method must be called only when holding the - * messageCacheLock - */ - protected IMAPMessage getMessageBySeqNumber(int seqnum) { - return messageCache.getMessageBySeqnum(seqnum); - } - - private boolean isDirectory() { - return ((type & HOLDS_FOLDERS) != 0); - } -} - -/** - * An object that holds a Message object - * and reports its size and writes it to another OutputStream - * on demand. Used by appendMessages to avoid the need to - * buffer the entire message in memory in a single byte array - * before sending it to the server. - */ -class MessageLiteral implements Literal { - private Message msg; - private int msgSize = -1; - private byte[] buf; // the buffered message, if not null - - public MessageLiteral(Message msg, int maxsize) - throws MessagingException, IOException { - this.msg = msg; - // compute the size here so exceptions can be returned immediately - LengthCounter lc = new LengthCounter(maxsize); - OutputStream os = new CRLFOutputStream(lc); - msg.writeTo(os); - os.flush(); - msgSize = lc.getSize(); - buf = lc.getBytes(); - } - - public int size() { - return msgSize; - } - - public void writeTo(OutputStream os) throws IOException { - // the message should not change between the constructor and this call - try { - if (buf != null) - os.write(buf, 0, msgSize); - else { - os = new CRLFOutputStream(os); - msg.writeTo(os); - } - } catch (MessagingException mex) { - // exceptions here are bad, "should" never happen - throw new IOException("MessagingException while appending message: " - + mex); - } - } -} - -/** - * Count the number of bytes written to the stream. - * Also, save a copy of small messages to avoid having to process - * the data again. - */ -class LengthCounter extends OutputStream { - private int size = 0; - private byte[] buf; - private int maxsize; - - public LengthCounter(int maxsize) { - buf = new byte[8192]; - this.maxsize = maxsize; - } - - public void write(int b) { - int newsize = size + 1; - if (buf != null) { - if (newsize > maxsize && maxsize >= 0) { - buf = null; - } else if (newsize > buf.length) { - byte newbuf[] = new byte[Math.max(buf.length << 1, newsize)]; - System.arraycopy(buf, 0, newbuf, 0, size); - buf = newbuf; - buf[size] = (byte)b; - } else { - buf[size] = (byte)b; - } - } - size = newsize; - } - - public void write(byte b[], int off, int len) { - if ((off < 0) || (off > b.length) || (len < 0) || - ((off + len) > b.length) || ((off + len) < 0)) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return; - } - int newsize = size + len; - if (buf != null) { - if (newsize > maxsize && maxsize >= 0) { - buf = null; - } else if (newsize > buf.length) { - byte newbuf[] = new byte[Math.max(buf.length << 1, newsize)]; - System.arraycopy(buf, 0, newbuf, 0, size); - buf = newbuf; - System.arraycopy(b, off, buf, size, len); - } else { - System.arraycopy(b, off, buf, size, len); - } - } - size = newsize; - } - - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - public int getSize() { - return size; - } - - public byte[] getBytes() { - return buf; - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPInputStream.java b/src/main/java/com/sun/mail/imap/IMAPInputStream.java deleted file mode 100644 index e44ef11f..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPInputStream.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.io.*; -import javax.mail.*; -import com.sun.mail.imap.protocol.*; -import com.sun.mail.iap.*; -import com.sun.mail.util.*; - -/** - * This class implements an IMAP data stream. - * - * @author John Mani - */ - -public class IMAPInputStream extends InputStream { - private IMAPMessage msg; // this message - private String section; // section-id - private int pos; // track the position within the IMAP datastream - private int blksize; // number of bytes to read in each FETCH request - private int max; // the total number of bytes in this section. - // -1 indicates unknown - private byte[] buf; // the buffer obtained from fetchBODY() - private int bufcount; // The index one greater than the index of the - // last valid byte in 'buf' - private int bufpos; // The current position within 'buf' - private boolean lastBuffer; // is this the last buffer of data? - private boolean peek; // peek instead of fetch? - private ByteArray readbuf; // reuse for each read - - // Allocate this much extra space in the read buffer to allow - // space for the FETCH response overhead - private static final int slop = 64; - - - /** - * Create an IMAPInputStream. - */ - public IMAPInputStream(IMAPMessage msg, String section, int max, - boolean peek) { - this.msg = msg; - this.section = section; - this.max = max; - this.peek = peek; - pos = 0; - blksize = msg.getFetchBlockSize(); - } - - /** - * Do a NOOP to force any untagged EXPUNGE responses - * and then check if this message is expunged. - */ - private void forceCheckExpunged() - throws MessageRemovedIOException, FolderClosedIOException { - synchronized (msg.getMessageCacheLock()) { - try { - msg.getProtocol().noop(); - } catch (ConnectionException cex) { - throw new FolderClosedIOException(msg.getFolder(), - cex.getMessage()); - } catch (FolderClosedException fex) { - throw new FolderClosedIOException(fex.getFolder(), - fex.getMessage()); - } catch (ProtocolException pex) { - // ignore it - } - } - if (msg.isExpunged()) - throw new MessageRemovedIOException(); - } - - /** - * Fetch more data from the server. This method assumes that all - * data has already been read in, hence bufpos > bufcount. - */ - private void fill() throws IOException { - /* - * If we've read the last buffer, there's no more to read. - * If we know the total number of bytes available from this - * section, let's check if we have consumed that many bytes. - */ - if (lastBuffer || max != -1 && pos >= max) { - if (pos == 0) - checkSeen(); - readbuf = null; // XXX - return to pool? - return; // the caller of fill() will return -1. - } - - BODY b = null; - if (readbuf == null) - readbuf = new ByteArray(blksize + slop); - - ByteArray ba; - int cnt; - // Acquire MessageCacheLock, to freeze seqnum. - synchronized (msg.getMessageCacheLock()) { - try { - IMAPProtocol p = msg.getProtocol(); - - // Check whether this message is expunged - if (msg.isExpunged()) - throw new MessageRemovedIOException( - "No content for expunged message"); - - int seqnum = msg.getSequenceNumber(); - cnt = blksize; - if (max != -1 && pos + blksize > max) - cnt = max - pos; - if (peek) - b = p.peekBody(seqnum, section, pos, cnt, readbuf); - else - b = p.fetchBody(seqnum, section, pos, cnt, readbuf); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new IOException(pex.getMessage()); - } catch (FolderClosedException fex) { - throw new FolderClosedIOException(fex.getFolder(), - fex.getMessage()); - } - - if (b == null || ((ba = b.getByteArray()) == null)) { - forceCheckExpunged(); - throw new IOException("No content"); - } - } - - // make sure the SEEN flag is set after reading the first chunk - if (pos == 0) - checkSeen(); - - // setup new values .. - buf = ba.getBytes(); - bufpos = ba.getStart(); - int n = ba.getCount(); // will be zero, if all data has been - // consumed from the server. - // if we got less than we asked for, this is the last buffer of data - lastBuffer = n < cnt; - bufcount = bufpos + n; - pos += n; - } - - /** - * Reads the next byte of data from this buffered input stream. - * If no byte is available, the value -1 is returned. - */ - public synchronized int read() throws IOException { - if (bufpos >= bufcount) { - fill(); - if (bufpos >= bufcount) - return -1; // EOF - } - return buf[bufpos++] & 0xff; - } - - /** - * Reads up to len bytes of data from this - * input stream into the given buffer.

    - * - * Returns the total number of bytes read into the buffer, - * or -1 if there is no more data.

    - * - * Note that this method mimics the "weird !" semantics of - * BufferedInputStream in that the number of bytes actually - * returned may be less that the requested value. So callers - * of this routine should be aware of this and must check - * the return value to insure that they have obtained the - * requisite number of bytes. - */ - public synchronized int read(byte b[], int off, int len) - throws IOException { - - int avail = bufcount - bufpos; - if (avail <= 0) { - fill(); - avail = bufcount - bufpos; - if (avail <= 0) - return -1; // EOF - } - int cnt = (avail < len) ? avail : len; - System.arraycopy(buf, bufpos, b, off, cnt); - bufpos += cnt; - return cnt; - } - - /** - * Reads up to b.length bytes of data from this input - * stream into an array of bytes.

    - * - * Returns the total number of bytes read into the buffer, or - * -1 is there is no more data.

    - * - * Note that this method mimics the "weird !" semantics of - * BufferedInputStream in that the number of bytes actually - * returned may be less that the requested value. So callers - * of this routine should be aware of this and must check - * the return value to insure that they have obtained the - * requisite number of bytes. - */ - public int read(byte b[]) throws IOException { - return read(b, 0, b.length); - } - - /** - * Returns the number of bytes that can be read from this input - * stream without blocking. - */ - public synchronized int available() throws IOException { - return (bufcount - bufpos); - } - - /** - * Normally the SEEN flag will have been set by now, but if not, - * force it to be set (as long as the folder isn't open read-only - * and we're not peeking). - * And of course, if there's no folder (e.g., a nested message) - * don't do anything. - */ - private void checkSeen() { - if (peek) // if we're peeking, don't set the SEEN flag - return; - try { - Folder f = msg.getFolder(); - if (f != null && f.getMode() != Folder.READ_ONLY && - !msg.isSet(Flags.Flag.SEEN)) - msg.setFlag(Flags.Flag.SEEN, true); - } catch (MessagingException ex) { - // ignore it - } - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPMessage.java b/src/main/java/com/sun/mail/imap/IMAPMessage.java deleted file mode 100644 index f3233b49..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPMessage.java +++ /dev/null @@ -1,1468 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.util.Date; -import java.io.*; -import java.util.*; - -import javax.mail.*; -import javax.mail.internet.*; -import javax.activation.*; - -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; - -/** - * This class implements an IMAPMessage object.

    - * - * An IMAPMessage object starts out as a light-weight object. It gets - * filled-in incrementally when a request is made for some item. Or - * when a prefetch is done using the FetchProfile.

    - * - * An IMAPMessage has a messageNumber and a sequenceNumber. The - * messageNumber is its index into its containing folder's messageCache. - * The sequenceNumber is its IMAP sequence-number. - * - * @author John Mani - * @author Bill Shannon - */ -/* - * The lock hierarchy is that the lock on the IMAPMessage object, if - * it's acquired at all, must be acquired before the message cache lock. - * The IMAPMessage lock protects the message flags, sort of. - * - * XXX - I'm not convinced that all fields of IMAPMessage are properly - * protected by locks. - */ - -public class IMAPMessage extends MimeMessage implements ReadableMime { - protected BODYSTRUCTURE bs; // BODYSTRUCTURE - protected ENVELOPE envelope; // ENVELOPE - - /** - * A map of the extension FETCH items. In addition to saving the - * data in this map, an entry in this map indicates that we *have* - * the data, and so it doesn't need to be fetched again. The map - * is created only when needed, to avoid significantly increasing - * the effective size of an IMAPMessage object. - * - * @since JavaMail 1.4.6 - */ - protected Map items; // Map - - private Date receivedDate; // INTERNALDATE - private int size = -1; // RFC822.SIZE - - private boolean peek; // use BODY.PEEK when fetching content? - - // this message's IMAP UID - private long uid = -1; - - // this message's IMAP sectionId (null for toplevel message, - // non-null for a nested message) - protected String sectionId; - - // processed values - private String type; // Content-Type (with params) - private String subject; // decoded (Unicode) subject - private String description; // decoded (Unicode) desc - - // Indicates that we've loaded *all* headers for this message - private volatile boolean headersLoaded = false; - - /* Hashtable of names of headers we've loaded from the server. - * Used in isHeaderLoaded() and getHeaderLoaded() to keep track - * of those headers we've attempted to load from the server. We - * need this table of names to avoid multiple attempts at loading - * headers that don't exist for a particular message. - * - * Could this somehow be included in the InternetHeaders object ?? - */ - private Hashtable loadedHeaders = new Hashtable(1); - - // This is our Envelope - static final String EnvelopeCmd = "ENVELOPE INTERNALDATE RFC822.SIZE"; - - /** - * Constructor. - */ - protected IMAPMessage(IMAPFolder folder, int msgnum) { - super(folder, msgnum); - flags = null; - } - - /** - * Constructor, for use by IMAPNestedMessage. - */ - protected IMAPMessage(Session session) { - super(session); - } - - /** - * Get this message's folder's protocol connection. - * Throws FolderClosedException, if the protocol connection - * is not available. - * - * ASSERT: Must hold the messageCacheLock. - */ - protected IMAPProtocol getProtocol() - throws ProtocolException, FolderClosedException { - ((IMAPFolder)folder).waitIfIdle(); - IMAPProtocol p = ((IMAPFolder)folder).protocol; - if (p == null) - throw new FolderClosedException(folder); - else - return p; - } - - /* - * Is this an IMAP4 REV1 server? - */ - protected boolean isREV1() throws FolderClosedException { - // access the folder's protocol object without waiting - // for IDLE to complete - IMAPProtocol p = ((IMAPFolder)folder).protocol; - if (p == null) - throw new FolderClosedException(folder); - else - return p.isREV1(); - } - - /** - * Get the messageCacheLock, associated with this Message's - * Folder. - */ - protected Object getMessageCacheLock() { - return ((IMAPFolder)folder).messageCacheLock; - } - - /** - * Get this message's IMAP sequence number. - * - * ASSERT: This method must be called only when holding the - * messageCacheLock. - */ - protected int getSequenceNumber() { - return ((IMAPFolder)folder).messageCache.seqnumOf(getMessageNumber()); - } - - /** - * Wrapper around the protected method Message.setMessageNumber() to - * make that method accessible to IMAPFolder. - */ - protected void setMessageNumber(int msgnum) { - super.setMessageNumber(msgnum); - } - - protected long getUID() { - return uid; - } - - protected void setUID(long uid) { - this.uid = uid; - } - - // expose to MessageCache - protected void setExpunged(boolean set) { - super.setExpunged(set); - } - - // Convenience routine - protected void checkExpunged() throws MessageRemovedException { - if (expunged) - throw new MessageRemovedException(); - } - - /** - * Do a NOOP to force any untagged EXPUNGE responses - * and then check if this message is expunged. - */ - protected void forceCheckExpunged() - throws MessageRemovedException, FolderClosedException { - synchronized (getMessageCacheLock()) { - try { - getProtocol().noop(); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - // ignore it - } - } - if (expunged) - throw new MessageRemovedException(); - } - - // Return the block size for FETCH requests - // MUST be overridden by IMAPNestedMessage - protected int getFetchBlockSize() { - return ((IMAPStore)folder.getStore()).getFetchBlockSize(); - } - - // Return the block size for FETCH requests - // MUST be overridden by IMAPNestedMessage - protected boolean ignoreBodyStructureSize() { - return ((IMAPStore)folder.getStore()).ignoreBodyStructureSize(); - } - - /** - * Get the "From" attribute. - */ - public Address[] getFrom() throws MessagingException { - checkExpunged(); - loadEnvelope(); - InternetAddress[] a = envelope.from; - /* - * Per RFC 2822, the From header is required, and thus the IMAP - * spec also requires that it be present, but we know that in - * practice it is often missing. Some servers fill in the - * From field with the Sender field in this case, but at least - * Exchange 2007 does not. Use the same fallback strategy used - * by MimeMessage. - */ - if (a == null || a.length == 0) - a = envelope.sender; - return aaclone(a); - } - - public void setFrom(Address address) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - public void addFrom(Address[] addresses) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the "Sender" attribute. - */ - public Address getSender() throws MessagingException { - checkExpunged(); - loadEnvelope(); - if (envelope.sender != null) - return (envelope.sender)[0]; // there can be only one sender - else - return null; - } - - - public void setSender(Address address) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the desired Recipient type. - */ - public Address[] getRecipients(Message.RecipientType type) - throws MessagingException { - checkExpunged(); - loadEnvelope(); - - if (type == Message.RecipientType.TO) - return aaclone(envelope.to); - else if (type == Message.RecipientType.CC) - return aaclone(envelope.cc); - else if (type == Message.RecipientType.BCC) - return aaclone(envelope.bcc); - else - return super.getRecipients(type); - } - - public void setRecipients(Message.RecipientType type, Address[] addresses) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - public void addRecipients(Message.RecipientType type, Address[] addresses) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the ReplyTo addresses. - */ - public Address[] getReplyTo() throws MessagingException { - checkExpunged(); - loadEnvelope(); - /* - * The IMAP spec requires that the Reply-To field never be - * null, but at least Exchange 2007 fails to fill it in in - * some cases. Use the same fallback strategy used by - * MimeMessage. - */ - if (envelope.replyTo == null || envelope.replyTo.length == 0) - return getFrom(); - return aaclone(envelope.replyTo); - } - - public void setReplyTo(Address[] addresses) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the decoded subject. - */ - public String getSubject() throws MessagingException { - checkExpunged(); - - if (subject != null) // already cached ? - return subject; - - loadEnvelope(); - if (envelope.subject == null) // no subject - return null; - - // Cache and return the decoded value. - try { - // The server *should* unfold the value, but just in case it - // doesn't we unfold it here. - subject = - MimeUtility.decodeText(MimeUtility.unfold(envelope.subject)); - } catch (UnsupportedEncodingException ex) { - subject = envelope.subject; - } - - return subject; - } - - public void setSubject(String subject, String charset) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the SentDate. - */ - public Date getSentDate() throws MessagingException { - checkExpunged(); - loadEnvelope(); - if (envelope.date == null) - return null; - else - return new Date(envelope.date.getTime()); - } - - public void setSentDate(Date d) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the recieved date (INTERNALDATE) - */ - public Date getReceivedDate() throws MessagingException { - checkExpunged(); - loadEnvelope(); - if (receivedDate == null) - return null; - else - return new Date(receivedDate.getTime()); - } - - /** - * Get the message size.

    - * - * Note that this returns RFC822.SIZE. That is, it's the - * size of the whole message, header and body included. - */ - public int getSize() throws MessagingException { - checkExpunged(); - if (size == -1) - loadEnvelope(); // XXX - could just fetch the size - return size; - } - - /** - * Get the total number of lines.

    - * - * Returns the "body_fld_lines" field from the - * BODYSTRUCTURE. Note that this field is available - * only for text/plain and message/rfc822 types - */ - public int getLineCount() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - return bs.lines; - } - - /** - * Get the content language. - */ - public String[] getContentLanguage() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - if (bs.language != null) - return (String[])(bs.language).clone(); - else - return null; - } - - public void setContentLanguage(String[] languages) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the In-Reply-To header. - * - * @since JavaMail 1.3.3 - */ - public String getInReplyTo() throws MessagingException { - checkExpunged(); - loadEnvelope(); - return envelope.inReplyTo; - } - - /** - * Get the Content-Type. - * - * Generate this header from the BODYSTRUCTURE. Append parameters - * as well. - */ - public synchronized String getContentType() throws MessagingException { - checkExpunged(); - - // If we haven't cached the type yet .. - if (type == null) { - loadBODYSTRUCTURE(); - // generate content-type from BODYSTRUCTURE - ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams); - type = ct.toString(); - } - return type; - } - - /** - * Get the Content-Disposition. - */ - public String getDisposition() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - return bs.disposition; - } - - public void setDisposition(String disposition) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the Content-Transfer-Encoding. - */ - public String getEncoding() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - return bs.encoding; - } - - /** - * Get the Content-ID. - */ - public String getContentID() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - return bs.id; - } - - public void setContentID(String cid) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the Content-MD5. - */ - public String getContentMD5() throws MessagingException { - checkExpunged(); - loadBODYSTRUCTURE(); - return bs.md5; - } - - public void setContentMD5(String md5) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the decoded Content-Description. - */ - public String getDescription() throws MessagingException { - checkExpunged(); - - if (description != null) // cached value ? - return description; - - loadBODYSTRUCTURE(); - if (bs.description == null) - return null; - - try { - description = MimeUtility.decodeText(bs.description); - } catch (UnsupportedEncodingException ex) { - description = bs.description; - } - - return description; - } - - public void setDescription(String description, String charset) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get the Message-ID. - */ - public String getMessageID() throws MessagingException { - checkExpunged(); - loadEnvelope(); - return envelope.messageId; - } - - /** - * Get the "filename" Disposition parameter. (Only available in - * IMAP4rev1). If thats not available, get the "name" ContentType - * parameter. - */ - public String getFileName() throws MessagingException { - checkExpunged(); - - String filename = null; - loadBODYSTRUCTURE(); - - if (bs.dParams != null) - filename = bs.dParams.get("filename"); - if (filename == null && bs.cParams != null) - filename = bs.cParams.get("name"); - return filename; - } - - public void setFileName(String filename) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get all the bytes for this message. Overrides getContentStream() - * in MimeMessage. This method is ultimately used by the DataHandler - * to obtain the input stream for this message. - * - * @see javax.mail.internet.MimeMessage#getContentStream - */ - protected InputStream getContentStream() throws MessagingException { - InputStream is = null; - boolean pk = getPeek(); // get before acquiring message cache lock - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - // This message could be expunged when we were waiting - // to acquire the lock ... - checkExpunged(); - - if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1 - return new IMAPInputStream(this, toSection("TEXT"), - bs != null && !ignoreBodyStructureSize() ? - bs.size : -1, pk); - - if (p.isREV1()) { - BODY b; - if (pk) - b = p.peekBody(getSequenceNumber(), toSection("TEXT")); - else - b = p.fetchBody(getSequenceNumber(), toSection("TEXT")); - if (b != null) - is = b.getByteArrayInputStream(); - } else { - RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), "TEXT"); - if (rd != null) - is = rd.getByteArrayInputStream(); - } - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - } - - if (is == null) - throw new MessagingException("No content"); - else - return is; - } - - /** - * Get the DataHandler object for this message. - */ - public synchronized DataHandler getDataHandler() - throws MessagingException { - checkExpunged(); - - if (dh == null) { - loadBODYSTRUCTURE(); - if (type == null) { // type not yet computed - // generate content-type from BODYSTRUCTURE - ContentType ct = new ContentType(bs.type, bs.subtype, - bs.cParams); - type = ct.toString(); - } - - /* Special-case Multipart and Nested content. All other - * cases are handled by the superclass. - */ - if (bs.isMulti()) - dh = new DataHandler( - new IMAPMultipartDataSource(this, bs.bodies, - sectionId, this) - ); - else if (bs.isNested() && isREV1() && bs.envelope != null) - /* Nested messages are handled specially only for - * IMAP4rev1. IMAP4 doesn't provide enough support to - * FETCH the components of nested messages - */ - dh = new DataHandler( - new IMAPNestedMessage(this, - bs.bodies[0], - bs.envelope, - sectionId == null ? "1" : sectionId + ".1"), - type - ); - } - - return super.getDataHandler(); - } - - public void setDataHandler(DataHandler content) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Return the MIME format stream corresponding to this message. - * - * @return the MIME format stream - * @since JavaMail 1.4.5 - */ - public InputStream getMimeStream() throws MessagingException { - InputStream is = null; - boolean pk = getPeek(); // get before acquiring message cache lock - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - checkExpunged(); // insure this message is not expunged - - if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1 - return new IMAPInputStream(this, sectionId, -1, pk); - - if (p.isREV1()) { - BODY b; - if (pk) - b = p.peekBody(getSequenceNumber(), sectionId); - else - b = p.fetchBody(getSequenceNumber(), sectionId); - if (b != null) - is = b.getByteArrayInputStream(); - } else { - RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), null); - if (rd != null) - is = rd.getByteArrayInputStream(); - } - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - } - - if (is == null) { - forceCheckExpunged(); // may throw MessageRemovedException - // nope, the server doesn't think it's expunged, - // something else is wrong - throw new MessagingException("No content"); - } - return is; - } - - /** - * Write out the bytes into the given OutputStream. - */ - public void writeTo(OutputStream os) - throws IOException, MessagingException { - InputStream is = getMimeStream(); - try { - // write out the bytes - byte[] bytes = new byte[16*1024]; - int count; - while ((count = is.read(bytes)) != -1) - os.write(bytes, 0, count); - } finally { - is.close(); - } - } - - /** - * Get the named header. - */ - public String[] getHeader(String name) throws MessagingException { - checkExpunged(); - - if (isHeaderLoaded(name)) // already loaded ? - return headers.getHeader(name); - - // Load this particular header - InputStream is = null; - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - // This message could be expunged when we were waiting - // to acquire the lock ... - checkExpunged(); - - if (p.isREV1()) { - BODY b = p.peekBody(getSequenceNumber(), - toSection("HEADER.FIELDS (" + name + ")") - ); - if (b != null) - is = b.getByteArrayInputStream(); - } else { - RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), - "HEADER.LINES (" + name + ")"); - if (rd != null) - is = rd.getByteArrayInputStream(); - } - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - } - - // if we get this far without "is" being set, something has gone - // wrong; prevent a later NullPointerException and return null here - if (is == null) - return null; - - if (headers == null) - headers = new InternetHeaders(); - headers.load(is); // load this header into the Headers object. - setHeaderLoaded(name); // Mark this header as loaded - - return headers.getHeader(name); - } - - /** - * Get the named header. - */ - public String getHeader(String name, String delimiter) - throws MessagingException { - checkExpunged(); - - // force the header to be loaded by invoking getHeader(name) - if (getHeader(name) == null) - return null; - return headers.getHeader(name, delimiter); - } - - public void setHeader(String name, String value) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - public void addHeader(String name, String value) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - public void removeHeader(String name) - throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get all headers. - */ - public Enumeration getAllHeaders() throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getAllHeaders(); - } - - /** - * Get matching headers. - */ - public Enumeration getMatchingHeaders(String[] names) - throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getMatchingHeaders(names); - } - - /** - * Get non-matching headers. - */ - public Enumeration getNonMatchingHeaders(String[] names) - throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getNonMatchingHeaders(names); - } - - public void addHeaderLine(String line) throws MessagingException { - throw new IllegalWriteException("IMAPMessage is read-only"); - } - - /** - * Get all header-lines. - */ - public Enumeration getAllHeaderLines() throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getAllHeaderLines(); - } - - /** - * Get all matching header-lines. - */ - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getMatchingHeaderLines(names); - } - - /** - * Get all non-matching headerlines. - */ - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException { - checkExpunged(); - loadHeaders(); - return super.getNonMatchingHeaderLines(names); - } - - /** - * Get the Flags for this message. - */ - public synchronized Flags getFlags() throws MessagingException { - checkExpunged(); - loadFlags(); - return super.getFlags(); - } - - /** - * Test if the given Flags are set in this message. - */ - public synchronized boolean isSet(Flags.Flag flag) - throws MessagingException { - checkExpunged(); - loadFlags(); - return super.isSet(flag); - } - - /** - * Set/Unset the given flags in this message. - */ - public synchronized void setFlags(Flags flag, boolean set) - throws MessagingException { - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - checkExpunged(); // Insure that this message is not expunged - p.storeFlags(getSequenceNumber(), flag, set); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } - } - } - - /** - * Set whether or not to use the PEEK variant of FETCH when - * fetching message content. - * - * @since JavaMail 1.3.3 - */ - public synchronized void setPeek(boolean peek) { - this.peek = peek; - } - - /** - * Get whether or not to use the PEEK variant of FETCH when - * fetching message content. - * - * @since JavaMail 1.3.3 - */ - public synchronized boolean getPeek() { - return peek; - } - - /** - * Invalidate cached header and envelope information for this - * message. Subsequent accesses of this information will - * cause it to be fetched from the server. - * - * @since JavaMail 1.3.3 - */ - public synchronized void invalidateHeaders() { - headersLoaded = false; - loadedHeaders.clear(); - headers = null; - envelope = null; - bs = null; - receivedDate = null; - size = -1; - type = null; - subject = null; - description = null; - flags = null; - } - - /** - * This class implements the test to be done on each - * message in the folder. The test is to check whether the - * message has already cached all the items requested in the - * FetchProfile. If any item is missing, the test succeeds and - * breaks out. - */ - public static class FetchProfileCondition implements Utility.Condition { - private boolean needEnvelope = false; - private boolean needFlags = false; - private boolean needBodyStructure = false; - private boolean needUID = false; - private boolean needHeaders = false; - private boolean needSize = false; - private String[] hdrs = null; - private Set need = new HashSet(); // Set - - /** - * Create a FetchProfileCondition to determine if we need to fetch - * any of the information specified in the FetchProfile. - */ - public FetchProfileCondition(FetchProfile fp, FetchItem[] fitems) { - if (fp.contains(FetchProfile.Item.ENVELOPE)) - needEnvelope = true; - if (fp.contains(FetchProfile.Item.FLAGS)) - needFlags = true; - if (fp.contains(FetchProfile.Item.CONTENT_INFO)) - needBodyStructure = true; - if (fp.contains(FetchProfile.Item.SIZE)) - needSize = true; - if (fp.contains(UIDFolder.FetchProfileItem.UID)) - needUID = true; - if (fp.contains(IMAPFolder.FetchProfileItem.HEADERS)) - needHeaders = true; - if (fp.contains(IMAPFolder.FetchProfileItem.SIZE)) - needSize = true; - hdrs = fp.getHeaderNames(); - for (int i = 0; i < fitems.length; i++) { - if (fp.contains(fitems[i].getFetchProfileItem())) - need.add(fitems[i]); - } - } - - /** - * Return true if we NEED to fetch the requested information - * for the specified message. - */ - public boolean test(IMAPMessage m) { - if (needEnvelope && m._getEnvelope() == null) - return true; // no envelope - if (needFlags && m._getFlags() == null) - return true; // no flags - if (needBodyStructure && m._getBodyStructure() == null) - return true; // no BODYSTRUCTURE - if (needUID && m.getUID() == -1) // no UID - return true; - if (needHeaders && !m.areHeadersLoaded()) // no headers - return true; - if (needSize && m.size == -1) // no size - return true; - - // Is the desired header present ? - for (int i = 0; i < hdrs.length; i++) { - if (!m.isHeaderLoaded(hdrs[i])) - return true; // Nope, return - } - Iterator it = need.iterator(); - while (it.hasNext()) { - FetchItem fitem = (FetchItem)it.next(); - if (m.items == null || m.items.get(fitem.getName()) == null) - return true; - } - - return false; - } - } - - /** - * Apply the data in the FETCH item to this message. - * - * ASSERT: Must hold the messageCacheLock. - * - * @since JavaMail 1.4.6 - */ - protected boolean handleFetchItem(Item item, - String[] hdrs, boolean allHeaders) - throws MessagingException { - // Check for the FLAGS item - if (item instanceof Flags) - flags = (Flags)item; - // Check for ENVELOPE items - else if (item instanceof ENVELOPE) - envelope = (ENVELOPE)item; - else if (item instanceof INTERNALDATE) - receivedDate = ((INTERNALDATE)item).getDate(); - else if (item instanceof RFC822SIZE) - size = ((RFC822SIZE)item).size; - - // Check for the BODYSTRUCTURE item - else if (item instanceof BODYSTRUCTURE) - bs = (BODYSTRUCTURE)item; - // Check for the UID item - else if (item instanceof UID) { - UID u = (UID)item; - uid = u.uid; // set uid - // add entry into uid table - if (((IMAPFolder)folder).uidTable == null) - ((IMAPFolder)folder).uidTable = new Hashtable(); - ((IMAPFolder)folder).uidTable.put(new Long(u.uid), this); - } - - // Check for header items - else if (item instanceof RFC822DATA || - item instanceof BODY) { - InputStream headerStream; - if (item instanceof RFC822DATA) // IMAP4 - headerStream = - ((RFC822DATA)item).getByteArrayInputStream(); - else // IMAP4rev1 - headerStream = - ((BODY)item).getByteArrayInputStream(); - - // Load the obtained headers. - InternetHeaders h = new InternetHeaders(); - // Some IMAP servers (e.g., gmx.net) return NIL - // instead of a string just containing a CR/LF - // when the header list is empty. - if (headerStream != null) - h.load(headerStream); - if (headers == null || allHeaders) - headers = h; - else { - /* - * This is really painful. A second fetch - * of the same headers (which might occur because - * a new header was added to the set requested) - * will return headers we already know about. - * In this case, only load the headers we haven't - * seen before to avoid adding duplicates of - * headers we already have. - * - * XXX - There's a race condition here if another - * thread is reading headers in the same message - * object, because InternetHeaders is not thread - * safe. - */ - Enumeration e = h.getAllHeaders(); - while (e.hasMoreElements()) { - Header he = (Header)e.nextElement(); - if (!isHeaderLoaded(he.getName())) - headers.addHeader( - he.getName(), he.getValue()); - } - } - - // if we asked for all headers, assume we got them - if (allHeaders) - setHeadersLoaded(true); - else { - // Mark all headers we asked for as 'loaded' - for (int k = 0; k < hdrs.length; k++) - setHeaderLoaded(hdrs[k]); - } - } else - return false; // not handled - return true; // something above handled it - } - - /** - * Apply the data in the extension FETCH items to this message. - * This method adds all the items to the items map. - * Subclasses may override this method to call super and then - * also copy the data to a more convenient form. - * - * ASSERT: Must hold the messageCacheLock. - * - * @since JavaMail 1.4.6 - */ - protected void handleExtensionFetchItems(Map extensionItems) - throws MessagingException { - if (items == null) - items = extensionItems; - else - items.putAll(extensionItems); - } - - /** - * Fetch an individual item for the current message. - * Note that handleExtensionFetchItems will have been called - * to store this item in the message before this method - * returns. - * - * @since JavaMail 1.4.6 - */ - protected Object fetchItem(FetchItem fitem) - throws MessagingException { - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - Object robj = null; - - try { - IMAPProtocol p = getProtocol(); - - checkExpunged(); // Insure that this message is not expunged - - int seqnum = getSequenceNumber(); - Response[] r = p.fetch(seqnum, fitem.getName()); - - for (int i = 0; i < r.length; i++) { - // If this response is NOT a FetchResponse or if it does - // not match our seqnum, skip. - if (r[i] == null || - !(r[i] instanceof FetchResponse) || - ((FetchResponse)r[i]).getNumber() != seqnum) - continue; - - FetchResponse f = (FetchResponse)r[i]; - Object o = f.getExtensionItems().get(fitem.getName()); - if (o != null) - robj = o; - } - - // ((IMAPFolder)folder).handleResponses(r); - p.notifyResponseHandlers(r); - p.handleResult(r[r.length - 1]); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - return robj; - - } // Release MessageCacheLock - } - - /** - * Return the data associated with the FetchItem. - * If the data hasn't been fetched, call the fetchItem - * method to fetch it. Returns null if there is no - * data for the FetchItem. - * - * @since JavaMail 1.4.6 - */ - public synchronized Object getItem(FetchItem fitem) - throws MessagingException { - Object item = items == null ? null : items.get(fitem.getName()); - if (item == null) - item = fetchItem(fitem); - return item; - } - - /* - * Load the Envelope for this message. - */ - private synchronized void loadEnvelope() throws MessagingException { - if (envelope != null) // already loaded - return; - - Response[] r = null; - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - checkExpunged(); // Insure that this message is not expunged - - int seqnum = getSequenceNumber(); - r = p.fetch(seqnum, EnvelopeCmd); - - for (int i = 0; i < r.length; i++) { - // If this response is NOT a FetchResponse or if it does - // not match our seqnum, skip. - if (r[i] == null || - !(r[i] instanceof FetchResponse) || - ((FetchResponse)r[i]).getNumber() != seqnum) - continue; - - FetchResponse f = (FetchResponse)r[i]; - - // Look for the Envelope items. - int count = f.getItemCount(); - for (int j = 0; j < count; j++) { - Item item = f.getItem(j); - - if (item instanceof ENVELOPE) - envelope = (ENVELOPE)item; - else if (item instanceof INTERNALDATE) - receivedDate = ((INTERNALDATE)item).getDate(); - else if (item instanceof RFC822SIZE) - size = ((RFC822SIZE)item).size; - } - } - - // ((IMAPFolder)folder).handleResponses(r); - p.notifyResponseHandlers(r); - p.handleResult(r[r.length - 1]); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - - } // Release MessageCacheLock - - if (envelope == null) - throw new MessagingException("Failed to load IMAP envelope"); - } - - /* - * Load the BODYSTRUCTURE - */ - private synchronized void loadBODYSTRUCTURE() - throws MessagingException { - if (bs != null) // already loaded - return; - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - // This message could be expunged when we were waiting - // to acquire the lock ... - checkExpunged(); - - bs = p.fetchBodyStructure(getSequenceNumber()); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - if (bs == null) { - // if the FETCH is successful, we should always get a - // BODYSTRUCTURE, but some servers fail to return it - // if the message has been expunged - forceCheckExpunged(); - throw new MessagingException("Unable to load BODYSTRUCTURE"); - } - } - } - - /* - * Load all headers. - */ - private synchronized void loadHeaders() throws MessagingException { - if (headersLoaded) - return; - - InputStream is = null; - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized (getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - // This message could be expunged when we were waiting - // to acquire the lock ... - checkExpunged(); - - if (p.isREV1()) { - BODY b = p.peekBody(getSequenceNumber(), - toSection("HEADER")); - if (b != null) - is = b.getByteArrayInputStream(); - } else { - RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), - "HEADER"); - if (rd != null) - is = rd.getByteArrayInputStream(); - } - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - } // Release MessageCacheLock - - if (is == null) - throw new MessagingException("Cannot load header"); - headers = new InternetHeaders(is); - headersLoaded = true; - } - - /* - * Load this message's Flags - */ - private synchronized void loadFlags() throws MessagingException { - if (flags != null) - return; - - // Acquire MessageCacheLock, to freeze seqnum. - synchronized(getMessageCacheLock()) { - try { - IMAPProtocol p = getProtocol(); - - // This message could be expunged when we were waiting - // to acquire the lock ... - checkExpunged(); - - flags = p.fetchFlags(getSequenceNumber()); - // make sure flags is always set, even if server is broken - if (flags == null) - flags = new Flags(); - } catch (ConnectionException cex) { - throw new FolderClosedException(folder, cex.getMessage()); - } catch (ProtocolException pex) { - forceCheckExpunged(); - throw new MessagingException(pex.getMessage(), pex); - } - } // Release MessageCacheLock - } - - /* - * Are all headers loaded? - */ - private boolean areHeadersLoaded() { - return headersLoaded; - } - - /* - * Set whether all headers are loaded. - */ - private void setHeadersLoaded(boolean loaded) { - headersLoaded = loaded; - } - - /* - * Check if the given header was ever loaded from the server - */ - private boolean isHeaderLoaded(String name) { - if (headersLoaded) // All headers for this message have been loaded - return true; - - return loadedHeaders.containsKey(name.toUpperCase(Locale.ENGLISH)); - } - - /* - * Mark that the given headers have been loaded from the server. - */ - private void setHeaderLoaded(String name) { - loadedHeaders.put(name.toUpperCase(Locale.ENGLISH), name); - } - - /* - * Convert the given FETCH item identifier to the approriate - * section-string for this message. - */ - private String toSection(String what) { - if (sectionId == null) - return what; - else - return sectionId + "." + what; - } - - /* - * Clone an array of InternetAddresses. - */ - private InternetAddress[] aaclone(InternetAddress[] aa) { - if (aa == null) - return null; - else - return (InternetAddress[])aa.clone(); - } - - private Flags _getFlags() { - return flags; - } - - private ENVELOPE _getEnvelope() { - return envelope; - } - - private BODYSTRUCTURE _getBodyStructure() { - return bs; - } - - /*********************************************************** - * accessor routines to make available certain private/protected - * fields to other classes in this package. - ***********************************************************/ - - /* - * Called by IMAPFolder. - * Must not be synchronized. - */ - void _setFlags(Flags flags) { - this.flags = flags; - } - - /* - * Called by IMAPNestedMessage. - */ - Session _getSession() { - return session; - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java b/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java deleted file mode 100644 index fdd80687..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.io.InputStream; -import java.io.IOException; -import java.util.Vector; - -import javax.mail.*; -import javax.mail.internet.*; - -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; - -/** - * This class - * - * @author John Mani - */ - -public class IMAPMultipartDataSource extends MimePartDataSource - implements MultipartDataSource { - private Vector parts; - - protected IMAPMultipartDataSource(MimePart part, BODYSTRUCTURE[] bs, - String sectionId, IMAPMessage msg) { - super(part); - - parts = new Vector(bs.length); - for (int i = 0; i < bs.length; i++) - parts.addElement( - new IMAPBodyPart(bs[i], - sectionId == null ? - Integer.toString(i+1) : - sectionId + "." + Integer.toString(i+1), - msg) - ); - } - - public int getCount() { - return parts.size(); - } - - public BodyPart getBodyPart(int index) throws MessagingException { - return (BodyPart)parts.elementAt(index); - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java b/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java deleted file mode 100644 index 5d058a21..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.io.*; -import javax.mail.*; -import com.sun.mail.imap.protocol.*; -import com.sun.mail.iap.ProtocolException; - -/** - * This class implements a nested IMAP message - * - * @author John Mani - */ - -public class IMAPNestedMessage extends IMAPMessage { - private IMAPMessage msg; // the enclosure of this nested message - - /** - * Package private constructor.

    - * - * Note that nested messages have no containing folder, nor - * a message number. - */ - IMAPNestedMessage(IMAPMessage m, BODYSTRUCTURE b, ENVELOPE e, String sid) { - super(m._getSession()); - msg = m; - bs = b; - envelope = e; - sectionId = sid; - setPeek(m.getPeek()); - } - - /* - * Get the enclosing message's Protocol object. Overrides - * IMAPMessage.getProtocol(). - */ - protected IMAPProtocol getProtocol() - throws ProtocolException, FolderClosedException { - return msg.getProtocol(); - } - - /* - * Is this an IMAP4 REV1 server? - */ - protected boolean isREV1() throws FolderClosedException { - return msg.isREV1(); - } - - /* - * Get the enclosing message's messageCacheLock. Overrides - * IMAPMessage.getMessageCacheLock(). - */ - protected Object getMessageCacheLock() { - return msg.getMessageCacheLock(); - } - - /* - * Get the enclosing message's sequence number. Overrides - * IMAPMessage.getSequenceNumber(). - */ - protected int getSequenceNumber() { - return msg.getSequenceNumber(); - } - - /* - * Check whether the enclosing message is expunged. Overrides - * IMAPMessage.checkExpunged(). - */ - protected void checkExpunged() throws MessageRemovedException { - msg.checkExpunged(); - } - - /* - * Check whether the enclosing message is expunged. Overrides - * Message.isExpunged(). - */ - public boolean isExpunged() { - return msg.isExpunged(); - } - - /* - * Get the enclosing message's fetchBlockSize. - */ - protected int getFetchBlockSize() { - return msg.getFetchBlockSize(); - } - - /* - * Get the enclosing message's ignoreBodyStructureSize. - */ - protected boolean ignoreBodyStructureSize() { - return msg.ignoreBodyStructureSize(); - } - - /* - * IMAPMessage uses RFC822.SIZE. We use the "size" field from - * our BODYSTRUCTURE. - */ - public int getSize() throws MessagingException { - return bs.size; - } - - /* - * Disallow setting flags on nested messages - */ - public synchronized void setFlags(Flags flag, boolean set) - throws MessagingException { - // Cannot set FLAGS on a nested IMAP message - throw new MethodNotSupportedException( - "Cannot set flags on this nested message"); - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPSSLStore.java b/src/main/java/com/sun/mail/imap/IMAPSSLStore.java deleted file mode 100644 index fdb2c25f..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPSSLStore.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import javax.mail.*; - -/** - * This class provides access to an IMAP message store over SSL.

    - */ - -public class IMAPSSLStore extends IMAPStore { - - /** - * Constructor that takes a Session object and a URLName that - * represents a specific IMAP server. - */ - public IMAPSSLStore(Session session, URLName url) { - super(session, url, "imaps", true); // call super constructor - } -} diff --git a/src/main/java/com/sun/mail/imap/IMAPStore.java b/src/main/java/com/sun/mail/imap/IMAPStore.java deleted file mode 100644 index 77c2edd3..00000000 --- a/src/main/java/com/sun/mail/imap/IMAPStore.java +++ /dev/null @@ -1,1934 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.lang.reflect.*; -import java.util.Vector; -import java.util.StringTokenizer; -import java.io.PrintStream; -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.logging.Level; - -import javax.mail.*; -import javax.mail.event.*; - -import com.sun.mail.iap.*; -import com.sun.mail.imap.protocol.*; -import com.sun.mail.util.PropUtil; -import com.sun.mail.util.MailLogger; -import com.sun.mail.util.SocketConnectException; -import com.sun.mail.util.MailConnectException; - -/** - * This class provides access to an IMAP message store.

    - * - * Applications that need to make use of IMAP-specific features may cast - * a Store object to an IMAPStore object and - * use the methods on this class. The {@link #getQuota getQuota} and - * {@link #setQuota setQuota} methods support the IMAP QUOTA extension. - * Refer to RFC 2087 - * for more information.

    - * - * See the com.sun.mail.imap package - * documentation for further information on the IMAP protocol provider.

    - * - * WARNING: The APIs unique to this class should be - * considered EXPERIMENTAL. They may be changed in the - * future in ways that are incompatible with applications using the - * current APIs. - * - * @author John Mani - * @author Bill Shannon - * @author Jim Glennon - */ -/* - * This package is implemented over the "imap.protocol" package, which - * implements the protocol-level commands.

    - * - * A connected IMAPStore maintains a pool of IMAP protocol objects for - * use in communicating with the IMAP server. The IMAPStore will create - * the initial AUTHENTICATED connection and seed the pool with this - * connection. As folders are opened and new IMAP protocol objects are - * needed, the IMAPStore will provide them from the connection pool, - * or create them if none are available. When a folder is closed, - * its IMAP protocol object is returned to the connection pool if the - * pool is not over capacity. The pool size can be configured by setting - * the mail.imap.connectionpoolsize property.

    - * - * Note that all connections in the connection pool have their response - * handler set to be the Store. When the connection is removed from the - * pool for use by a folder, the response handler is removed and then set - * to either the Folder or to the special nonStoreResponseHandler, depending - * on how the connection is being used. This is probably excessive. - * Better would be for the Protocol object to support only a single - * response handler, which would be set before the connection is used - * and cleared when the connection is in the pool and can't be used.

    - * - * A mechanism is provided for timing out idle connection pool IMAP - * protocol objects. Timed out connections are closed and removed (pruned) - * from the connection pool. The time out interval can be configured via - * the mail.imap.connectionpooltimeout property.

    - * - * The connected IMAPStore object may or may not maintain a separate IMAP - * protocol object that provides the store a dedicated connection to the - * IMAP server. This is provided mainly for compatibility with previous - * implementations of JavaMail and is determined by the value of the - * mail.imap.separatestoreconnection property.

    - * - * An IMAPStore object provides closed IMAPFolder objects thru its list() - * and listSubscribed() methods. A closed IMAPFolder object acquires an - * IMAP protocol object from the store to communicate with the server. When - * the folder is opened, it gets its own protocol object and thus its own, - * separate connection to the server. The store maintains references to - * all 'open' folders. When a folder is/gets closed, the store removes - * it from its list. When the store is/gets closed, it closes all open - * folders in its list, thus cleaning up all open connections to the - * server.

    - * - * A mutex is used to control access to the connection pool resources. - * Any time any of these resources need to be accessed, the following - * convention should be followed: - * - * synchronized (pool) { // ACQUIRE LOCK - * // access connection pool resources - * } // RELEASE LOCK

    - * - * The locking relationship between the store and folders is that the - * store lock must be acquired before a folder lock. This is currently only - * applicable in the store's cleanup method. It's important that the - * connection pool lock is not held when calling into folder objects. - * The locking hierarchy is that a folder lock must be acquired before - * any connection pool operations are performed. You never need to hold - * all three locks, but if you hold more than one this is the order you - * have to acquire them in.

    - * - * That is: Store > Folder, Folder > pool, Store > pool

    - * - * The IMAPStore implements the ResponseHandler interface and listens to - * BYE or untagged OK-notification events from the server as a result of - * Store operations. IMAPFolder forwards notifications that result from - * Folder operations using the store connection; the IMAPStore ResponseHandler - * is not used directly in this case.

    - */ - -public class IMAPStore extends Store - implements QuotaAwareStore, ResponseHandler { - - /** - * A special event type for a StoreEvent to indicate an IMAP - * response, if the mail.imap.enableimapevents property is set. - */ - public static final int RESPONSE = 1000; - - protected final String name; // name of this protocol - protected final int defaultPort; // default IMAP port - protected final boolean isSSL; // use SSL? - - private final int blksize; // Block size for data requested - // in FETCH requests. Defaults to - // 16K - - private boolean ignoreSize; // ignore the size in BODYSTRUCTURE? - - private final int statusCacheTimeout; // cache Status for 1 second - - private final int appendBufferSize; // max size of msg buffered for append - - private final int minIdleTime; // minimum idle time - - private volatile int port = -1; // port to use - - // Auth info - protected String host; - protected String user; - protected String password; - protected String proxyAuthUser; - protected String authorizationID; - protected String saslRealm; - - private Namespaces namespaces; - - private boolean disableAuthLogin = false; // disable AUTH=LOGIN - private boolean disableAuthPlain = false; // disable AUTH=PLAIN - private boolean disableAuthNtlm = false; // disable AUTH=NTLM - private boolean enableStartTLS = false; // enable STARTTLS - private boolean requireStartTLS = false; // require STARTTLS - private boolean usingSSL = false; // using SSL? - private boolean enableSASL = false; // enable SASL authentication - private String[] saslMechanisms; - private boolean forcePasswordRefresh = false; - // enable notification of IMAP responses - private boolean enableImapEvents = false; - private String guid; // for Yahoo! Mail IMAP - - /* - * This field is set in the Store's response handler if we see - * a BYE response. The releaseStore method checks this field - * and if set it cleans up the Store. Field is volatile because - * there's no lock we consistently hold while manipulating it. - * - * Because volatile doesn't really work before JDK 1.5, - * use a lock to protect these two fields. - */ - private volatile boolean connectionFailed = false; - private volatile boolean forceClose = false; - private final Object connectionFailedLock = new Object(); - - private boolean debugusername; // include username in debug output? - private boolean debugpassword; // include password in debug output? - protected MailLogger logger; // for debug output - - private boolean messageCacheDebug; - - // constructors for IMAPFolder class provided by user - private volatile Constructor folderConstructor = null; - private volatile Constructor folderConstructorLI = null; - - // Connection pool info - - static class ConnectionPool { - - // container for the pool's IMAP protocol objects - private Vector authenticatedConnections = new Vector(); - - // vectore of open folders - private Vector folders; - - // is the store connection being used? - private boolean storeConnectionInUse = false; - - // the last time (in millis) the pool was checked for timed out - // connections - private long lastTimePruned; - - // flag to indicate whether there is a dedicated connection for - // store commands - private final boolean separateStoreConnection; - - // client timeout interval - private final long clientTimeoutInterval; - - // server timeout interval - private final long serverTimeoutInterval; - - // size of the connection pool - private final int poolSize; - - // interval for checking for timed out connections - private final long pruningInterval; - - // connection pool logger - private final MailLogger logger; - - /* - * The idleState field supports the IDLE command. - * Normally when executing an IMAP command we hold the - * store's lock. - * While executing the IDLE command we can't hold the - * lock or it would prevent other threads from - * entering Store methods even far enough to check whether - * an IDLE command is in progress. We need to check before - * issuing another command so that we can abort the IDLE - * command. - * - * The idleState field is protected by the store's lock. - * The RUNNING state is the normal state and means no IDLE - * command is in progress. The IDLE state means we've issued - * an IDLE command and are reading responses. The ABORTING - * state means we've sent the DONE continuation command and - * are waiting for the thread running the IDLE command to - * break out of its read loop. - * - * When an IDLE command is in progress, the thread calling - * the idle method will be reading from the IMAP connection - * while not holding the store's lock. - * It's obviously critical that no other thread try to send a - * command or read from the connection while in this state. - * However, other threads can send the DONE continuation - * command that will cause the server to break out of the IDLE - * loop and send the ending tag response to the IDLE command. - * The thread in the idle method that's reading the responses - * from the IDLE command will see this ending response and - * complete the idle method, setting the idleState field back - * to RUNNING, and notifying any threads waiting to use the - * connection. - * - * All uses of the IMAP connection (IMAPProtocol object) must - * be preceeded by a check to make sure an IDLE command is not - * running, and abort the IDLE command if necessary. This check - * is made while holding the connection pool lock. While - * waiting for the IDLE command to complete, these other threads - * will give up the connection pool lock. This check is done by - * the getStoreProtocol() method. - */ - private static final int RUNNING = 0; // not doing IDLE command - private static final int IDLE = 1; // IDLE command in effect - private static final int ABORTING = 2; // IDLE command aborting - private int idleState = RUNNING; - private IMAPProtocol idleProtocol; // protocol object when IDLE - - ConnectionPool(String name, MailLogger plogger, Session session) { - lastTimePruned = System.currentTimeMillis(); - - boolean debug = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".connectionpool.debug", false); - logger = plogger.getSubLogger("connectionpool", - "DEBUG IMAP CP", debug); - - // check if the default connection pool size is overridden - int size = PropUtil.getIntSessionProperty(session, - "mail." + name + ".connectionpoolsize", -1); - if (size > 0) { - poolSize = size; - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.connectionpoolsize: " + poolSize); - } else - poolSize = 1; - - // check if the default client-side timeout value is overridden - int connectionPoolTimeout = PropUtil.getIntSessionProperty(session, - "mail." + name + ".connectionpooltimeout", -1); - if (connectionPoolTimeout > 0) { - clientTimeoutInterval = connectionPoolTimeout; - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.connectionpooltimeout: " + - clientTimeoutInterval); - } else - clientTimeoutInterval = 45 * 1000; // 45 seconds - - // check if the default server-side timeout value is overridden - int serverTimeout = PropUtil.getIntSessionProperty(session, - "mail." + name + ".servertimeout", -1); - if (serverTimeout > 0) { - serverTimeoutInterval = serverTimeout; - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.servertimeout: " + - serverTimeoutInterval); - } else - serverTimeoutInterval = 30 * 60 * 1000; // 30 minutes - - // check if the default server-side timeout value is overridden - int pruning = PropUtil.getIntSessionProperty(session, - "mail." + name + ".pruninginterval", -1); - if (pruning > 0) { - pruningInterval = pruning; - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.pruninginterval: " + - pruningInterval); - } else - pruningInterval = 60 * 1000; // 1 minute - - // check to see if we should use a separate (i.e. dedicated) - // store connection - separateStoreConnection = - PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".separatestoreconnection", false); - if (separateStoreConnection) - logger.config("dedicate a store connection"); - - } - } - - private final ConnectionPool pool; - - /** - * A special response handler for connections that are being used - * to perform operations on behalf of an object other than the Store. - * It DOESN'T cause the Store to be cleaned up if a BYE is seen. - * The BYE may be real or synthetic and in either case just indicates - * that the connection is dead. - */ - private ResponseHandler nonStoreResponseHandler = new ResponseHandler() { - public void handleResponse(Response r) { - // Any of these responses may have a response code. - if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) - handleResponseCode(r); - if (r.isBYE()) - logger.fine("IMAPStore non-store connection dead"); - } - }; - - /** - * Constructor that takes a Session object and a URLName that - * represents a specific IMAP server. - */ - public IMAPStore(Session session, URLName url) { - this(session, url, "imap", false); - } - - /** - * Constructor used by this class and by IMAPSSLStore subclass. - */ - protected IMAPStore(Session session, URLName url, - String name, boolean isSSL) { - super(session, url); // call super constructor - if (url != null) - name = url.getProtocol(); - this.name = name; - if (!isSSL) - isSSL = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".ssl.enable", false); - if (isSSL) - this.defaultPort = 993; - else - this.defaultPort = 143; - this.isSSL = isSSL; - - debug = session.getDebug(); - debugusername = PropUtil.getBooleanSessionProperty(session, - "mail.debug.auth.username", true); - debugpassword = PropUtil.getBooleanSessionProperty(session, - "mail.debug.auth.password", false); - logger = new MailLogger(this.getClass(), - "DEBUG " + name.toUpperCase(), session); - - boolean partialFetch = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".partialfetch", true); - if (!partialFetch) { - blksize = -1; - logger.config("mail.imap.partialfetch: false"); - } else { - blksize = PropUtil.getIntSessionProperty(session, - "mail." + name +".fetchsize", 1024 * 16); - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.fetchsize: " + blksize); - } - - ignoreSize = PropUtil.getBooleanSessionProperty(session, - "mail." + name +".ignorebodystructuresize", false); - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.ignorebodystructuresize: " + ignoreSize); - - statusCacheTimeout = PropUtil.getIntSessionProperty(session, - "mail." + name + ".statuscachetimeout", 1000); - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.statuscachetimeout: " + - statusCacheTimeout); - - appendBufferSize = PropUtil.getIntSessionProperty(session, - "mail." + name + ".appendbuffersize", -1); - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.appendbuffersize: " + appendBufferSize); - - minIdleTime = PropUtil.getIntSessionProperty(session, - "mail." + name + ".minidletime", 10); - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.minidletime: " + minIdleTime); - - // check if we should do a PROXYAUTH login - String s = session.getProperty("mail." + name + ".proxyauth.user"); - if (s != null) { - proxyAuthUser = s; - if (logger.isLoggable(Level.CONFIG)) - logger.config("mail.imap.proxyauth.user: " + proxyAuthUser); - } - - // check if AUTH=LOGIN is disabled - disableAuthLogin = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".auth.login.disable", false); - if (disableAuthLogin) - logger.config("disable AUTH=LOGIN"); - - // check if AUTH=PLAIN is disabled - disableAuthPlain = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".auth.plain.disable", false); - if (disableAuthPlain) - logger.config("disable AUTH=PLAIN"); - - // check if AUTH=NTLM is disabled - disableAuthNtlm = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".auth.ntlm.disable", false); - if (disableAuthNtlm) - logger.config("disable AUTH=NTLM"); - - // check if STARTTLS is enabled - enableStartTLS = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".starttls.enable", false); - if (enableStartTLS) - logger.config("enable STARTTLS"); - - // check if STARTTLS is required - requireStartTLS = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".starttls.required", false); - if (requireStartTLS) - logger.config("require STARTTLS"); - - // check if SASL is enabled - enableSASL = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".sasl.enable", false); - if (enableSASL) - logger.config("enable SASL"); - - // check if SASL mechanisms are specified - if (enableSASL) { - s = session.getProperty("mail." + name + ".sasl.mechanisms"); - if (s != null && s.length() > 0) { - if (logger.isLoggable(Level.CONFIG)) - logger.config("SASL mechanisms allowed: " + s); - Vector v = new Vector(5); - StringTokenizer st = new StringTokenizer(s, " ,"); - while (st.hasMoreTokens()) { - String m = st.nextToken(); - if (m.length() > 0) - v.addElement(m); - } - saslMechanisms = new String[v.size()]; - v.copyInto(saslMechanisms); - } - } - - // check if an authorization ID has been specified - s = session.getProperty("mail." + name + ".sasl.authorizationid"); - if (s != null) { - authorizationID = s; - logger.log(Level.CONFIG, "mail.imap.sasl.authorizationid: {0}", - authorizationID); - } - - // check if a SASL realm has been specified - s = session.getProperty("mail." + name + ".sasl.realm"); - if (s != null) { - saslRealm = s; - logger.log(Level.CONFIG, "mail.imap.sasl.realm: {0}", saslRealm); - } - - // check if forcePasswordRefresh is enabled - forcePasswordRefresh = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".forcepasswordrefresh", false); - if (forcePasswordRefresh) - logger.config("enable forcePasswordRefresh"); - - // check if enableimapevents is enabled - enableImapEvents = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".enableimapevents", false); - if (enableImapEvents) - logger.config("enable IMAP events"); - - // check if message cache debugging set - messageCacheDebug = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".messagecache.debug", false); - - guid = session.getProperty("mail." + name + ".yahoo.guid"); - if (guid != null) - logger.log(Level.CONFIG, "mail.imap.yahoo.guid: {0}", guid); - - s = session.getProperty("mail." + name + ".folder.class"); - if (s != null) { - logger.log(Level.CONFIG, "IMAP: folder class: {0}", s); - try { - ClassLoader cl = this.getClass().getClassLoader(); - - // now load the class - Class folderClass = null; - try { - // First try the "application's" class loader. - // This should eventually be replaced by - // Thread.currentThread().getContextClassLoader(). - folderClass = Class.forName(s, false, cl); - } catch (ClassNotFoundException ex1) { - // That didn't work, now try the "system" class loader. - // (Need both of these because JDK 1.1 class loaders - // may not delegate to their parent class loader.) - folderClass = Class.forName(s); - } - - Class[] c = { String.class, char.class, IMAPStore.class, - Boolean.class }; - folderConstructor = folderClass.getConstructor(c); - Class[] c2 = { ListInfo.class, IMAPStore.class }; - folderConstructorLI = folderClass.getConstructor(c2); - } catch (Exception ex) { - logger.log(Level.CONFIG, - "IMAP: failed to load folder class", ex); - } - } - - pool = new ConnectionPool(name, logger, session); - } - - /** - * Implementation of protocolConnect(). Will create a connection - * to the server and authenticate the user using the mechanisms - * specified by various properties.

    - * - * The host, user, and password - * parameters must all be non-null. If the authentication mechanism - * being used does not require a password, an empty string or other - * suitable dummy password should be used. - */ - protected synchronized boolean - protocolConnect(String host, int pport, String user, String password) - throws MessagingException { - - IMAPProtocol protocol = null; - - // check for non-null values of host, password, user - if (host == null || password == null || user == null) { - if (logger.isLoggable(Level.FINE)) - logger.fine("protocolConnect returning false" + - ", host=" + host + - ", user=" + traceUser(user) + - ", password=" + tracePassword(password)); - return false; - } - - // set the port correctly - if (pport != -1) { - port = pport; - } else { - port = PropUtil.getIntSessionProperty(session, - "mail." + name + ".port", port); - } - - // use the default if needed - if (port == -1) { - port = defaultPort; - } - - try { - boolean poolEmpty; - synchronized (pool) { - poolEmpty = pool.authenticatedConnections.isEmpty(); - } - - if (poolEmpty) { - if (logger.isLoggable(Level.FINE)) - logger.fine("trying to connect to host \"" + host + - "\", port " + port + ", isSSL " + isSSL); - protocol = newIMAPProtocol(host, port); - if (logger.isLoggable(Level.FINE)) - logger.fine("protocolConnect login" + - ", host=" + host + - ", user=" + traceUser(user) + - ", password=" + tracePassword(password)); - login(protocol, user, password); - - protocol.addResponseHandler(this); - - usingSSL = protocol.isSSL(); // in case anyone asks - - this.host = host; - this.user = user; - this.password = password; - - synchronized (pool) { - pool.authenticatedConnections.addElement(protocol); - } - } - } catch (CommandFailedException cex) { - // login failure, close connection to server - if (protocol != null) - protocol.disconnect(); - protocol = null; - throw new AuthenticationFailedException( - cex.getResponse().getRest()); - } catch (ProtocolException pex) { // any other exception - // failure in login command, close connection to server - if (protocol != null) - protocol.disconnect(); - protocol = null; - throw new MessagingException(pex.getMessage(), pex); - } catch (SocketConnectException scex) { - throw new MailConnectException(scex); - } catch (IOException ioex) { - throw new MessagingException(ioex.getMessage(), ioex); - } - - return true; - } - - /** - * Create an IMAPProtocol object connected to the host and port. - * Subclasses of IMAPStore may override this method to return a - * subclass of IMAPProtocol that supports product-specific extensions. - * - * @since JavaMail 1.4.6 - */ - protected IMAPProtocol newIMAPProtocol(String host, int port) - throws IOException, ProtocolException { - return new IMAPProtocol(name, host, port, - session.getProperties(), - isSSL, - logger - ); - } - - private void login(IMAPProtocol p, String u, String pw) - throws ProtocolException { - // turn on TLS if it's been enabled or required and is supported - if (enableStartTLS || requireStartTLS) { - if (p.hasCapability("STARTTLS")) { - p.startTLS(); - // if startTLS succeeds, refresh capabilities - p.capability(); - } else if (requireStartTLS) { - logger.fine("STARTTLS required but not supported by server"); - throw new ProtocolException( - "STARTTLS required but not supported by server"); - } - } - if (p.isAuthenticated()) - return; // no need to login - - // allow subclasses to issue commands before login - preLogin(p); - - // issue special ID command to Yahoo! Mail IMAP server - if (guid != null) - p.id(guid); - - /* - * Put a special "marker" in the capabilities list so we can - * detect if the server refreshed the capabilities in the OK - * response. - */ - p.getCapabilities().put("__PRELOGIN__", ""); - String authzid; - if (authorizationID != null) - authzid = authorizationID; - else if (proxyAuthUser != null) - authzid = proxyAuthUser; - else - authzid = null; - - if (enableSASL) - p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw); - - if (p.isAuthenticated()) - ; // SASL login succeeded, go to bottom - else if (p.hasCapability("AUTH=PLAIN") && !disableAuthPlain) - p.authplain(authzid, u, pw); - else if ((p.hasCapability("AUTH-LOGIN") || - p.hasCapability("AUTH=LOGIN")) && !disableAuthLogin) - p.authlogin(u, pw); - else if (p.hasCapability("AUTH=NTLM") && !disableAuthNtlm) - p.authntlm(authzid, u, pw); - else if (!p.hasCapability("LOGINDISABLED")) - p.login(u, pw); - else - throw new ProtocolException("No login methods supported!"); - - if (proxyAuthUser != null) - p.proxyauth(proxyAuthUser); - - /* - * If marker is still there, capabilities haven't been refreshed, - * refresh them now. - */ - if (p.hasCapability("__PRELOGIN__")) { - try { - p.capability(); - } catch (ConnectionException cex) { - throw cex; // rethrow connection failures - // XXX - assume connection has been closed - } catch (ProtocolException pex) { - // ignore other exceptions that "should never happen" - } - } - } - - /** - * This method is called after the connection is made and - * TLS is started (if needed), but before any authentication - * is attempted. Subclasses can override this method to - * issue commands that are needed in the "not authenticated" - * state. Note that if the connection is pre-authenticated, - * this method won't be called.

    - * - * The implementation of this method in this class does nothing. - * - * @since JavaMail 1.4.4 - */ - protected void preLogin(IMAPProtocol p) throws ProtocolException { - } - - /** - * Does this IMAPStore use SSL when connecting to the server? - * - * @return true if using SSL - * @since JavaMail 1.4.6 - */ - public boolean isSSL() { - return usingSSL; - } - - /** - * Set the user name that will be used for subsequent connections - * after this Store is first connected (for example, when creating - * a connection to open a Folder). This value is overridden - * by any call to the Store's connect method.

    - * - * Some IMAP servers may provide an authentication ID that can - * be used for more efficient authentication for future connections. - * This authentication ID is provided in a server-specific manner - * not described here.

    - * - * Most applications will never need to use this method. - * - * @since JavaMail 1.3.3 - */ - public synchronized void setUsername(String user) { - this.user = user; - } - - /** - * Set the password that will be used for subsequent connections - * after this Store is first connected (for example, when creating - * a connection to open a Folder). This value is overridden - * by any call to the Store's connect method.

    - * - * Most applications will never need to use this method. - * - * @since JavaMail 1.3.3 - */ - public synchronized void setPassword(String password) { - this.password = password; - } - - /* - * Get a new authenticated protocol object for this Folder. - * Also store a reference to this folder in our list of - * open folders. - */ - IMAPProtocol getProtocol(IMAPFolder folder) - throws MessagingException { - IMAPProtocol p = null; - - // keep looking for a connection until we get a good one - while (p == null) { - - // New authenticated protocol objects are either acquired - // from the connection pool, or created when the pool is - // empty or no connections are available. None are available - // if the current pool size is one and the separate store - // property is set or the connection is in use. - - synchronized (pool) { - - // If there's none available in the pool, - // create a new one. - if (pool.authenticatedConnections.isEmpty() || - (pool.authenticatedConnections.size() == 1 && - (pool.separateStoreConnection || pool.storeConnectionInUse))) { - - logger.fine("no connections in the pool, creating a new one"); - try { - if (forcePasswordRefresh) - refreshPassword(); - // Use cached host, port and timeout values. - p = newIMAPProtocol(host, port); - // Use cached auth info - login(p, user, password); - } catch(Exception ex1) { - if (p != null) - try { - p.disconnect(); - } catch (Exception ex2) { } - p = null; - } - - if (p == null) - throw new MessagingException("connection failure"); - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("connection available -- size: " + - pool.authenticatedConnections.size()); - - // remove the available connection from the Authenticated queue - p = (IMAPProtocol)pool.authenticatedConnections.lastElement(); - pool.authenticatedConnections.removeElement(p); - - // check if the connection is still live - long lastUsed = System.currentTimeMillis() - p.getTimestamp(); - if (lastUsed > pool.serverTimeoutInterval) { - try { - /* - * Swap in a special response handler that will handle - * alerts, but won't cause the store to be closed and - * cleaned up if the connection is dead. - */ - p.removeResponseHandler(this); - p.addResponseHandler(nonStoreResponseHandler); - p.noop(); - p.removeResponseHandler(nonStoreResponseHandler); - p.addResponseHandler(this); - } catch (ProtocolException pex) { - try { - p.removeResponseHandler(nonStoreResponseHandler); - p.disconnect(); - } finally { - // don't let any exception stop us - p = null; - continue; // try again, from the top - } - } - } - - // remove the store as a response handler. - p.removeResponseHandler(this); - } - - // check if we need to look for client-side timeouts - timeoutConnections(); - - // Add folder to folder-list - if (folder != null) { - if (pool.folders == null) - pool.folders = new Vector(); - pool.folders.addElement(folder); - } - } - - } - - return p; - } - - /** - * Get this Store's protocol connection. - * - * When acquiring a store protocol object, it is important to - * use the following steps: - * - * IMAPProtocol p = null; - * try { - * p = getStoreProtocol(); - * // perform the command - * } catch (ConnectionException cex) { - * throw new StoreClosedException(this, cex.getMessage()); - * } catch (WhateverException ex) { - * // handle it - * } finally { - * releaseStoreProtocol(p); - * } - */ - private IMAPProtocol getStoreProtocol() throws ProtocolException { - IMAPProtocol p = null; - - while (p == null) { - synchronized (pool) { - waitIfIdle(); - - // If there's no authenticated connections available create a - // new one and place it in the authenticated queue. - if (pool.authenticatedConnections.isEmpty()) { - pool.logger.fine("getStoreProtocol() - no connections " + - "in the pool, creating a new one"); - try { - if (forcePasswordRefresh) - refreshPassword(); - // Use cached host, port and timeout values. - p = newIMAPProtocol(host, port); - // Use cached auth info - login(p, user, password); - } catch(Exception ex1) { - if (p != null) - try { - p.logout(); - } catch (Exception ex2) { } - p = null; - } - - if (p == null) - throw new ConnectionException( - "failed to create new store connection"); - - p.addResponseHandler(this); - pool.authenticatedConnections.addElement(p); - - } else { - // Always use the first element in the Authenticated queue. - if (pool.logger.isLoggable(Level.FINE)) - pool.logger.fine("getStoreProtocol() - " + - "connection available -- size: " + - pool.authenticatedConnections.size()); - p = (IMAPProtocol)pool.authenticatedConnections.firstElement(); - } - - if (pool.storeConnectionInUse) { - try { - // someone else is using the connection, give up - // and wait until they're done - p = null; - pool.wait(); - } catch (InterruptedException ex) { } - } else { - pool.storeConnectionInUse = true; - - pool.logger.fine("getStoreProtocol() -- storeConnectionInUse"); - } - - timeoutConnections(); - } - } - return p; - } - - /** - * Get a store protocol object for use by a folder. - */ - IMAPProtocol getFolderStoreProtocol() throws ProtocolException { - IMAPProtocol p = getStoreProtocol(); - p.removeResponseHandler(this); - p.addResponseHandler(nonStoreResponseHandler); - return p; - } - - /* - * Some authentication systems use one time passwords - * or tokens, so each authentication request requires - * a new password. This "kludge" allows a callback - * to application code to get a new password. - * - * XXX - remove this when SASL support is added - */ - private void refreshPassword() { - if (logger.isLoggable(Level.FINE)) - logger.fine("refresh password, user: " + traceUser(user)); - InetAddress addr; - try { - addr = InetAddress.getByName(host); - } catch (UnknownHostException e) { - addr = null; - } - PasswordAuthentication pa = - session.requestPasswordAuthentication(addr, port, - name, null, user); - if (pa != null) { - user = pa.getUserName(); - password = pa.getPassword(); - } - } - - /** - * If a SELECT succeeds, but indicates that the folder is - * READ-ONLY, and the user asked to open the folder READ_WRITE, - * do we allow the open to succeed? - */ - boolean allowReadOnlySelect() { - return PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".allowreadonlyselect", false); - } - - /** - * Report whether the separateStoreConnection is set. - */ - boolean hasSeparateStoreConnection() { - return pool.separateStoreConnection; - } - - /** - * Return the connection pool logger. - */ - MailLogger getConnectionPoolLogger() { - return pool.logger; - } - - /** - * Report whether message cache debugging is enabled. - */ - boolean getMessageCacheDebug() { - return messageCacheDebug; - } - - /** - * Report whether the connection pool is full. - */ - boolean isConnectionPoolFull() { - - synchronized (pool) { - if (pool.logger.isLoggable(Level.FINE)) - pool.logger.fine("connection pool current size: " + - pool.authenticatedConnections.size() + - " pool size: " + pool.poolSize); - - return (pool.authenticatedConnections.size() >= pool.poolSize); - - } - } - - /** - * Release the protocol object back to the connection pool. - */ - void releaseProtocol(IMAPFolder folder, IMAPProtocol protocol) { - - synchronized (pool) { - if (protocol != null) { - // If the pool is not full, add the store as a response handler - // and return the protocol object to the connection pool. - if (!isConnectionPoolFull()) { - protocol.addResponseHandler(this); - pool.authenticatedConnections.addElement(protocol); - - if (logger.isLoggable(Level.FINE)) - logger.fine( - "added an Authenticated connection -- size: " + - pool.authenticatedConnections.size()); - } else { - logger.fine( - "pool is full, not adding an Authenticated connection"); - try { - protocol.logout(); - } catch (ProtocolException pex) {}; - } - } - - if (pool.folders != null) - pool.folders.removeElement(folder); - - timeoutConnections(); - } - } - - /** - * Release the store connection. - */ - private void releaseStoreProtocol(IMAPProtocol protocol) { - - // will be called from idle() without the Store lock held, - // but cleanup is synchronized and will acquire the Store lock - - if (protocol == null) { - cleanup(); // failed to ever get the connection - return; // nothing to release - } - - /* - * Read out the flag that says whether this connection failed - * before releasing the protocol object for others to use. - */ - boolean failed; - synchronized (connectionFailedLock) { - failed = connectionFailed; - connectionFailed = false; // reset for next use - } - - // now free the store connection - synchronized (pool) { - pool.storeConnectionInUse = false; - pool.notifyAll(); // in case anyone waiting - - pool.logger.fine("releaseStoreProtocol()"); - - timeoutConnections(); - } - - /* - * If the connection died while we were using it, clean up. - * It's critical that the store connection be freed and the - * connection pool not be locked while we do this. - */ - assert !Thread.holdsLock(pool); - if (failed) - cleanup(); - } - - /** - * Release a store protocol object that was being used by a folder. - */ - void releaseFolderStoreProtocol(IMAPProtocol protocol) { - if (protocol == null) - return; // should never happen - protocol.removeResponseHandler(nonStoreResponseHandler); - protocol.addResponseHandler(this); - synchronized (pool) { - pool.storeConnectionInUse = false; - pool.notifyAll(); // in case anyone waiting - - pool.logger.fine("releaseFolderStoreProtocol()"); - - timeoutConnections(); - } - } - - /** - * Empty the connection pool. - */ - private void emptyConnectionPool(boolean force) { - - synchronized (pool) { - for (int index = pool.authenticatedConnections.size() - 1; - index >= 0; --index) { - try { - IMAPProtocol p = (IMAPProtocol) - pool.authenticatedConnections.elementAt(index); - p.removeResponseHandler(this); - if (force) - p.disconnect(); - else - p.logout(); - } catch (ProtocolException pex) {}; - } - - pool.authenticatedConnections.removeAllElements(); - } - - pool.logger.fine("removed all authenticated connections from pool"); - } - - /** - * Check to see if it's time to shrink the connection pool. - */ - private void timeoutConnections() { - - synchronized (pool) { - - // If we've exceeded the pruning interval, look for stale - // connections to logout. - if (System.currentTimeMillis() - pool.lastTimePruned > - pool.pruningInterval && - pool.authenticatedConnections.size() > 1) { - - if (pool.logger.isLoggable(Level.FINE)) { - pool.logger.fine("checking for connections to prune: " + - (System.currentTimeMillis() - pool.lastTimePruned)); - pool.logger.fine("clientTimeoutInterval: " + - pool.clientTimeoutInterval); - } - - IMAPProtocol p; - - // Check the timestamp of the protocol objects in the pool and - // logout if the interval exceeds the client timeout value - // (leave the first connection). - for (int index = pool.authenticatedConnections.size() - 1; - index > 0; index--) { - p = (IMAPProtocol)pool.authenticatedConnections. - elementAt(index); - if (pool.logger.isLoggable(Level.FINE)) - pool.logger.fine("protocol last used: " + - (System.currentTimeMillis() - p.getTimestamp())); - if (System.currentTimeMillis() - p.getTimestamp() > - pool.clientTimeoutInterval) { - - pool.logger.fine( - "authenticated connection timed out, " + - "logging out the connection"); - - p.removeResponseHandler(this); - pool.authenticatedConnections.removeElementAt(index); - - try { - p.logout(); - } catch (ProtocolException pex) {} - } - } - pool.lastTimePruned = System.currentTimeMillis(); - } - } - } - - /** - * Get the block size to use for fetch requests on this Store. - */ - int getFetchBlockSize() { - return blksize; - } - - /** - * Ignore the size reported in the BODYSTRUCTURE when fetching data? - */ - boolean ignoreBodyStructureSize() { - return ignoreSize; - } - - /** - * Get a reference to the session. - */ - Session getSession() { - return session; - } - - /** - * Get the number of milliseconds to cache STATUS response. - */ - int getStatusCacheTimeout() { - return statusCacheTimeout; - } - - /** - * Get the maximum size of a message to buffer for append. - */ - int getAppendBufferSize() { - return appendBufferSize; - } - - /** - * Get the minimum amount of time to delay when returning from idle. - */ - int getMinIdleTime() { - return minIdleTime; - } - - /** - * Return true if the specified capability string is in the list - * of capabilities the server announced. - * - * @since JavaMail 1.3.3 - */ - public synchronized boolean hasCapability(String capability) - throws MessagingException { - IMAPProtocol p = null; - try { - p = getStoreProtocol(); - return p.hasCapability(capability); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - } - - /** - * Check whether this store is connected. Override superclass - * method, to actually ping our server connection. - */ - public synchronized boolean isConnected() { - if (!super.isConnected()) { - // if we haven't been connected at all, don't bother with - // the NOOP. - return false; - } - - /* - * The below noop() request can: - * (1) succeed - in which case all is fine. - * - * (2) fail because the server returns NO or BAD, in which - * case we ignore it since we can't really do anything. - * (2) fail because a BYE response is obtained from the - * server - * (3) fail because the socket.write() to the server fails, - * in which case the iap.protocol() code converts the - * IOException into a BYE response. - * - * Thus, our BYE handler will take care of closing the Store - * in case our connection is really gone. - */ - - IMAPProtocol p = null; - try { - p = getStoreProtocol(); - p.noop(); - } catch (ProtocolException pex) { - // will return false below - } finally { - releaseStoreProtocol(p); - } - - - return super.isConnected(); - } - - /** - * Close this Store. - */ - public synchronized void close() throws MessagingException { - if (!super.isConnected()) // Already closed. - return; - - IMAPProtocol protocol = null; - try { - boolean isEmpty; - synchronized (pool) { - // If there's no authenticated connections available - // don't create a new one - isEmpty = pool.authenticatedConnections.isEmpty(); - } - /* - * Have to drop the lock before calling cleanup. - * Yes, there's a potential race here. The pool could - * become empty after we check, in which case we'll just - * waste time getting a new connection and closing it. - * Or, the pool could be empty now and not empty by the - * time we get into cleanup, but that's ok because cleanup - * will just close the connection. - */ - if (isEmpty) { - pool.logger.fine("close() - no connections "); - cleanup(); - return; - } - - protocol = getStoreProtocol(); - /* - * We have to remove the protocol from the pool so that, - * when our response handler processes the BYE response - * and calls cleanup, which calls emptyConnection, that - * we don't try to log out this connection twice. - */ - synchronized (pool) { - pool.authenticatedConnections.removeElement(protocol); - } - - /* - * LOGOUT. - * - * Note that protocol.logout() closes the server socket - * connection, regardless of what happens .. - * - * Also note that protocol.logout() results in a BYE - * response (As per rfc 2060, BYE is a *required* response - * to LOGOUT). In fact, even if protocol.logout() fails - * with an IOException (if the server connection is dead), - * iap.Protocol.command() converts that exception into a - * BYE response. So, I depend on my BYE handler to set the - * flag that causes releaseStoreProtocol to do the - * Store cleanup. - */ - protocol.logout(); - } catch (ProtocolException pex) { - // Hmm .. will this ever happen ? - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(protocol); - } - } - - protected void finalize() throws Throwable { - super.finalize(); - close(); - } - - /** - * Cleanup before dying. - */ - private synchronized void cleanup() { - // if we're not connected, someone beat us to it - if (!super.isConnected()) { - logger.fine("IMAPStore cleanup, not connected"); - return; - } - - /* - * If forceClose is true, some thread ran into an error that suggests - * the server might be dead, so we force the folders to close - * abruptly without waiting for the server. Used when - * the store connection times out, for example. - */ - boolean force; - synchronized (connectionFailedLock) { - force = forceClose; - forceClose = false; - connectionFailed = false; - } - if (logger.isLoggable(Level.FINE)) - logger.fine("IMAPStore cleanup, force " + force); - - Vector foldersCopy = null; - boolean done = true; - - // To avoid violating the locking hierarchy, there's no lock we - // can hold that prevents another thread from trying to open a - // folder at the same time we're trying to close all the folders. - // Thus, there's an inherent race condition here. We close all - // the folders we know about and then check whether any new folders - // have been opened in the mean time. We keep trying until we're - // successful in closing all the folders. - for (;;) { - // Make a copy of the folders list so we do not violate the - // folder-connection pool locking hierarchy. - synchronized (pool) { - if (pool.folders != null) { - done = false; - foldersCopy = pool.folders; - pool.folders = null; - } else { - done = true; - } - } - if (done) - break; - - // Close and remove any open folders under this Store. - for (int i = 0, fsize = foldersCopy.size(); i < fsize; i++) { - IMAPFolder f = (IMAPFolder)foldersCopy.elementAt(i); - - try { - if (force) { - logger.fine("force folder to close"); - // Don't want to wait for folder connection to timeout - // (if, for example, the server is down) so we close - // folders abruptly. - f.forceClose(); - } else { - logger.fine("close folder"); - f.close(false); - } - } catch (MessagingException mex) { - // Who cares ?! Ignore 'em. - } catch (IllegalStateException ex) { - // Ditto - } - } - - } - - synchronized (pool) { - emptyConnectionPool(force); - } - - // to set the state and send the closed connection event - try { - super.close(); - } catch (MessagingException mex) { } - logger.fine("IMAPStore cleanup done"); - } - - /** - * Get the default folder, representing the root of this user's - * namespace. Returns a closed DefaultFolder object. - */ - public synchronized Folder getDefaultFolder() throws MessagingException { - checkConnected(); - return new DefaultFolder(this); - } - - /** - * Get named folder. Returns a new, closed IMAPFolder. - */ - public synchronized Folder getFolder(String name) - throws MessagingException { - checkConnected(); - return newIMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR); - } - - /** - * Get named folder. Returns a new, closed IMAPFolder. - */ - public synchronized Folder getFolder(URLName url) - throws MessagingException { - checkConnected(); - return newIMAPFolder(url.getFile(), IMAPFolder.UNKNOWN_SEPARATOR); - } - - /** - * Create an IMAPFolder object. If user supplied their own class, - * use it. Otherwise, call the constructor. - */ - protected IMAPFolder newIMAPFolder(String fullName, char separator, - Boolean isNamespace) { - IMAPFolder f = null; - if (folderConstructor != null) { - try { - Object[] o = - { fullName, new Character(separator), this, isNamespace }; - f = (IMAPFolder)folderConstructor.newInstance(o); - } catch (Exception ex) { - logger.log(Level.FINE, - "exception creating IMAPFolder class", ex); - } - } - if (f == null) - f = new IMAPFolder(fullName, separator, this, isNamespace); - return f; - } - - /** - * Create an IMAPFolder object. Call the newIMAPFolder method - * above with a null isNamespace. - */ - protected IMAPFolder newIMAPFolder(String fullName, char separator) { - return newIMAPFolder(fullName, separator, null); - } - - /** - * Create an IMAPFolder object. If user supplied their own class, - * use it. Otherwise, call the constructor. - */ - protected IMAPFolder newIMAPFolder(ListInfo li) { - IMAPFolder f = null; - if (folderConstructorLI != null) { - try { - Object[] o = { li, this }; - f = (IMAPFolder)folderConstructorLI.newInstance(o); - } catch (Exception ex) { - logger.log(Level.FINE, - "exception creating IMAPFolder class LI", ex); - } - } - if (f == null) - f = new IMAPFolder(li, this); - return f; - } - - /** - * Using the IMAP NAMESPACE command (RFC 2342), return a set - * of folders representing the Personal namespaces. - */ - public Folder[] getPersonalNamespaces() throws MessagingException { - Namespaces ns = getNamespaces(); - if (ns == null || ns.personal == null) - return super.getPersonalNamespaces(); - return namespaceToFolders(ns.personal, null); - } - - /** - * Using the IMAP NAMESPACE command (RFC 2342), return a set - * of folders representing the User's namespaces. - */ - public Folder[] getUserNamespaces(String user) - throws MessagingException { - Namespaces ns = getNamespaces(); - if (ns == null || ns.otherUsers == null) - return super.getUserNamespaces(user); - return namespaceToFolders(ns.otherUsers, user); - } - - /** - * Using the IMAP NAMESPACE command (RFC 2342), return a set - * of folders representing the Shared namespaces. - */ - public Folder[] getSharedNamespaces() throws MessagingException { - Namespaces ns = getNamespaces(); - if (ns == null || ns.shared == null) - return super.getSharedNamespaces(); - return namespaceToFolders(ns.shared, null); - } - - private synchronized Namespaces getNamespaces() throws MessagingException { - checkConnected(); - - IMAPProtocol p = null; - - if (namespaces == null) { - try { - p = getStoreProtocol(); - namespaces = p.namespace(); - } catch (BadCommandException bex) { - // NAMESPACE not supported, ignore it - } catch (ConnectionException cex) { - throw new StoreClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - } - return namespaces; - } - - private Folder[] namespaceToFolders(Namespaces.Namespace[] ns, - String user) { - Folder[] fa = new Folder[ns.length]; - for (int i = 0; i < fa.length; i++) { - String name = ns[i].prefix; - if (user == null) { - // strip trailing delimiter - int len = name.length(); - if ( len > 0 && name.charAt(len - 1) == ns[i].delimiter) - name = name.substring(0, len - 1); - } else { - // add user - name += user; - } - fa[i] = newIMAPFolder(name, ns[i].delimiter, - Boolean.valueOf(user == null)); - } - return fa; - } - - /** - * Get the quotas for the named quota root. - * Quotas are controlled on the basis of a quota root, not - * (necessarily) a folder. The relationship between folders - * and quota roots depends on the IMAP server. Some servers - * might implement a single quota root for all folders owned by - * a user. Other servers might implement a separate quota root - * for each folder. A single folder can even have multiple - * quota roots, perhaps controlling quotas for different - * resources. - * - * @param root the name of the quota root - * @return array of Quota objects - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - public synchronized Quota[] getQuota(String root) - throws MessagingException { - checkConnected(); - Quota[] qa = null; - - IMAPProtocol p = null; - try { - p = getStoreProtocol(); - qa = p.getQuotaRoot(root); - } catch (BadCommandException bex) { - throw new MessagingException("QUOTA not supported", bex); - } catch (ConnectionException cex) { - throw new StoreClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - return qa; - } - - /** - * Set the quotas for the quota root specified in the quota argument. - * Typically this will be one of the quota roots obtained from the - * getQuota method, but it need not be. - * - * @param quota the quota to set - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - public synchronized void setQuota(Quota quota) throws MessagingException { - checkConnected(); - IMAPProtocol p = null; - try { - p = getStoreProtocol(); - p.setQuota(quota); - } catch (BadCommandException bex) { - throw new MessagingException("QUOTA not supported", bex); - } catch (ConnectionException cex) { - throw new StoreClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - releaseStoreProtocol(p); - } - } - - private void checkConnected() { - assert Thread.holdsLock(this); - if (!super.isConnected()) - throw new IllegalStateException("Not connected"); - } - - /** - * Response handler method. - */ - public void handleResponse(Response r) { - // Any of these responses may have a response code. - if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) - handleResponseCode(r); - if (r.isBYE()) { - logger.fine("IMAPStore connection dead"); - // Store's IMAP connection is dead, save the response so that - // releaseStoreProtocol will cleanup later. - synchronized (connectionFailedLock) { - connectionFailed = true; - if (r.isSynthetic()) - forceClose = true; - } - return; - } - } - - /** - * Use the IMAP IDLE command (see - * RFC 2177), - * if supported by the server, to enter idle mode so that the server - * can send unsolicited notifications - * without the need for the client to constantly poll the server. - * Use a ConnectionListener to be notified of - * events. When another thread (e.g., the listener thread) - * needs to issue an IMAP comand for this Store, the idle mode will - * be terminated and this method will return. Typically the caller - * will invoke this method in a loop.

    - * - * If the mail.imap.enableimapevents property is set, notifications - * received while the IDLE command is active will be delivered to - * ConnectionListeners as events with a type of - * IMAPStore.RESPONSE. The event's message will be - * the raw IMAP response string. - * Note that most IMAP servers will not deliver any events when - * using the IDLE command on a connection with no mailbox selected - * (i.e., this method). In most cases you'll want to use the - * idle method on IMAPFolder.

    - * - * NOTE: This capability is highly experimental and likely will change - * in future releases.

    - * - * The mail.imap.minidletime property enforces a minimum delay - * before returning from this method, to ensure that other threads - * have a chance to issue commands before the caller invokes this - * method again. The default delay is 10 milliseconds. - * - * @exception MessagingException if the server doesn't support the - * IDLE extension - * @exception IllegalStateException if the store isn't connected - * - * @since JavaMail 1.4.1 - */ - public void idle() throws MessagingException { - IMAPProtocol p = null; - // ASSERT: Must NOT be called with the connection pool - // synchronization lock held. - assert !Thread.holdsLock(pool); - synchronized (this) { - checkConnected(); - } - boolean needNotification = false; - try { - synchronized (pool) { - p = getStoreProtocol(); - if (pool.idleState != ConnectionPool.RUNNING) { - // some other thread must be running the IDLE - // command, we'll just wait for it to finish - // without aborting it ourselves - try { - // give up lock and wait to be not idle - pool.wait(); - } catch (InterruptedException ex) { } - return; - } - p.idleStart(); - needNotification = true; - pool.idleState = ConnectionPool.IDLE; - pool.idleProtocol = p; - } - - /* - * We gave up the pool lock so that other threads - * can get into the pool far enough to see that we're - * in IDLE and abort the IDLE. - * - * Now we read responses from the IDLE command, especially - * including unsolicited notifications from the server. - * We don't hold the pool lock while reading because - * it protects the idleState and other threads need to be - * able to examine the state. - * - * We hold the pool lock while processing the responses. - */ - for (;;) { - Response r = p.readIdleResponse(); - synchronized (pool) { - if (r == null || !p.processIdleResponse(r)) { - pool.idleState = ConnectionPool.RUNNING; - pool.idleProtocol = null; - pool.notifyAll(); - needNotification = false; - break; - } - } - if (enableImapEvents && r.isUnTagged()) { - notifyStoreListeners(IMAPStore.RESPONSE, r.toString()); - } - } - - /* - * Enforce a minimum delay to give time to threads - * processing the responses that came in while we - * were idle. - */ - int minidle = getMinIdleTime(); - if (minidle > 0) { - try { - Thread.sleep(minidle); - } catch (InterruptedException ex) { } - } - - } catch (BadCommandException bex) { - throw new MessagingException("IDLE not supported", bex); - } catch (ConnectionException cex) { - throw new StoreClosedException(this, cex.getMessage()); - } catch (ProtocolException pex) { - throw new MessagingException(pex.getMessage(), pex); - } finally { - if (needNotification) { - synchronized (pool) { - pool.idleState = ConnectionPool.RUNNING; - pool.idleProtocol = null; - pool.notifyAll(); - } - } - releaseStoreProtocol(p); - } - } - - /* - * If an IDLE command is in progress, abort it if necessary, - * and wait until it completes. - * ASSERT: Must be called with the pool's lock held. - */ - private void waitIfIdle() throws ProtocolException { - assert Thread.holdsLock(pool); - while (pool.idleState != ConnectionPool.RUNNING) { - if (pool.idleState == ConnectionPool.IDLE) { - pool.idleProtocol.idleAbort(); - pool.idleState = ConnectionPool.ABORTING; - } - try { - // give up lock and wait to be not idle - pool.wait(); - } catch (InterruptedException ex) { } - } - } - - /** - * Handle notifications and alerts. - * Response must be an OK, NO, BAD, or BYE response. - */ - void handleResponseCode(Response r) { - String s = r.getRest(); // get the text after the response - boolean isAlert = false; - if (s.startsWith("[")) { // a response code - int i = s.indexOf(']'); - // remember if it's an alert - if (i > 0 && s.substring(0, i + 1).equalsIgnoreCase("[ALERT]")) - isAlert = true; - // strip off the response code in any event - s = s.substring(i + 1).trim(); - } - if (isAlert) - notifyStoreListeners(StoreEvent.ALERT, s); - else if (r.isUnTagged() && s.length() > 0) - // Only send notifications that come with untagged - // responses, and only if there is actually some - // text there. - notifyStoreListeners(StoreEvent.NOTICE, s); - } - - private String traceUser(String user) { - return debugusername ? user : ""; - } - - private String tracePassword(String password) { - return debugpassword ? password : - (password == null ? "" : ""); - } -} diff --git a/src/main/java/com/sun/mail/imap/MessageCache.java b/src/main/java/com/sun/mail/imap/MessageCache.java deleted file mode 100644 index b4088921..00000000 --- a/src/main/java/com/sun/mail/imap/MessageCache.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.io.PrintStream; -import java.util.*; -import java.util.logging.Level; - -import javax.mail.*; -import com.sun.mail.util.PropUtil; -import com.sun.mail.util.MailLogger; - -/** - * A cache of IMAPMessage objects along with the - * mapping from message number to IMAP sequence number. - * - * All operations on this object are protected by the messageCacheLock - * in IMAPFolder. - */ -public class MessageCache { - /* - * The array of IMAPMessage objects. Elements of the array might - * be null if no one has asked for the message. The array expands - * as needed and might be larger than the number of messages in the - * folder. The "size" field indicates the number of entries that - * are valid. - */ - private IMAPMessage[] messages; - - /* - * A parallel array of sequence numbers for each message. If the - * array pointer is null, the sequence number of a message is just - * its message number. This is the common case, until a message is - * expunged. - */ - private int[] seqnums; - - /* - * The amount of the messages (and seqnum) array that is valid. - * Might be less than the actual size of the array. - */ - private int size; - - /** - * The folder these messages belong to. - */ - private IMAPFolder folder; - - // debugging logger - private MailLogger logger; - - /** - * Grow the array by at least this much, to avoid constantly - * reallocating the array. - */ - private static final int SLOP = 64; - - /** - * Construct a new message cache of the indicated size. - */ - MessageCache(IMAPFolder folder, IMAPStore store, int size) { - this.folder = folder; - logger = folder.logger.getSubLogger("messagecache", "DEBUG IMAP MC", - store.getMessageCacheDebug()); - if (logger.isLoggable(Level.CONFIG)) - logger.config("create cache of size " + size); - ensureCapacity(size, 1); - } - - /** - * Constructor for debugging and testing. - */ - MessageCache(int size, boolean debug) { - this.folder = null; - logger = new MailLogger( - this.getClass(), "messagecache", - "DEBUG IMAP MC", debug, System.out); - if (logger.isLoggable(Level.CONFIG)) - logger.config("create DEBUG cache of size " + size); - ensureCapacity(size, 1); - } - - /** - * Size of cache. - */ - public int size() { - return size; - } - - /** - * Get the message object for the indicated message number. - * If the message object hasn't been created, create it. - */ - public IMAPMessage getMessage(int msgnum) { - // check range - if (msgnum < 1 || msgnum > size) - throw new ArrayIndexOutOfBoundsException( - "message number (" + msgnum + ") out of bounds (" + size + ")"); - IMAPMessage msg = messages[msgnum-1]; - if (msg == null) { - if (logger.isLoggable(Level.FINE)) - logger.fine("create message number " + msgnum); - msg = folder.newIMAPMessage(msgnum); - messages[msgnum-1] = msg; - // mark message expunged if no seqnum - if (seqnumOf(msgnum) <= 0) { - logger.fine("it's expunged!"); - msg.setExpunged(true); - } - } - return msg; - } - - /** - * Get the message object for the indicated sequence number. - * If the message object hasn't been created, create it. - * Return null if there's no message with that sequence number. - */ - public IMAPMessage getMessageBySeqnum(int seqnum) { - int msgnum = msgnumOf(seqnum); - if (msgnum < 0) { // XXX - < 1 ? - if (logger.isLoggable(Level.FINE)) - logger.fine("no message seqnum " + seqnum); - return null; - } else - return getMessage(msgnum); - } - - /** - * Expunge the message with the given sequence number. - */ - public void expungeMessage(int seqnum) { - int msgnum = msgnumOf(seqnum); - if (msgnum < 0) { - if (logger.isLoggable(Level.FINE)) - logger.fine("expunge no seqnum " + seqnum); - return; // XXX - should never happen - } - IMAPMessage msg = messages[msgnum-1]; - if (msg != null) { - if (logger.isLoggable(Level.FINE)) - logger.fine("expunge existing " + msgnum); - msg.setExpunged(true); - } - if (seqnums == null) { // time to fill it in - logger.fine("create seqnums array"); - seqnums = new int[messages.length]; - for (int i = 1; i < msgnum; i++) - seqnums[i-1] = i; - seqnums[msgnum - 1] = 0; - for (int i = msgnum + 1; i <= seqnums.length; i++) - seqnums[i-1] = i - 1; - } else { - seqnums[msgnum - 1] = 0; - for (int i = msgnum + 1; i <= seqnums.length; i++) { - assert seqnums[i-1] != 1; - if (seqnums[i-1] > 0) - seqnums[i-1]--; - } - } - } - - /** - * Remove all the expunged messages from the array, - * returning a list of removed message objects. - */ - public IMAPMessage[] removeExpungedMessages() { - logger.fine("remove expunged messages"); - List mlist = new ArrayList(); // list of expunged messages - - /* - * Walk through the array compressing it by copying - * higher numbered messages further down in the array, - * effectively removing expunged messages from the array. - * oldnum is the index we use to walk through the array. - * newnum is the index where we copy the next valid message. - * oldnum == newnum until we encounter an expunged message. - */ - int oldnum = 1; - int newnum = 1; - while (oldnum <= size) { - // is message expunged? - if (seqnumOf(oldnum) <= 0) { - IMAPMessage m = getMessage(oldnum); - mlist.add(m); - } else { - // keep this message - if (newnum != oldnum) { - // move message down in the array (compact array) - messages[newnum-1] = messages[oldnum-1]; - if (messages[newnum-1] != null) - messages[newnum-1].setMessageNumber(newnum); - } - newnum++; - } - oldnum++; - } - seqnums = null; - shrink(newnum, oldnum); - - IMAPMessage[] rmsgs = new IMAPMessage[mlist.size()]; - if (logger.isLoggable(Level.FINE)) - logger.fine("return " + rmsgs.length); - mlist.toArray(rmsgs); - return rmsgs; - } - - /** - * Remove expunged messages in msgs from the array, - * returning a list of removed message objects. - * All messages in msgs must be IMAPMessage objects - * from this folder. - */ - public IMAPMessage[] removeExpungedMessages(Message[] msgs) { - logger.fine("remove expunged messages"); - List mlist = new ArrayList(); // list of expunged messages - - /* - * Copy the message numbers of the expunged messages into - * a separate array and sort the array to make it easier to - * process later. - */ - int[] mnum = new int[msgs.length]; - for (int i = 0; i < msgs.length; i++) - mnum[i] = msgs[i].getMessageNumber(); - Arrays.sort(mnum); - - /* - * Walk through the array compressing it by copying - * higher numbered messages further down in the array, - * effectively removing expunged messages from the array. - * oldnum is the index we use to walk through the array. - * newnum is the index where we copy the next valid message. - * oldnum == newnum until we encounter an expunged message. - * - * Even though we know the message number of the first possibly - * expunged message, we still start scanning at message number 1 - * so that we can check whether there's any message whose - * sequence number is different than its message number. If there - * is, we can't throw away the seqnums array when we're done. - */ - int oldnum = 1; - int newnum = 1; - int mnumi = 0; // index into mnum - boolean keepSeqnums = false; - while (oldnum <= size) { - /* - * Are there still expunged messsages in msgs to consider, - * and is the message we're considering the next one in the - * list, and is it expunged? - */ - if (mnumi < mnum.length && - oldnum == mnum[mnumi] && - seqnumOf(oldnum) <= 0) { - IMAPMessage m = getMessage(oldnum); - mlist.add(m); - /* - * Just in case there are duplicate entries in the msgs array, - * we keep advancing mnumi past any duplicates, but of course - * stop when we get to the end of the array. - */ - while (mnumi < mnum.length && mnum[mnumi] <= oldnum) - mnumi++; // consider next message in array - } else { - // keep this message - if (newnum != oldnum) { - // move message down in the array (compact array) - messages[newnum-1] = messages[oldnum-1]; - if (messages[newnum-1] != null) - messages[newnum-1].setMessageNumber(newnum); - if (seqnums != null) - seqnums[newnum-1] = seqnums[oldnum-1]; - } - if (seqnums != null && seqnums[newnum-1] != newnum) - keepSeqnums = true; - newnum++; - } - oldnum++; - } - - if (!keepSeqnums) - seqnums = null; - shrink(newnum, oldnum); - - IMAPMessage[] rmsgs = new IMAPMessage[mlist.size()]; - if (logger.isLoggable(Level.FINE)) - logger.fine("return " + rmsgs.length); - mlist.toArray(rmsgs); - return rmsgs; - } - - /** - * Shrink the messages and seqnums arrays. newend is one past last - * valid element. oldend is one past the previous last valid element. - */ - private void shrink(int newend, int oldend) { - size = newend - 1; - if (logger.isLoggable(Level.FINE)) - logger.fine("size now " + size); - if (size == 0) { // no messages left - messages = null; - seqnums = null; - } else if (size > SLOP && size < messages.length / 2) { - // if array shrinks by too much, reallocate it - logger.fine("reallocate array"); - IMAPMessage[] newm = new IMAPMessage[size + SLOP]; - System.arraycopy(messages, 0, newm, 0, size); - messages = newm; - if (seqnums != null) { - int[] news = new int[size + SLOP]; - System.arraycopy(seqnums, 0, news, 0, size); - seqnums = news; - } - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("clean " + newend + " to " + oldend); - // clear out unused entries in array - for (int msgnum = newend; msgnum < oldend; msgnum++) { - messages[msgnum-1] = null; - if (seqnums != null) - seqnums[msgnum-1] = 0; - } - } - } - - /** - * Add count messages to the cache. - * newSeqNum is the sequence number of the first message added. - */ - public void addMessages(int count, int newSeqNum) { - if (logger.isLoggable(Level.FINE)) - logger.fine("add " + count + " messages"); - // don't have to do anything other than making sure there's space - ensureCapacity(size + count, newSeqNum); - } - - /* - * Make sure the arrays are at least big enough to hold - * "newsize" messages. - */ - private void ensureCapacity(int newsize, int newSeqNum) { - if (messages == null) - messages = new IMAPMessage[newsize + SLOP]; - else if (messages.length < newsize) { - if (logger.isLoggable(Level.FINE)) - logger.fine("expand capacity to " + newsize); - IMAPMessage[] newm = new IMAPMessage[newsize + SLOP]; - System.arraycopy(messages, 0, newm, 0, messages.length); - messages = newm; - if (seqnums != null) { - int[] news = new int[newsize + SLOP]; - System.arraycopy(seqnums, 0, news, 0, seqnums.length); - for (int i = size; i < news.length; i++) - news[i] = newSeqNum++; - seqnums = news; - if (logger.isLoggable(Level.FINE)) - logger.fine("message " + newsize + - " has sequence number " + seqnums[newsize-1]); - } - } else if (newsize < size) { // shrinking? - // this should never happen - if (logger.isLoggable(Level.FINE)) - logger.fine("shrink capacity to " + newsize); - for (int msgnum = newsize + 1; msgnum <= size; msgnum++) { - messages[msgnum-1] = null; - if (seqnums != null) - seqnums[msgnum-1] = -1; - } - } - size = newsize; - } - - /** - * Return the sequence number for the given message number. - */ - public int seqnumOf(int msgnum) { - if (seqnums == null) - return msgnum; - else { - if (logger.isLoggable(Level.FINE)) - logger.fine("msgnum " + msgnum + " is seqnum " + - seqnums[msgnum-1]); - return seqnums[msgnum-1]; - } - } - - /** - * Return the message number for the given sequence number. - */ - private int msgnumOf(int seqnum) { - if (seqnums == null) - return seqnum; - if (seqnum < 1) { // should never happen - if (logger.isLoggable(Level.FINE)) - logger.fine("bad seqnum " + seqnum); - return -1; - } - for (int msgnum = seqnum; msgnum <= size; msgnum++) { - if (seqnums[msgnum-1] == seqnum) - return msgnum; - if (seqnums[msgnum-1] > seqnum) - break; // message doesn't exist - } - return -1; - } -} diff --git a/src/main/java/com/sun/mail/imap/Rights.java b/src/main/java/com/sun/mail/imap/Rights.java deleted file mode 100644 index 3705c362..00000000 --- a/src/main/java/com/sun/mail/imap/Rights.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.util.*; - -/** - * The Rights class represents the set of rights for an authentication - * identifier (for instance, a user or a group).

    - * - * A right is represented by the Rights.Right - * inner class.

    - * - * A set of standard rights are predefined (see RFC 2086). Most folder - * implementations are expected to support these rights. Some - * implementations may also support site-defined rights.

    - * - * The following code sample illustrates how to examine your - * rights for a folder.

    - *

    - *
    - * Rights rights = folder.myRights();
    - *
    - * // Check if I can write this folder
    - * if (rights.contains(Rights.Right.WRITE))
    - *	System.out.println("Can write folder");
    - *
    - * // Now give Joe all my rights, except the ability to write the folder
    - * rights.remove(Rights.Right.WRITE);
    - * ACL acl = new ACL("joe", rights);
    - * folder.setACL(acl);
    - * 
    - *

    - * - * @author Bill Shannon - */ - -public class Rights implements Cloneable { - - private boolean[] rights = new boolean[128]; // XXX - - /** - * This inner class represents an individual right. A set - * of standard rights objects are predefined here. - */ - public static final class Right { - private static Right[] cache = new Right[128]; - - // XXX - initialization order? - /** - * Lookup - mailbox is visible to LIST/LSUB commands. - */ - public static final Right LOOKUP = getInstance('l'); - - /** - * Read - SELECT the mailbox, perform CHECK, FETCH, PARTIAL, - * SEARCH, COPY from mailbox - */ - public static final Right READ = getInstance('r'); - - /** - * Keep seen/unseen information across sessions - STORE \SEEN flag. - */ - public static final Right KEEP_SEEN = getInstance('s'); - - /** - * Write - STORE flags other than \SEEN and \DELETED. - */ - public static final Right WRITE = getInstance('w'); - - /** - * Insert - perform APPEND, COPY into mailbox. - */ - public static final Right INSERT = getInstance('i'); - - /** - * Post - send mail to submission address for mailbox, - * not enforced by IMAP4 itself. - */ - public static final Right POST = getInstance('p'); - - /** - * Create - CREATE new sub-mailboxes in any implementation-defined - * hierarchy, RENAME or DELETE mailbox. - */ - public static final Right CREATE = getInstance('c'); - - /** - * Delete - STORE \DELETED flag, perform EXPUNGE. - */ - public static final Right DELETE = getInstance('d'); - - /** - * Administer - perform SETACL. - */ - public static final Right ADMINISTER = getInstance('a'); - - char right; // the right represented by this Right object - - /** - * Private constructor used only by getInstance. - */ - private Right(char right) { - if ((int)right >= 128) - throw new IllegalArgumentException("Right must be ASCII"); - this.right = right; - } - - /** - * Get a Right object representing the specified character. - * Characters are assigned per RFC 2086. - */ - public static synchronized Right getInstance(char right) { - if ((int)right >= 128) - throw new IllegalArgumentException("Right must be ASCII"); - if (cache[(int)right] == null) - cache[(int)right] = new Right(right); - return cache[(int)right]; - } - - public String toString() { - return String.valueOf(right); - } - } - - - /** - * Construct an empty Rights object. - */ - public Rights() { } - - /** - * Construct a Rights object initialized with the given rights. - * - * @param rights the rights for initialization - */ - public Rights(Rights rights) { - System.arraycopy(rights.rights, 0, this.rights, 0, this.rights.length); - } - - /** - * Construct a Rights object initialized with the given rights. - * - * @param rights the rights for initialization - */ - public Rights(String rights) { - for (int i = 0; i < rights.length(); i++) - add(Right.getInstance(rights.charAt(i))); - } - - /** - * Construct a Rights object initialized with the given right. - * - * @param right the right for initialization - */ - public Rights(Right right) { - this.rights[(int)right.right] = true; - } - - /** - * Add the specified right to this Rights object. - * - * @param right the right to add - */ - public void add(Right right) { - this.rights[(int)right.right] = true; - } - - /** - * Add all the rights in the given Rights object to this - * Rights object. - * - * @param rights Rights object - */ - public void add(Rights rights) { - for (int i = 0; i < rights.rights.length; i++) - if (rights.rights[i]) - this.rights[i] = true; - } - - /** - * Remove the specified right from this Rights object. - * - * @param right the right to be removed - */ - public void remove(Right right) { - this.rights[(int)right.right] = false; - } - - /** - * Remove all rights in the given Rights object from this - * Rights object. - * - * @param rights the rights to be removed - */ - public void remove(Rights rights) { - for (int i = 0; i < rights.rights.length; i++) - if (rights.rights[i]) - this.rights[i] = false; - } - - /** - * Check whether the specified right is present in this Rights object. - * - * @return true of the given right is present, otherwise false. - */ - public boolean contains(Right right) { - return this.rights[(int)right.right]; - } - - /** - * Check whether all the rights in the specified Rights object are - * present in this Rights object. - * - * @return true if all rights in the given Rights object are present, - * otherwise false. - */ - public boolean contains(Rights rights) { - for (int i = 0; i < rights.rights.length; i++) - if (rights.rights[i] && !this.rights[i]) - return false; - - // If we've made it till here, return true - return true; - } - - /** - * Check whether the two Rights objects are equal. - * - * @return true if they're equal - */ - public boolean equals(Object obj) { - if (!(obj instanceof Rights)) - return false; - - Rights rights = (Rights)obj; - - for (int i = 0; i < rights.rights.length; i++) - if (rights.rights[i] != this.rights[i]) - return false; - - return true; - } - - /** - * Compute a hash code for this Rights object. - * - * @return the hash code - */ - public int hashCode() { - int hash = 0; - for (int i = 0; i < this.rights.length; i++) - if (this.rights[i]) - hash++; - return hash; - } - - /** - * Return all the rights in this Rights object. Returns - * an array of size zero if no rights are set. - * - * @return array of Rights.Right objects representing rights - */ - public Right[] getRights() { - Vector v = new Vector(); - for (int i = 0; i < this.rights.length; i++) - if (this.rights[i]) - v.addElement(Right.getInstance((char)i)); - Right[] rights = new Right[v.size()]; - v.copyInto(rights); - return rights; - } - - /** - * Returns a clone of this Rights object. - */ - public Object clone() { - Rights r = null; - try { - r = (Rights)super.clone(); - r.rights = new boolean[128]; - System.arraycopy(this.rights, 0, r.rights, 0, this.rights.length); - } catch (CloneNotSupportedException cex) { - // ignore, can't happen - } - return r; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < this.rights.length; i++) - if (this.rights[i]) - sb.append((char)i); - return sb.toString(); - } - - /***** - public static void main(String argv[]) throws Exception { - // a new rights object - Rights f1 = new Rights(); - f1.add(Rights.Right.READ); - f1.add(Rights.Right.WRITE); - f1.add(Rights.Right.CREATE); - f1.add(Rights.Right.DELETE); - - // check copy constructor - Rights fc = new Rights(f1); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check clone - fc = (Rights)f1.clone(); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // add a right and make sure it still works right - f1.add(Rights.Right.ADMINISTER); - - // shouldn't be equal here - if (!f1.equals(fc) && !fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check clone - fc = (Rights)f1.clone(); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - fc.add(Rights.Right.INSERT); - if (!f1.equals(fc) && !fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check copy constructor - fc = new Rights(f1); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // another new rights object - Rights f2 = new Rights(Rights.Right.READ); - f2.add(Rights.Right.WRITE); - - if (f1.contains(Rights.Right.READ)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains(Rights.Right.WRITE)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains(Rights.Right.CREATE)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains(Rights.Right.DELETE)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f2.contains(Rights.Right.WRITE)) - System.out.println("success"); - else - System.out.println("fail"); - - - System.out.println("----------------"); - - Right[] r = f1.getRights(); - for (int i = 0; i < r.length; i++) - System.out.println(r[i]); - System.out.println("----------------"); - - if (f1.contains(f2)) // this should be true - System.out.println("success"); - else - System.out.println("fail"); - - if (!f2.contains(f1)) // this should be false - System.out.println("success"); - else - System.out.println("fail"); - - Rights f3 = new Rights(); - f3.add(Rights.Right.READ); - f3.add(Rights.Right.WRITE); - f3.add(Rights.Right.CREATE); - f3.add(Rights.Right.DELETE); - f3.add(Rights.Right.ADMINISTER); - f3.add(Rights.Right.LOOKUP); - - f1.add(Rights.Right.LOOKUP); - - if (f1.equals(f3)) - System.out.println("equals success"); - else - System.out.println("fail"); - if (f3.equals(f1)) - System.out.println("equals success"); - else - System.out.println("fail"); - System.out.println("f1 hash code " + f1.hashCode()); - System.out.println("f3 hash code " + f3.hashCode()); - if (f1.hashCode() == f3.hashCode()) - System.out.println("success"); - else - System.out.println("fail"); - } - ****/ -} diff --git a/src/main/java/com/sun/mail/imap/SortTerm.java b/src/main/java/com/sun/mail/imap/SortTerm.java deleted file mode 100644 index 8d939549..00000000 --- a/src/main/java/com/sun/mail/imap/SortTerm.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -/** - * A particular sort criteria, as defined by - * RFC 5256. - * Sort criteria are used with the - * {@link IMAPFolder#getSortedMessages getSortedMessages} method. - * Multiple sort criteria are specified in an array with the order in - * the array specifying the order in which the sort criteria are applied. - * - * @since JavaMail 1.4.4 - */ -public final class SortTerm { - /** - * Sort by message arrival date and time. - */ - public static final SortTerm ARRIVAL = new SortTerm("ARRIVAL"); - - /** - * Sort by email address of first Cc recipient. - */ - public static final SortTerm CC = new SortTerm("CC"); - - /** - * Sort by sent date and time. - */ - public static final SortTerm DATE = new SortTerm("DATE"); - - /** - * Sort by first From email address. - */ - public static final SortTerm FROM = new SortTerm("FROM"); - - /** - * Reverse the sort order of the following item. - */ - public static final SortTerm REVERSE = new SortTerm("REVERSE"); - - /** - * Sort by the message size. - */ - public static final SortTerm SIZE = new SortTerm("SIZE"); - - /** - * Sort by the base subject text. Note that the "base subject" - * is defined by RFC 5256 and doesn't include items such as "Re:" - * in the subject header. - */ - public static final SortTerm SUBJECT = new SortTerm("SUBJECT"); - - /** - * Sort by email address of first To recipient. - */ - public static final SortTerm TO = new SortTerm("TO"); - - private String term; - private SortTerm(String term) { - this.term = term; - } - - public String toString() { - return term; - } -} diff --git a/src/main/java/com/sun/mail/imap/Utility.java b/src/main/java/com/sun/mail/imap/Utility.java deleted file mode 100644 index e0d2b224..00000000 --- a/src/main/java/com/sun/mail/imap/Utility.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap; - -import java.util.Vector; - -import javax.mail.*; - -import com.sun.mail.util.*; -import com.sun.mail.imap.protocol.MessageSet; -import com.sun.mail.imap.protocol.UIDSet; - -/** - * Holder for some static utility methods. - * - * @author John Mani - * @author Bill Shannon - */ - -public final class Utility { - - // Cannot be initialized - private Utility() { } - - /** - * Run thru the given array of messages, apply the given - * Condition on each message and generate sets of contiguous - * sequence-numbers for the successful messages. If a message - * in the given array is found to be expunged, it is ignored. - * - * ASSERT: Since this method uses and returns message sequence - * numbers, you should use this method only when holding the - * messageCacheLock. - */ - public static - MessageSet[] toMessageSet(Message[] msgs, Condition cond) { - Vector v = new Vector(1); - int current, next; - - IMAPMessage msg; - for (int i = 0; i < msgs.length; i++) { - msg = (IMAPMessage)msgs[i]; - if (msg.isExpunged()) // expunged message, skip it - continue; - - current = msg.getSequenceNumber(); - // Apply the condition. If it fails, skip it. - if ((cond != null) && !cond.test(msg)) - continue; - - MessageSet set = new MessageSet(); - set.start = current; - - // Look for contiguous sequence numbers - for (++i; i < msgs.length; i++) { - // get next message - msg = (IMAPMessage)msgs[i]; - - if (msg.isExpunged()) // expunged message, skip it - continue; - next = msg.getSequenceNumber(); - - // Does this message match our condition ? - if ((cond != null) && !cond.test(msg)) - continue; - - if (next == current+1) - current = next; - else { // break in sequence - // We need to reexamine this message at the top of - // the outer loop, so decrement 'i' to cancel the - // outer loop's autoincrement - i--; - break; - } - } - set.end = current; - v.addElement(set); - } - - if (v.isEmpty()) // No valid messages - return null; - else { - MessageSet[] sets = new MessageSet[v.size()]; - v.copyInto(sets); - return sets; - } - } - - /** - * Return UIDSets for the messages. Note that the UIDs - * must have already been fetched for the messages. - */ - public static UIDSet[] toUIDSet(Message[] msgs) { - Vector v = new Vector(1); - long current, next; - - IMAPMessage msg; - for (int i = 0; i < msgs.length; i++) { - msg = (IMAPMessage)msgs[i]; - if (msg.isExpunged()) // expunged message, skip it - continue; - - current = msg.getUID(); - - UIDSet set = new UIDSet(); - set.start = current; - - // Look for contiguous UIDs - for (++i; i < msgs.length; i++) { - // get next message - msg = (IMAPMessage)msgs[i]; - - if (msg.isExpunged()) // expunged message, skip it - continue; - next = msg.getUID(); - - if (next == current+1) - current = next; - else { // break in sequence - // We need to reexamine this message at the top of - // the outer loop, so decrement 'i' to cancel the - // outer loop's autoincrement - i--; - break; - } - } - set.end = current; - v.addElement(set); - } - - if (v.isEmpty()) // No valid messages - return null; - else { - UIDSet[] sets = new UIDSet[v.size()]; - v.copyInto(sets); - return sets; - } - } - - /** - * This interface defines the test to be executed in - * toMessageSet(). - */ - public static interface Condition { - public boolean test(IMAPMessage message); - } -} diff --git a/src/main/java/com/sun/mail/imap/package.html b/src/main/java/com/sun/mail/imap/package.html deleted file mode 100644 index 336677ab..00000000 --- a/src/main/java/com/sun/mail/imap/package.html +++ /dev/null @@ -1,623 +0,0 @@ - - - - - - - - -An IMAP protocol provider for the JavaMail API -that provides access to an IMAP message store. -Both the IMAP4 and IMAP4rev1 protocols are supported. -Refer to -RFC 2060 -for more information. -

    -The IMAP protocol provider can use SASL -(RFC 2222) -authentication mechanisms on systems that support the -javax.security.sasl APIs, such as J2SE 5.0. -In addition to the SASL mechanisms that are built into -the SASL implementation, users can also provide additional -SASL mechanisms of their own design to support custom authentication -schemes. See the - -Java SASL API Programming and Deployment Guide for details. -Note that the current implementation doesn't support SASL mechanisms -that provide their own integrity or confidentiality layer. -

    -A connected IMAPStore maintains a pool of IMAP protocol objects for -use in communicating with the IMAP server. The IMAPStore will create -the initial AUTHENTICATED connection and seed the pool with this -connection. As folders are opened and new IMAP protocol objects are -needed, the IMAPStore will provide them from the connection pool, -or create them if none are available. When a folder is closed, -its IMAP protocol object is returned to the connection pool if the -pool is not over capacity. -

    -A mechanism is provided for timing out idle connection pool IMAP -protocol objects. Timed out connections are closed and removed (pruned) -from the connection pool. -

    -The connected IMAPStore object may or may not maintain a separate IMAP -protocol object that provides the store a dedicated connection to the -IMAP server. This is provided mainly for compatibility with previous -implementations of the IMAP protocol provider. -

    -The IMAP protocol provider supports the following properties, -which may be set in the JavaMail Session object. -The properties are always set as strings; the Type column describes -how the string is interpreted. For example, use -

    -	props.put("mail.imap.port", "888");
    -
    -to set the mail.imap.port property, which is of type int. -

    -Note that if you're using the "imaps" protocol to access IMAP over SSL, -all the properties would be named "mail.imaps.*". -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    mail.imap.userStringDefault user name for IMAP.
    mail.imap.hostStringThe IMAP server to connect to.
    mail.imap.portintThe IMAP server port to connect to, if the connect() method doesn't -explicitly specify one. Defaults to 143.
    mail.imap.partialfetchbooleanControls whether the IMAP partial-fetch capability should be used. -Defaults to true.
    mail.imap.fetchsizeintPartial fetch size in bytes. Defaults to 16K.
    mail.imap.ignorebodystructuresizebooleanThe IMAP BODYSTRUCTURE response includes the exact size of each body part. -Normally, this size is used to determine how much data to fetch for each -body part. -Some servers report this size incorrectly in some cases; this property can -be set to work around such server bugs. -If this property is set to true, this size is ignored and data is fetched -until the server reports the end of data. -This will result in an extra fetch if the data size is a multiple of the -block size. -Defaults to false.
    mail.imap.connectiontimeoutintSocket connection timeout value in milliseconds. -Default is infinite timeout.
    mail.imap.timeoutintSocket I/O timeout value in milliseconds. Default is infinite timeout.
    mail.imap.statuscachetimeoutintTimeout value in milliseconds for cache of STATUS command response. -Default is 1000 (1 second). Zero disables cache.
    mail.imap.appendbuffersizeint -Maximum size of a message to buffer in memory when appending to an IMAP -folder. If not set, or set to -1, there is no maximum and all messages -are buffered. If set to 0, no messages are buffered. If set to (e.g.) -8192, messages of 8K bytes or less are buffered, larger messages are -not buffered. Buffering saves cpu time at the expense of short term -memory usage. If you commonly append very large messages to IMAP -mailboxes you might want to set this to a moderate value (1M or less). -
    mail.imap.connectionpoolsizeintMaximum number of available connections in the connection pool. -Default is 1.
    mail.imap.connectionpooltimeoutintTimeout value in milliseconds for connection pool connections. Default -is 45000 (45 seconds).
    mail.imap.separatestoreconnectionbooleanFlag to indicate whether to use a dedicated store connection for store -commands. Default is false.
    mail.imap.allowreadonlyselectbooleanIf false, attempts to open a folder read/write will fail -if the SELECT command succeeds but indicates that the folder is READ-ONLY. -This sometimes indicates that the folder contents can'tbe changed, but -the flags are per-user and can be changed, such as might be the case for -public shared folders. If true, such open attempts will succeed, allowing -the flags to be changed. The getMode method on the -Folder object will return Folder.READ_ONLY -in this case even though the open method specified -Folder.READ_WRITE. Default is false.
    mail.imap.auth.login.disablebooleanIf true, prevents use of the non-standard AUTHENTICATE LOGIN -command, instead using the plain LOGIN command. -Default is false.
    mail.imap.auth.plain.disablebooleanIf true, prevents use of the AUTHENTICATE PLAIN command. -Default is false.
    mail.imap.auth.ntlm.disablebooleanIf true, prevents use of the AUTHENTICATE NTLM command. -Default is false.
    mail.imap.proxyauth.userStringIf the server supports the PROXYAUTH extension, this property -specifies the name of the user to act as. Authenticate to the -server using the administrator's credentials. After authentication, -the IMAP provider will issue the PROXYAUTH command with -the user name specified in this property. -
    mail.imap.localaddressString -Local address (host name) to bind to when creating the IMAP socket. -Defaults to the address picked by the Socket class. -Should not normally need to be set, but useful with multi-homed hosts -where it's important to pick a particular local address to bind to. -
    mail.imap.localportint -Local port number to bind to when creating the IMAP socket. -Defaults to the port number picked by the Socket class. -
    mail.imap.sasl.enableboolean -If set to true, attempt to use the javax.security.sasl package to -choose an authentication mechanism for login. -Defaults to false. -
    mail.imap.sasl.mechanismsString -A space or comma separated list of SASL mechanism names to try -to use. -
    mail.imap.sasl.authorizationidString -The authorization ID to use in the SASL authentication. -If not set, the authentication ID (user name) is used. -
    mail.imap.sasl.realmStringThe realm to use with SASL authentication mechanisms that -require a realm, such as DIGEST-MD5.
    mail.imap.sasl. xgwtrustedapphack.enableboolean -If set to true, enables a workaround for a bug in the Novell Groupwise -XGWTRUSTEDAPP SASL mechanism, when that mechanism is being used. -Defaults to true. -
    mail.imap.auth.ntlm.domainString -The NTLM authentication domain. -
    mail.imap.auth.ntlm.flagsint -NTLM protocol-specific flags. -See -http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details. -
    mail.imap.socketFactorySocketFactory -If set to a class that implements the -javax.net.SocketFactory interface, this class -will be used to create IMAP sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.imap.socketFactory.classString -If set, specifies the name of a class that implements the -javax.net.SocketFactory interface. This class -will be used to create IMAP sockets. -
    mail.imap.socketFactory.fallbackboolean -If set to true, failure to create a socket using the specified -socket factory class will cause the socket to be created using -the java.net.Socket class. -Defaults to true. -
    mail.imap.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.imap.ssl.enableboolean -If set to true, use SSL to connect and use the SSL port by default. -Defaults to false for the "imap" protocol and true for the "imaps" protocol. -
    mail.imap.ssl.checkserveridentityboolean -If set to true, check the server identity as specified by -RFC 2595. -These additional checks based on the content of the server's certificate -are intended to prevent man-in-the-middle attacks. -Defaults to false. -
    mail.imap.ssl.trustString -If set, and a socket factory hasn't been specified, enables use of a -{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. -If set to "*", all hosts are trusted. -If set to a whitespace separated list of hosts, those hosts are trusted. -Otherwise, trust depends on the certificate the server presents. -
    mail.imap.ssl.socketFactorySSLSocketFactory -If set to a class that extends the -javax.net.ssl.SSLSocketFactory class, this class -will be used to create IMAP SSL sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.imap.ssl.socketFactory.classString -If set, specifies the name of a class that extends the -javax.net.ssl.SSLSocketFactory class. This class -will be used to create IMAP SSL sockets. -
    mail.imap.ssl.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.imap.ssl.protocolsstring -Specifies the SSL protocols that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledProtocols method. -
    mail.imap.ssl.ciphersuitesstring -Specifies the SSL cipher suites that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. -
    mail.imap.starttls.enablebooleanIf true, enables the use of the STARTTLS command (if -supported by the server) to switch the connection to a TLS-protected -connection before issuing any login commands. Note that an appropriate -trust store must configured so that the client will trust the server's -certificate. This feature only works on J2SE 1.4 and newer systems. -Default is false.
    mail.imap.starttls.requiredboolean -If true, requires the use of the STARTTLS command. -If the server doesn't support the STARTTLS command, or the command -fails, the connect method will fail. -Defaults to false. -
    mail.imap.socks.hoststring -Specifies the host name of a SOCKS5 proxy server that will be used for -connections to the mail server. -(Note that this only works on JDK 1.5 or newer.) -
    mail.imap.socks.portstring -Specifies the port number for the SOCKS5 proxy server. -This should only need to be used if the proxy server is not using -the standard port number of 1080. -
    mail.imap.minidletimeint -Applications typically call the idle method in a loop. If another -thread termiantes the IDLE command, it needs a chance to do its -work before another IDLE command is issued. The idle method enforces -a delay to prevent thrashing between the IDLE command and regular -commands. This property sets the delay in milliseconds. If not -set, the default is 10 milliseconds. -
    mail.imap.enableimapeventsboolean -Enable special IMAP-specific events to be delivered to the Store's -ConnectionListener. If true, unsolicited responses -received during the Store's idle method will be sent -as ConnectionEvents with a type of -IMAPStore.RESPONSE. The event's message will be the -raw IMAP response string. -By default, these events are not sent. -NOTE: This capability is highly experimental and likely will change -in future releases. -
    mail.imap.folder.classString -Class name of a subclass of com.sun.mail.imap.IMAPFolder. -The subclass can be used to provide support for additional IMAP commands. -The subclass must have public constructors of the form -public MyIMAPFolder(String fullName, char separator, IMAPStore store, -Boolean isNamespace) and -public MyIMAPFolder(ListInfo li, IMAPStore store) -
    -

    -In general, applications should not need to use the classes in this -package directly. Instead, they should use the APIs defined by -javax.mail package (and subpackages). Applications should -never construct instances of IMAPStore or -IMAPFolder directly. Instead, they should use the -Session method getStore to acquire an -appropriate Store object, and from that acquire -Folder objects. -

    -In addition to printing debugging output as controlled by the -{@link javax.mail.Session Session} configuration, -the com.sun.mail.imap provider logs the same information using -{@link java.util.logging.Logger} as described in the following table: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Logger NameLogging LevelPurpose
    com.sun.mail.imapCONFIGConfiguration of the IMAPStore
    com.sun.mail.imapFINEGeneral debugging output
    com.sun.mail.imap.connectionpoolCONFIGConfiguration of the IMAP connection pool
    com.sun.mail.imap.connectionpoolFINEDebugging output related to the IMAP connection pool
    com.sun.mail.imap.messagecacheCONFIGConfiguration of the IMAP message cache
    com.sun.mail.imap.messagecacheFINEDebugging output related to the IMAP message cache
    com.sun.mail.imap.protocolFINESTComplete protocol trace
    - -

    -WARNING: The APIs unique to this package should be -considered EXPERIMENTAL. They may be changed in the -future in ways that are incompatible with applications using the -current APIs. - - - diff --git a/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java b/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java deleted file mode 100644 index 9f0a8df5..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.text.StringCharacterIterator; -import java.text.CharacterIterator; - -/** - * See the BASE64MailboxEncoder for a description of the RFC2060 and how - * mailbox names should be encoded. This class will do the correct decoding - * for mailbox names. - * - * @author Christopher Cotton - */ - -public class BASE64MailboxDecoder { - - public static String decode(String original) { - if (original == null || original.length() == 0) - return original; - - boolean changedString = false; - int copyTo = 0; - // it will always be less than the original - char[] chars = new char[original.length()]; - StringCharacterIterator iter = new StringCharacterIterator(original); - - for(char c = iter.first(); c != CharacterIterator.DONE; - c = iter.next()) { - - if (c == '&') { - changedString = true; - copyTo = base64decode(chars, copyTo, iter); - } else { - chars[copyTo++] = c; - } - } - - // now create our string from the char array - if (changedString) { - return new String(chars, 0, copyTo); - } else { - return original; - } - } - - - protected static int base64decode(char[] buffer, int offset, - CharacterIterator iter) { - boolean firsttime = true; - int leftover = -1; - - while(true) { - // get the first byte - byte orig_0 = (byte) iter.next(); - if (orig_0 == -1) break; // no more chars - if (orig_0 == '-') { - if (firsttime) { - // means we got the string "&-" which is turned into a "&" - buffer[offset++] = '&'; - } - // we are done now - break; - } - firsttime = false; - - // next byte - byte orig_1 = (byte) iter.next(); - if (orig_1 == -1 || orig_1 == '-') - break; // no more chars, invalid base64 - - byte a, b, current; - a = pem_convert_array[orig_0 & 0xff]; - b = pem_convert_array[orig_1 & 0xff]; - // The first decoded byte - current = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)); - - // use the leftover to create a Unicode Character (2 bytes) - if (leftover != -1) { - buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); - leftover = -1; - } else { - leftover = current & 0xff; - } - - byte orig_2 = (byte) iter.next(); - if (orig_2 == '=') { // End of this BASE64 encoding - continue; - } else if (orig_2 == -1 || orig_2 == '-') { - break; // no more chars - } - - // second decoded byte - a = b; - b = pem_convert_array[orig_2 & 0xff]; - current = (byte)(((a << 4) & 0xf0) | ((b >>> 2) & 0xf)); - - // use the leftover to create a Unicode Character (2 bytes) - if (leftover != -1) { - buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); - leftover = -1; - } else { - leftover = current & 0xff; - } - - byte orig_3 = (byte) iter.next(); - if (orig_3 == '=') { // End of this BASE64 encoding - continue; - } else if (orig_3 == -1 || orig_3 == '-') { - break; // no more chars - } - - // The third decoded byte - a = b; - b = pem_convert_array[orig_3 & 0xff]; - current = (byte)(((a << 6) & 0xc0) | (b & 0x3f)); - - // use the leftover to create a Unicode Character (2 bytes) - if (leftover != -1) { - buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); - leftover = -1; - } else { - leftover = current & 0xff; - } - } - - return offset; - } - - /** - * This character array provides the character to value map - * based on RFC1521, but with the modification from RFC2060 - * which changes the '/' to a ','. - */ - - // shared with BASE64MailboxEncoder - static final char pem_array[] = { - 'A','B','C','D','E','F','G','H', // 0 - 'I','J','K','L','M','N','O','P', // 1 - 'Q','R','S','T','U','V','W','X', // 2 - 'Y','Z','a','b','c','d','e','f', // 3 - 'g','h','i','j','k','l','m','n', // 4 - 'o','p','q','r','s','t','u','v', // 5 - 'w','x','y','z','0','1','2','3', // 6 - '4','5','6','7','8','9','+',',' // 7 - }; - - private static final byte pem_convert_array[] = new byte[256]; - - static { - for (int i = 0; i < 255; i++) - pem_convert_array[i] = -1; - for(int i = 0; i < pem_array.length; i++) - pem_convert_array[pem_array[i]] = (byte) i; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java b/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java deleted file mode 100644 index 55998fa0..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.*; - - -/** - * - * - from RFC2060 - -5.1.3. Mailbox International Naming Convention - - By convention, international mailbox names are specified using a - modified version of the UTF-7 encoding described in [UTF-7]. The - purpose of these modifications is to correct the following problems - with UTF-7: - - 1) UTF-7 uses the "+" character for shifting; this conflicts with - the common use of "+" in mailbox names, in particular USENET - newsgroup names. - - 2) UTF-7's encoding is BASE64 which uses the "/" character; this - conflicts with the use of "/" as a popular hierarchy delimiter. - - 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with - the use of "\" as a popular hierarchy delimiter. - - 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with - the use of "~" in some servers as a home directory indicator. - - 5) UTF-7 permits multiple alternate forms to represent the same - string; in particular, printable US-ASCII chararacters can be - represented in encoded form. - - In modified UTF-7, printable US-ASCII characters except for "&" - represent themselves; that is, characters with octet values 0x20-0x25 - and 0x27-0x7e. The character "&" (0x26) is represented by the two- - octet sequence "&-". - - All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all - Unicode 16-bit octets) are represented in modified BASE64, with a - further modification from [UTF-7] that "," is used instead of "/". - Modified BASE64 MUST NOT be used to represent any printing US-ASCII - character which can represent itself. - - "&" is used to shift to modified BASE64 and "-" to shift back to US- - ASCII. All names start in US-ASCII, and MUST end in US-ASCII (that - is, a name that ends with a Unicode 16-bit octet MUST end with a "- - "). - - - - - -Crispin Standards Track [Page 15] - -RFC 2060 IMAP4rev1 December 1996 - - - For example, here is a mailbox name which mixes English, Japanese, - and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw- - - - * This class will do the correct Encoding for the IMAP mailboxes - * - * @author Christopher Cotton - */ - -public class BASE64MailboxEncoder { - protected byte[] buffer = new byte[4]; - protected int bufsize = 0; - protected boolean started = false; - protected Writer out = null; - - - public static String encode(String original) { - BASE64MailboxEncoder base64stream = null; - char origchars[] = original.toCharArray(); - int length = origchars.length; - boolean changedString = false; - CharArrayWriter writer = new CharArrayWriter(length); - - // loop over all the chars - for(int index = 0; index < length; index++) { - char current = origchars[index]; - - // octets in the range 0x20-0x25,0x27-0x7e are themselves - // 0x26 "&" is represented as "&-" - if (current >= 0x20 && current <= 0x7e) { - if (base64stream != null) { - base64stream.flush(); - } - - if (current == '&') { - changedString = true; - writer.write('&'); - writer.write('-'); - } else { - writer.write(current); - } - } else { - - // use a B64MailboxEncoder to write out the other bytes - // as a modified BASE64. The stream will write out - // the beginning '&' and the ending '-' which is part - // of every encoding. - - if (base64stream == null) { - base64stream = new BASE64MailboxEncoder(writer); - changedString = true; - } - - base64stream.write(current); - } - } - - - if (base64stream != null) { - base64stream.flush(); - } - - if (changedString) { - return writer.toString(); - } else { - return original; - } - } - - - /** - * Create a BASE64 encoder - */ - public BASE64MailboxEncoder(Writer what) { - out = what; - } - - public void write(int c) { - try { - // write out the initial character if this is the first time - if (!started) { - started = true; - out.write('&'); - } - - // we write each character as a 2 byte unicode character - buffer[bufsize++] = (byte) (c >> 8); - buffer[bufsize++] = (byte) (c & 0xff); - - if (bufsize >= 3) { - encode(); - bufsize -= 3; - } - } catch (IOException e) { - //e.printStackTrace(); - } - } - - - public void flush() { - try { - // flush any bytes we have - if (bufsize > 0) { - encode(); - bufsize = 0; - } - - // write the terminating character of the encoding - if (started) { - out.write('-'); - started = false; - } - } catch (IOException e) { - //e.printStackTrace(); - } - } - - - protected void encode() throws IOException { - byte a, b, c; - if (bufsize == 1) { - a = buffer[0]; - b = 0; - c = 0; - out.write(pem_array[(a >>> 2) & 0x3F]); - out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); - // no padding characters are written - } else if (bufsize == 2) { - a = buffer[0]; - b = buffer[1]; - c = 0; - out.write(pem_array[(a >>> 2) & 0x3F]); - out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); - out.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); - // no padding characters are written - } else { - a = buffer[0]; - b = buffer[1]; - c = buffer[2]; - out.write(pem_array[(a >>> 2) & 0x3F]); - out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); - out.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); - out.write(pem_array[c & 0x3F]); - - // copy back the extra byte - if (bufsize == 4) - buffer[0] = buffer[3]; - } - } - - private final static char pem_array[] = { - 'A','B','C','D','E','F','G','H', // 0 - 'I','J','K','L','M','N','O','P', // 1 - 'Q','R','S','T','U','V','W','X', // 2 - 'Y','Z','a','b','c','d','e','f', // 3 - 'g','h','i','j','k','l','m','n', // 4 - 'o','p','q','r','s','t','u','v', // 5 - 'w','x','y','z','0','1','2','3', // 6 - '4','5','6','7','8','9','+',',' // 7 - }; -} diff --git a/src/main/java/com/sun/mail/imap/protocol/BODY.java b/src/main/java/com/sun/mail/imap/protocol/BODY.java deleted file mode 100644 index 040a9aa5..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/BODY.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.ByteArrayInputStream; -import com.sun.mail.iap.*; -import com.sun.mail.util.ASCIIUtility; - -/** - * The BODY fetch response item. - * - * @author John Mani - */ - -public class BODY implements Item { - - static final char[] name = {'B','O','D','Y'}; - - public int msgno; - public ByteArray data; - public String section; - public int origin = 0; - - /** - * Constructor - */ - public BODY(FetchResponse r) throws ParsingException { - msgno = r.getNumber(); - - r.skipSpaces(); - - int b; - while ((b = r.readByte()) != ']') { // skip section - if (b == 0) - throw new ParsingException( - "BODY parse error: missing ``]'' at section end"); - } - - - if (r.readByte() == '<') { // origin - origin = r.readNumber(); - r.skip(1); // skip '>'; - } - - data = r.readByteArray(); - } - - public ByteArray getByteArray() { - return data; - } - - public ByteArrayInputStream getByteArrayInputStream() { - if (data != null) - return data.toByteArrayInputStream(); - else - return null; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java b/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java deleted file mode 100644 index 580d4bb4..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Vector; -import javax.mail.internet.ParameterList; -import com.sun.mail.iap.*; -import com.sun.mail.util.PropUtil; - -/** - * A BODYSTRUCTURE response. - * - * @author John Mani - * @author Bill Shannon - */ - -public class BODYSTRUCTURE implements Item { - - static final char[] name = - {'B','O','D','Y','S','T','R','U','C','T','U','R','E'}; - public int msgno; - - public String type; // Type - public String subtype; // Subtype - public String encoding; // Encoding - public int lines = -1; // Size in lines - public int size = -1; // Size in bytes - public String disposition; // Disposition - public String id; // Content-ID - public String description; // Content-Description - public String md5; // MD-5 checksum - public String attachment; // Attachment name - public ParameterList cParams; // Body parameters - public ParameterList dParams; // Disposition parameters - public String[] language; // Language - public BODYSTRUCTURE[] bodies; // array of BODYSTRUCTURE objects - // for multipart & message/rfc822 - public ENVELOPE envelope; // for message/rfc822 - - private static int SINGLE = 1; - private static int MULTI = 2; - private static int NESTED = 3; - private int processedType; // MULTI | SINGLE | NESTED - - // special debugging output to debug parsing errors - private static boolean parseDebug = - PropUtil.getBooleanSystemProperty("mail.imap.parse.debug", false); - - - public BODYSTRUCTURE(FetchResponse r) throws ParsingException { - if (parseDebug) - System.out.println("DEBUG IMAP: parsing BODYSTRUCTURE"); - msgno = r.getNumber(); - if (parseDebug) - System.out.println("DEBUG IMAP: msgno " + msgno); - - r.skipSpaces(); - - if (r.readByte() != '(') - throw new ParsingException( - "BODYSTRUCTURE parse error: missing ``('' at start"); - - if (r.peekByte() == '(') { // multipart - if (parseDebug) - System.out.println("DEBUG IMAP: parsing multipart"); - type = "multipart"; - processedType = MULTI; - Vector v = new Vector(1); - int i = 1; - do { - v.addElement(new BODYSTRUCTURE(r)); - /* - * Even though the IMAP spec says there can't be any spaces - * between parts, some servers erroneously put a space in - * here. In the spirit of "be liberal in what you accept", - * we skip it. - */ - r.skipSpaces(); - } while (r.peekByte() == '('); - - // setup bodies. - bodies = new BODYSTRUCTURE[v.size()]; - v.copyInto(bodies); - - subtype = r.readString(); // subtype - if (parseDebug) - System.out.println("DEBUG IMAP: subtype " + subtype); - - if (r.readByte() == ')') { // done - if (parseDebug) - System.out.println("DEBUG IMAP: parse DONE"); - return; - } - - // Else, we have extension data - - if (parseDebug) - System.out.println("DEBUG IMAP: parsing extension data"); - // Body parameters - cParams = parseParameters(r); - if (r.readByte() == ')') { // done - if (parseDebug) - System.out.println("DEBUG IMAP: body parameters DONE"); - return; - } - - // Disposition - byte b = r.readByte(); - if (b == '(') { - if (parseDebug) - System.out.println("DEBUG IMAP: parse disposition"); - disposition = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: disposition " + - disposition); - dParams = parseParameters(r); - if (r.readByte() != ')') // eat the end ')' - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - "missing ``)'' at end of disposition in multipart"); - if (parseDebug) - System.out.println("DEBUG IMAP: disposition DONE"); - } else if (b == 'N' || b == 'n') { - if (parseDebug) - System.out.println("DEBUG IMAP: disposition NIL"); - r.skip(2); // skip 'NIL' - } else { - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - type + "/" + subtype + ": " + - "bad multipart disposition, b " + b); - } - - // RFC3501 allows no body-fld-lang after body-fld-disp, - // even though RFC2060 required it - if ((b = r.readByte()) == ')') { - if (parseDebug) - System.out.println("DEBUG IMAP: no body-fld-lang"); - return; // done - } - - if (b != ' ') - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - "missing space after disposition"); - - // Language - if (r.peekByte() == '(') { // a list follows - language = r.readStringList(); - if (parseDebug) - System.out.println( - "DEBUG IMAP: language len " + language.length); - } else { - String l = r.readString(); - if (l != null) { - String[] la = { l }; - language = la; - if (parseDebug) - System.out.println("DEBUG IMAP: language " + l); - } - } - - // RFC3501 defines an optional "body location" next, - // but for now we ignore it along with other extensions. - - // Throw away any further extension data - while (r.readByte() == ' ') - parseBodyExtension(r); - } - else { // Single part - if (parseDebug) - System.out.println("DEBUG IMAP: single part"); - type = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: type " + type); - processedType = SINGLE; - subtype = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: subtype " + subtype); - - // SIMS 4.0 returns NIL for a Content-Type of "binary", fix it here - if (type == null) { - type = "application"; - subtype = "octet-stream"; - } - cParams = parseParameters(r); - if (parseDebug) - System.out.println("DEBUG IMAP: cParams " + cParams); - id = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: id " + id); - description = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: description " + description); - /* - * XXX - Work around bug in Exchange 2010 that - * returns unquoted string. - */ - encoding = r.readAtomString(); - if (encoding != null && encoding.equalsIgnoreCase("NIL")) - encoding = null; - if (parseDebug) - System.out.println("DEBUG IMAP: encoding " + encoding); - size = r.readNumber(); - if (parseDebug) - System.out.println("DEBUG IMAP: size " + size); - if (size < 0) - throw new ParsingException( - "BODYSTRUCTURE parse error: bad ``size'' element"); - - // "text/*" & "message/rfc822" types have additional data .. - if (type.equalsIgnoreCase("text")) { - lines = r.readNumber(); - if (parseDebug) - System.out.println("DEBUG IMAP: lines " + lines); - if (lines < 0) - throw new ParsingException( - "BODYSTRUCTURE parse error: bad ``lines'' element"); - } else if (type.equalsIgnoreCase("message") && - subtype.equalsIgnoreCase("rfc822")) { - // Nested message - processedType = NESTED; - // The envelope comes next, but sadly Gmail handles nested - // messages just like simple body parts and fails to return - // the envelope and body structure of the message (sort of - // like IMAP4 before rev1). - r.skipSpaces(); - if (r.peekByte() == '(') { // the envelope follows - envelope = new ENVELOPE(r); - if (parseDebug) - System.out.println( - "DEBUG IMAP: got envelope of nested message"); - BODYSTRUCTURE[] bs = { new BODYSTRUCTURE(r) }; - bodies = bs; - lines = r.readNumber(); - if (parseDebug) - System.out.println("DEBUG IMAP: lines " + lines); - if (lines < 0) - throw new ParsingException( - "BODYSTRUCTURE parse error: bad ``lines'' element"); - } else { - if (parseDebug) - System.out.println("DEBUG IMAP: " + - "missing envelope and body of nested message"); - } - } else { - // Detect common error of including lines element on other types - r.skipSpaces(); - byte bn = r.peekByte(); - if (Character.isDigit((char)bn)) // number - throw new ParsingException( - "BODYSTRUCTURE parse error: server erroneously " + - "included ``lines'' element with type " + - type + "/" + subtype); - } - - if (r.peekByte() == ')') { - r.readByte(); - if (parseDebug) - System.out.println("DEBUG IMAP: parse DONE"); - return; // done - } - - // Optional extension data - - // MD5 - md5 = r.readString(); - if (r.readByte() == ')') { - if (parseDebug) - System.out.println("DEBUG IMAP: no MD5 DONE"); - return; // done - } - - // Disposition - byte b = r.readByte(); - if (b == '(') { - disposition = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: disposition " + - disposition); - dParams = parseParameters(r); - if (parseDebug) - System.out.println("DEBUG IMAP: dParams " + dParams); - if (r.readByte() != ')') // eat the end ')' - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - "missing ``)'' at end of disposition"); - } else if (b == 'N' || b == 'n') { - if (parseDebug) - System.out.println("DEBUG IMAP: disposition NIL"); - r.skip(2); // skip 'NIL' - } else { - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - type + "/" + subtype + ": " + - "bad single part disposition, b " + b); - } - - if (r.readByte() == ')') { - if (parseDebug) - System.out.println("DEBUG IMAP: disposition DONE"); - return; // done - } - - // Language - if (r.peekByte() == '(') { // a list follows - language = r.readStringList(); - if (parseDebug) - System.out.println("DEBUG IMAP: language len " + - language.length); - } else { // protocol is unnessarily complex here - String l = r.readString(); - if (l != null) { - String[] la = { l }; - language = la; - if (parseDebug) - System.out.println("DEBUG IMAP: language " + l); - } - } - - // RFC3501 defines an optional "body location" next, - // but for now we ignore it along with other extensions. - - // Throw away any further extension data - while (r.readByte() == ' ') - parseBodyExtension(r); - if (parseDebug) - System.out.println("DEBUG IMAP: all DONE"); - } - } - - public boolean isMulti() { - return processedType == MULTI; - } - - public boolean isSingle() { - return processedType == SINGLE; - } - - public boolean isNested() { - return processedType == NESTED; - } - - private ParameterList parseParameters(Response r) - throws ParsingException { - r.skipSpaces(); - - ParameterList list = null; - byte b = r.readByte(); - if (b == '(') { - list = new ParameterList(); - do { - String name = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: parameter name " + name); - if (name == null) - throw new ParsingException( - "BODYSTRUCTURE parse error: " + - type + "/" + subtype + ": " + - "null name in parameter list"); - String value = r.readString(); - if (parseDebug) - System.out.println("DEBUG IMAP: parameter value " + value); - list.set(name, value); - } while (r.readByte() != ')'); - list.combineSegments(); - } else if (b == 'N' || b == 'n') { - if (parseDebug) - System.out.println("DEBUG IMAP: parameter list NIL"); - r.skip(2); - } else - throw new ParsingException("Parameter list parse error"); - - return list; - } - - private void parseBodyExtension(Response r) throws ParsingException { - r.skipSpaces(); - - byte b = r.peekByte(); - if (b == '(') { - r.skip(1); // skip '(' - do { - parseBodyExtension(r); - } while (r.readByte() != ')'); - } else if (Character.isDigit((char)b)) // number - r.readNumber(); - else // nstring - r.readString(); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java b/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java deleted file mode 100644 index 5ea81c62..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Vector; -import java.util.Date; -import java.io.UnsupportedEncodingException; -import java.text.ParseException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.AddressException; -import javax.mail.internet.MailDateFormat; -import javax.mail.internet.MimeUtility; -import com.sun.mail.iap.*; - -/** - * The ENEVELOPE item of an IMAP FETCH response. - * - * @author John Mani - * @author Bill Shannon - */ - -public class ENVELOPE implements Item { - - // IMAP item name - static final char[] name = {'E','N','V','E','L','O','P','E'}; - public int msgno; - - public Date date = null; - public String subject; - public InternetAddress[] from; - public InternetAddress[] sender; - public InternetAddress[] replyTo; - public InternetAddress[] to; - public InternetAddress[] cc; - public InternetAddress[] bcc; - public String inReplyTo; - public String messageId; - - // Used to parse dates - private static MailDateFormat mailDateFormat = new MailDateFormat(); - - public ENVELOPE(FetchResponse r) throws ParsingException { - msgno = r.getNumber(); - - r.skipSpaces(); - - if (r.readByte() != '(') - throw new ParsingException("ENVELOPE parse error"); - - String s = r.readString(); - if (s != null) { - try { - date = mailDateFormat.parse(s); - } catch (Exception pex) { - // We need to be *very* tolerant about bogus dates (and - // there's lot of 'em around), so we ignore any - // exception (including RunTimeExceptions) and just let - // date be null. - } - } - - subject = r.readString(); - from = parseAddressList(r); - sender = parseAddressList(r); - replyTo = parseAddressList(r); - to = parseAddressList(r); - cc = parseAddressList(r); - bcc = parseAddressList(r); - inReplyTo = r.readString(); - messageId = r.readString(); - - if (r.readByte() != ')') - throw new ParsingException("ENVELOPE parse error"); - } - - private InternetAddress[] parseAddressList(Response r) - throws ParsingException { - r.skipSpaces(); // skip leading spaces - - byte b = r.readByte(); - if (b == '(') { - Vector v = new Vector(); - - do { - IMAPAddress a = new IMAPAddress(r); - // if we see an end-of-group address at the top, ignore it - if (!a.isEndOfGroup()) - v.addElement(a); - } while (r.peekByte() != ')'); - - // skip the terminating ')' at the end of the addresslist - r.skip(1); - - InternetAddress[] a = new InternetAddress[v.size()]; - v.copyInto(a); - return a; - } else if (b == 'N' || b == 'n') { // NIL - r.skip(2); // skip 'NIL' - return null; - } else - throw new ParsingException("ADDRESS parse error"); - } -} - -class IMAPAddress extends InternetAddress { - private boolean group = false; - private InternetAddress[] grouplist; - private String groupname; - - private static final long serialVersionUID = -3835822029483122232L; - - IMAPAddress(Response r) throws ParsingException { - r.skipSpaces(); // skip leading spaces - - if (r.readByte() != '(') - throw new ParsingException("ADDRESS parse error"); - - encodedPersonal = r.readString(); - - r.readString(); // throw away address_list - String mb = r.readString(); - String host = r.readString(); - // skip bogus spaces inserted by Yahoo IMAP server if - // "undisclosed-recipients" is a recipient - r.skipSpaces(); - if (r.readByte() != ')') // skip past terminating ')' - throw new ParsingException("ADDRESS parse error"); - - if (host == null) { - // it's a group list, start or end - group = true; - groupname = mb; - if (groupname == null) // end of group list - return; - // Accumulate a group list. The members of the group - // are accumulated in a Vector and the corresponding string - // representation of the group is accumulated in a StringBuffer. - StringBuffer sb = new StringBuffer(); - sb.append(groupname).append(':'); - Vector v = new Vector(); - while (r.peekByte() != ')') { - IMAPAddress a = new IMAPAddress(r); - if (a.isEndOfGroup()) // reached end of group - break; - if (v.size() != 0) // if not first element, need a comma - sb.append(','); - sb.append(a.toString()); - v.addElement(a); - } - sb.append(';'); - address = sb.toString(); - grouplist = new IMAPAddress[v.size()]; - v.copyInto(grouplist); - } else { - if (mb == null || mb.length() == 0) - address = host; - else if (host.length() == 0) - address = mb; - else - address = mb + "@" + host; - } - - } - - boolean isEndOfGroup() { - return group && groupname == null; - } - - public boolean isGroup() { - return group; - } - - public InternetAddress[] getGroup(boolean strict) throws AddressException { - if (grouplist == null) - return null; - return (InternetAddress[])grouplist.clone(); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/FLAGS.java b/src/main/java/com/sun/mail/imap/protocol/FLAGS.java deleted file mode 100644 index b8be7810..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/FLAGS.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import javax.mail.Flags; -import com.sun.mail.iap.*; - -/** - * This class - * - * @author John Mani - */ - -public class FLAGS extends Flags implements Item { - - // IMAP item name - static final char[] name = {'F','L','A','G','S'}; - public int msgno; - - private static final long serialVersionUID = 439049847053756670L; - - /** - * Constructor - */ - public FLAGS(IMAPResponse r) throws ParsingException { - msgno = r.getNumber(); - - r.skipSpaces(); - String[] flags = r.readSimpleList(); - if (flags != null) { // if not empty flaglist - for (int i = 0; i < flags.length; i++) { - String s = flags[i]; - if (s.length() >= 2 && s.charAt(0) == '\\') { - switch (Character.toUpperCase(s.charAt(1))) { - case 'S': // \Seen - add(Flags.Flag.SEEN); - break; - case 'R': // \Recent - add(Flags.Flag.RECENT); - break; - case 'D': - if (s.length() >= 3) { - char c = s.charAt(2); - if (c == 'e' || c == 'E') // \Deleted - add(Flags.Flag.DELETED); - else if (c == 'r' || c == 'R') // \Draft - add(Flags.Flag.DRAFT); - } else - add(s); // unknown, treat it as a user flag - break; - case 'A': // \Answered - add(Flags.Flag.ANSWERED); - break; - case 'F': // \Flagged - add(Flags.Flag.FLAGGED); - break; - case '*': // \* - add(Flags.Flag.USER); - break; - default: - add(s); // unknown, treat it as a user flag - break; - } - } else - add(s); - } - } - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/FetchItem.java b/src/main/java/com/sun/mail/imap/protocol/FetchItem.java deleted file mode 100644 index a1dd0da0..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/FetchItem.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.lang.reflect.*; - -import javax.mail.FetchProfile; -import com.sun.mail.iap.ParsingException; - -/** - * Metadata describing a FETCH item. - * Note that the "name" field MUST be in uppercase.

    - * - * @author Bill Shannon - * @since JavaMail 1.4.6 - */ - -public abstract class FetchItem { - private String name; - private FetchProfile.Item fetchProfileItem; - - public FetchItem(String name, FetchProfile.Item fetchProfileItem) { - this.name = name; - this.fetchProfileItem = fetchProfileItem; - } - - public String getName() { - return name; - } - - public FetchProfile.Item getFetchProfileItem() { - return fetchProfileItem; - } - - /** - * Parse the item into some kind of object appropriate for the item. - * Note that the item name will have been parsed and skipped already. - */ - public abstract Object parseItem(FetchResponse r) throws ParsingException; -} diff --git a/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java b/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java deleted file mode 100644 index a87d822d..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.*; -import java.util.*; -import com.sun.mail.util.*; -import com.sun.mail.iap.*; - -/** - * This class represents a FETCH response obtained from the input stream - * of an IMAP server. - * - * @author John Mani - * @author Bill Shannon - */ - -public class FetchResponse extends IMAPResponse { - /* - * Regular Items are saved in the items array. - * Extension items (items handled by subclasses - * that extend the IMAP provider) are saved in the - * extensionItems map, indexed by the FETCH item name. - * The map is only created when needed. - * - * XXX - Should consider unifying the handling of - * regular items and extension items. - */ - private Item[] items; - private Map extensionItems; - private final FetchItem[] fitems; - - public FetchResponse(Protocol p) - throws IOException, ProtocolException { - super(p); - fitems = null; - parse(); - } - - public FetchResponse(IMAPResponse r) - throws IOException, ProtocolException { - this(r, null); - } - - /** - * Construct a FetchResponse that handles the additional FetchItems. - * - * @since JavaMail 1.4.6 - */ - public FetchResponse(IMAPResponse r, FetchItem[] fitems) - throws IOException, ProtocolException { - super(r); - this.fitems = fitems; - parse(); - } - - public int getItemCount() { - return items.length; - } - - public Item getItem(int index) { - return items[index]; - } - - public Item getItem(Class c) { - for (int i = 0; i < items.length; i++) { - if (c.isInstance(items[i])) - return items[i]; - } - - return null; - } - - public static Item getItem(Response[] r, int msgno, Class c) { - if (r == null) - return null; - - for (int i = 0; i < r.length; i++) { - - if (r[i] == null || - !(r[i] instanceof FetchResponse) || - ((FetchResponse)r[i]).getNumber() != msgno) - continue; - - FetchResponse f = (FetchResponse)r[i]; - for (int j = 0; j < f.items.length; j++) { - if (c.isInstance(f.items[j])) - return f.items[j]; - } - } - - return null; - } - - /** - * Return a map of the extension items found in this fetch response. - * The map is indexed by extension item name. Callers should not - * modify the map. - * - * @since JavaMail 1.4.6 - */ - public Map getExtensionItems() { - if (extensionItems == null) - extensionItems = new HashMap(); - return extensionItems; - } - - private final static char[] HEADER = {'.','H','E','A','D','E','R'}; - private final static char[] TEXT = {'.','T','E','X','T'}; - - private void parse() throws ParsingException { - skipSpaces(); - if (buffer[index] != '(') - throw new ParsingException( - "error in FETCH parsing, missing '(' at index " + index); - - Vector v = new Vector(); - Item i = null; - do { - index++; // skip '(', or SPACE - - if (index >= size) - throw new ParsingException( - "error in FETCH parsing, ran off end of buffer, size " + size); - - i = parseItem(); - if (i != null) - v.addElement(i); - else if (!parseExtensionItem()) - throw new ParsingException( - "error in FETCH parsing, unrecognized item at index " + index); - } while (buffer[index] != ')'); - - index++; // skip ')' - items = new Item[v.size()]; - v.copyInto(items); - } - - /** - * Parse the item at the current position in the buffer, - * skipping over the item if successful. Otherwise, return null - * and leave the buffer position unmodified. - */ - private Item parseItem() throws ParsingException { - switch (buffer[index]) { - case 'E': case 'e': - if (match(ENVELOPE.name)) - return new ENVELOPE(this); - break; - case 'F': case 'f': - if (match(FLAGS.name)) - return new FLAGS((IMAPResponse)this); - break; - case 'I': case 'i': - if (match(INTERNALDATE.name)) - return new INTERNALDATE(this); - break; - case 'B': case 'b': - if (match(BODYSTRUCTURE.name)) - return new BODYSTRUCTURE(this); - else if (match(BODY.name)) { - if (buffer[index] == '[') - return new BODY(this); - else - return new BODYSTRUCTURE(this); - } - break; - case 'R': case 'r': - if (match(RFC822SIZE.name)) - return new RFC822SIZE(this); - else if (match(RFC822DATA.name)) { - if (match(HEADER)) - ; // skip ".HEADER" - else if (match(TEXT)) - ; // skip ".TEXT" - return new RFC822DATA(this); - } - break; - case 'U': case 'u': - if (match(UID.name)) - return new UID(this); - break; - default: - break; - } - return null; - } - - /** - * If this item is a known extension item, parse it. - */ - private boolean parseExtensionItem() throws ParsingException { - if (fitems == null) - return false; - for (int i = 0; i < fitems.length; i++) { - if (match(fitems[i].getName())) { - getExtensionItems().put(fitems[i].getName(), - fitems[i].parseItem(this)); - return true; - } - } - return false; - } - - /** - * Does the current buffer match the given item name? - * itemName is the name of the IMAP item to compare against. - * NOTE that itemName *must* be all uppercase. - * If the match is successful, the buffer pointer (index) - * is incremented past the matched item. - */ - private boolean match(char[] itemName) { - int len = itemName.length; - for (int i = 0, j = index; i < len;) - // IMAP tokens are case-insensitive. We store itemNames in - // uppercase, so convert operand to uppercase before comparing. - if (Character.toUpperCase((char)buffer[j++]) != itemName[i++]) - return false; - index += len; - return true; - } - - /** - * Does the current buffer match the given item name? - * itemName is the name of the IMAP item to compare against. - * NOTE that itemName *must* be all uppercase. - * If the match is successful, the buffer pointer (index) - * is incremented past the matched item. - */ - private boolean match(String itemName) { - int len = itemName.length(); - for (int i = 0, j = index; i < len;) - // IMAP tokens are case-insensitive. We store itemNames in - // uppercase, so convert operand to uppercase before comparing. - if (Character.toUpperCase((char)buffer[j++]) != - itemName.charAt(i++)) - return false; - index += len; - return true; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java b/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java deleted file mode 100644 index 544596b8..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java +++ /dev/null @@ -1,2467 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.*; -import java.util.*; -import java.text.*; -import java.lang.reflect.*; -import java.util.logging.Level; - -import javax.mail.*; -import javax.mail.internet.*; -import javax.mail.search.*; - -import com.sun.mail.util.*; -import com.sun.mail.iap.*; -import com.sun.mail.auth.Ntlm; - -import com.sun.mail.imap.ACL; -import com.sun.mail.imap.Rights; -import com.sun.mail.imap.AppendUID; -import com.sun.mail.imap.SortTerm; - -/** - * This class extends the iap.Protocol object and implements IMAP - * semantics. In general, there is a method corresponding to each - * IMAP protocol command. The typical implementation issues the - * appropriate protocol command, collects all responses, processes - * those responses that are specific to this command and then - * dispatches the rest (the unsolicited ones) to the dispatcher - * using the notifyResponseHandlers(r). - * - * @author John Mani - * @author Bill Shannon - */ - -public class IMAPProtocol extends Protocol { - - private boolean connected = false; // did constructor succeed? - private boolean rev1 = false; // REV1 server ? - private boolean noauthdebug = true; // hide auth info in debug output - private boolean authenticated; // authenticated? - // WARNING: authenticated may be set to true in superclass - // constructor, don't initialize it here. - - private Map capabilities; - // WARNING: capabilities may be initialized as a result of superclass - // constructor, don't initialize it here. - private List authmechs; - // WARNING: authmechs may be initialized as a result of superclass - // constructor, don't initialize it here. - - protected SearchSequence searchSequence; - protected String[] searchCharsets; // array of search charsets - - private String name; - private SaslAuthenticator saslAuthenticator; // if SASL is being used - - private ByteArray ba; // a buffer for fetchBody - - private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; - - private static final FetchItem[] fetchItems = { }; - - /** - * Constructor. - * Opens a connection to the given host at given port. - * - * @param host host to connect to - * @param port portnumber to connect to - * @param debug debug mode - * @param props Properties object used by this protocol - */ - public IMAPProtocol(String name, String host, int port, - Properties props, boolean isSSL, MailLogger logger) - throws IOException, ProtocolException { - super(host, port, props, "mail." + name, isSSL, logger); - - try { - this.name = name; - noauthdebug = - !PropUtil.getBooleanProperty(props, "mail.debug.auth", false); - - if (capabilities == null) - capability(); - - if (hasCapability("IMAP4rev1")) - rev1 = true; - - searchCharsets = new String[2]; // 2, for now. - searchCharsets[0] = "UTF-8"; - searchCharsets[1] = MimeUtility.mimeCharset( - MimeUtility.getDefaultJavaCharset() - ); - - connected = true; // must be last statement in constructor - } finally { - /* - * If we get here because an exception was thrown, we need - * to disconnect to avoid leaving a connected socket that - * no one will be able to use because this object was never - * completely constructed. - */ - if (!connected) - disconnect(); - } - } - - /** - * Return an array of FetchItem objects describing the - * FETCH items supported by this protocol. Subclasses may - * override this method to combine their FetchItems with - * the FetchItems returned by the superclass. - * - * @since JavaMail 1.4.6 - */ - public FetchItem[] getFetchItems() { - return fetchItems; - } - - /** - * CAPABILITY command. - * - * @see "RFC2060, section 6.1.1" - */ - public void capability() throws ProtocolException { - // Check CAPABILITY - Response[] r = command("CAPABILITY", null); - - if (!r[r.length-1].isOK()) - throw new ProtocolException(r[r.length-1].toString()); - - capabilities = new HashMap(10); - authmechs = new ArrayList(5); - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - - // Handle *all* untagged CAPABILITY responses. - // Though the spec seemingly states that only - // one CAPABILITY response string is allowed (6.1.1), - // some server vendors claim otherwise. - if (ir.keyEquals("CAPABILITY")) - parseCapabilities(ir); - } - } - - /** - * If the response contains a CAPABILITY response code, extract - * it and save the capabilities. - */ - protected void setCapabilities(Response r) { - byte b; - while ((b = r.readByte()) > 0 && b != (byte)'[') - ; - if (b == 0) - return; - String s; - s = r.readAtom(); - if (!s.equalsIgnoreCase("CAPABILITY")) - return; - capabilities = new HashMap(10); - authmechs = new ArrayList(5); - parseCapabilities(r); - } - - /** - * Parse the capabilities from a CAPABILITY response or from - * a CAPABILITY response code attached to (e.g.) an OK response. - */ - protected void parseCapabilities(Response r) { - String s; - while ((s = r.readAtom(']')) != null) { - if (s.length() == 0) { - if (r.peekByte() == (byte)']') - break; - /* - * Probably found something here that's not an atom. - * Rather than loop forever or fail completely, we'll - * try to skip this bogus capability. This is known - * to happen with: - * Netscape Messaging Server 4.03 (built Apr 27 1999) - * that returns: - * * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ... - * The "*" in the middle of the capability list causes - * us to loop forever here. - */ - r.skipToken(); - } else { - capabilities.put(s.toUpperCase(Locale.ENGLISH), s); - if (s.regionMatches(true, 0, "AUTH=", 0, 5)) { - authmechs.add(s.substring(5)); - if (logger.isLoggable(Level.FINE)) - logger.fine("AUTH: " + s.substring(5)); - } - } - } - } - - /** - * Check the greeting when first connecting; look for PREAUTH response. - */ - protected void processGreeting(Response r) throws ProtocolException { - super.processGreeting(r); // check if it's BAD - if (r.isOK()) { // check if it's OK - setCapabilities(r); - return; - } - // only other choice is PREAUTH - IMAPResponse ir = (IMAPResponse)r; - if (ir.keyEquals("PREAUTH")) { - authenticated = true; - setCapabilities(r); - } else - throw new ConnectionException(this, r); - } - - /** - * Returns true if the connection has been authenticated, - * either due to a successful login, or due to a PREAUTH greeting response. - */ - public boolean isAuthenticated() { - return authenticated; - } - - /** - * Returns true if this is a IMAP4rev1 server - */ - public boolean isREV1() { - return rev1; - } - - /** - * Returns whether this Protocol supports non-synchronizing literals. - */ - protected boolean supportsNonSyncLiterals() { - return hasCapability("LITERAL+"); - } - - /** - * Read a response from the server. - */ - public Response readResponse() throws IOException, ProtocolException { - // assert Thread.holdsLock(this); - // can't assert because it's called from constructor - IMAPResponse r = new IMAPResponse(this); - if (r.keyEquals("FETCH")) - r = new FetchResponse(r, getFetchItems()); - return r; - } - - /** - * Check whether the given capability is supported by - * this server. Returns true if so, otherwise - * returns false. - */ - public boolean hasCapability(String c) { - if (c.endsWith("*")) { - c = c.substring(0, c.length() - 1).toUpperCase(Locale.ENGLISH); - Iterator it = capabilities.keySet().iterator(); - while (it.hasNext()) { - if (((String)it.next()).startsWith(c)) - return true; - } - return false; - } - return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); - } - - /** - * Return the map of capabilities returned by the server. - * - * @since JavaMail 1.4.1 - */ - public Map getCapabilities() { - return capabilities; - } - - /** - * Close socket connection. - * - * This method just makes the Protocol.disconnect() method - * public. - */ - public void disconnect() { - super.disconnect(); - authenticated = false; // just in case - } - - /** - * The NOOP command. - * - * @see "RFC2060, section 6.1.2" - */ - public void noop() throws ProtocolException { - logger.fine("IMAPProtocol noop"); - simpleCommand("NOOP", null); - } - - /** - * LOGOUT Command. - * - * @see "RFC2060, section 6.1.3" - */ - public void logout() throws ProtocolException { - try { - Response[] r = command("LOGOUT", null); - - authenticated = false; - // dispatch any unsolicited responses. - // NOTE that the BYE response is dispatched here as well - notifyResponseHandlers(r); - } finally { - disconnect(); - } - } - - /** - * LOGIN Command. - * - * @see "RFC2060, section 6.2.2" - */ - public void login(String u, String p) throws ProtocolException { - Argument args = new Argument(); - args.writeString(u); - args.writeString(p); - - Response[] r = null; - try { - if (noauthdebug && isTracing()) { - logger.fine("LOGIN command trace suppressed"); - suspendTracing(); - } - r = command("LOGIN", args); - } finally { - resumeTracing(); - } - - // dispatch untagged responses - notifyResponseHandlers(r); - - // Handle result of this command - if (noauthdebug && isTracing()) - logger.fine("LOGIN command result: " + r[r.length-1]); - handleResult(r[r.length-1]); - // If the response includes a CAPABILITY response code, process it - setCapabilities(r[r.length-1]); - // if we get this far without an exception, we're authenticated - authenticated = true; - } - - /** - * The AUTHENTICATE command with AUTH=LOGIN authenticate scheme - * - * @see "RFC2060, section 6.2.1" - */ - public synchronized void authlogin(String u, String p) - throws ProtocolException { - Vector v = new Vector(); - String tag = null; - Response r = null; - boolean done = false; - - try { - - if (noauthdebug && isTracing()) { - logger.fine("AUTHENTICATE LOGIN command trace suppressed"); - suspendTracing(); - } - - try { - tag = writeCommand("AUTHENTICATE LOGIN", null); - } catch (Exception ex) { - // Convert this into a BYE response - r = Response.byeResponse(ex); - done = true; - } - - OutputStream os = getOutputStream(); // stream to IMAP server - - /* Wrap a BASE64Encoder around a ByteArrayOutputstream - * to craft b64 encoded username and password strings - * - * Note that the encoded bytes should be sent "as-is" to the - * server, *not* as literals or quoted-strings. - * - * Also note that unlike the B64 definition in MIME, CRLFs - * should *not* be inserted during the encoding process. So, I - * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, - * which should be sufficiently large ! - * - * Finally, format the line in a buffer so it can be sent as - * a single packet, to avoid triggering a bug in SUN's SIMS 2.0 - * server caused by patch 105346. - */ - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); - boolean first = true; - - while (!done) { // loop till we are done - try { - r = readResponse(); - if (r.isContinuation()) { - // Server challenge .. - String s; - if (first) { // Send encoded username - s = u; - first = false; - } else // Send encoded password - s = p; - - // obtain b64 encoded bytes - b64os.write(ASCIIUtility.getBytes(s)); - b64os.flush(); // complete the encoding - - bos.write(CRLF); // CRLF termination - os.write(bos.toByteArray()); // write out line - os.flush(); // flush the stream - bos.reset(); // reset buffer - } else if (r.isTagged() && r.getTag().equals(tag)) - // Ah, our tagged response - done = true; - else if (r.isBYE()) // outta here - done = true; - else // hmm .. unsolicited response here ?! - v.addElement(r); - } catch (Exception ioex) { - // convert this into a BYE response - r = Response.byeResponse(ioex); - done = true; - } - } - - } finally { - resumeTracing(); - } - - /* Dispatch untagged responses. - * NOTE: in our current upper level IMAP classes, we add the - * responseHandler to the Protocol object only *after* the - * connection has been authenticated. So, for now, the below - * code really ends up being just a no-op. - */ - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - notifyResponseHandlers(responses); - - // Handle the final OK, NO, BAD or BYE response - if (noauthdebug && isTracing()) - logger.fine("AUTHENTICATE LOGIN command result: " + r); - handleResult(r); - // If the response includes a CAPABILITY response code, process it - setCapabilities(r); - // if we get this far without an exception, we're authenticated - authenticated = true; - } - - - /** - * The AUTHENTICATE command with AUTH=PLAIN authentication scheme. - * This is based heavly on the {@link #authlogin} method. - * - * @param authzid the authorization id - * @param u the username - * @param p the password - * @throws ProtocolException as thrown by {@link Protocol#handleResult}. - * @see "RFC3501, section 6.2.2" - * @see "RFC2595, section 6" - * @since JavaMail 1.3.2 - */ - public synchronized void authplain(String authzid, String u, String p) - throws ProtocolException { - Vector v = new Vector(); - String tag = null; - Response r = null; - boolean done = false; - - try { - - if (noauthdebug && isTracing()) { - logger.fine("AUTHENTICATE PLAIN command trace suppressed"); - suspendTracing(); - } - - try { - tag = writeCommand("AUTHENTICATE PLAIN", null); - } catch (Exception ex) { - // Convert this into a BYE response - r = Response.byeResponse(ex); - done = true; - } - - OutputStream os = getOutputStream(); // stream to IMAP server - - /* Wrap a BASE64Encoder around a ByteArrayOutputstream - * to craft b64 encoded username and password strings - * - * Note that the encoded bytes should be sent "as-is" to the - * server, *not* as literals or quoted-strings. - * - * Also note that unlike the B64 definition in MIME, CRLFs - * should *not* be inserted during the encoding process. So, I - * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, - * which should be sufficiently large ! - * - * Finally, format the line in a buffer so it can be sent as - * a single packet, to avoid triggering a bug in SUN's SIMS 2.0 - * server caused by patch 105346. - */ - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); - - while (!done) { // loop till we are done - try { - r = readResponse(); - if (r.isContinuation()) { - // Server challenge .. - final String nullByte = "\0"; - String s = (authzid == null ? "" : authzid) + - nullByte + u + nullByte + p; - - // obtain b64 encoded bytes - b64os.write(ASCIIUtility.getBytes(s)); - b64os.flush(); // complete the encoding - - bos.write(CRLF); // CRLF termination - os.write(bos.toByteArray()); // write out line - os.flush(); // flush the stream - bos.reset(); // reset buffer - } else if (r.isTagged() && r.getTag().equals(tag)) - // Ah, our tagged response - done = true; - else if (r.isBYE()) // outta here - done = true; - else // hmm .. unsolicited response here ?! - v.addElement(r); - } catch (Exception ioex) { - // convert this into a BYE response - r = Response.byeResponse(ioex); - done = true; - } - } - - } finally { - resumeTracing(); - } - - /* Dispatch untagged responses. - * NOTE: in our current upper level IMAP classes, we add the - * responseHandler to the Protocol object only *after* the - * connection has been authenticated. So, for now, the below - * code really ends up being just a no-op. - */ - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - notifyResponseHandlers(responses); - - // Handle the final OK, NO, BAD or BYE response - if (noauthdebug && isTracing()) - logger.fine("AUTHENTICATE PLAIN command result: " + r); - handleResult(r); - // If the response includes a CAPABILITY response code, process it - setCapabilities(r); - // if we get this far without an exception, we're authenticated - authenticated = true; - } - - /** - * The AUTHENTICATE command with AUTH=NTLM authentication scheme. - * This is based heavly on the {@link #authlogin} method. - * - * @param authzid the authorization id - * @param u the username - * @param p the password - * @throws ProtocolException as thrown by {@link Protocol#handleResult}. - * @see "RFC3501, section 6.2.2" - * @see "RFC2595, section 6" - * @since JavaMail 1.4.3 - */ - public synchronized void authntlm(String authzid, String u, String p) - throws ProtocolException { - Vector v = new Vector(); - String tag = null; - Response r = null; - boolean done = false; - - String type1Msg = null; - int flags = PropUtil.getIntProperty(props, - "mail." + name + ".auth.ntlm.flags", 0); - String domain = props.getProperty( - "mail." + name + ".auth.ntlm.domain", ""); - Ntlm ntlm = new Ntlm(domain, getLocalHost(), u, p, logger); - - try { - - if (noauthdebug && isTracing()) { - logger.fine("AUTHENTICATE NTLM command trace suppressed"); - suspendTracing(); - } - - try { - tag = writeCommand("AUTHENTICATE NTLM", null); - } catch (Exception ex) { - // Convert this into a BYE response - r = Response.byeResponse(ex); - done = true; - } - - OutputStream os = getOutputStream(); // stream to IMAP server - boolean first = true; - - while (!done) { // loop till we are done - try { - r = readResponse(); - if (r.isContinuation()) { - // Server challenge .. - String s; - if (first) { - s = ntlm.generateType1Msg(flags); - first = false; - } else { - s = ntlm.generateType3Msg(r.getRest()); - } - - os.write(ASCIIUtility.getBytes(s)); - os.write(CRLF); // CRLF termination - os.flush(); // flush the stream - } else if (r.isTagged() && r.getTag().equals(tag)) - // Ah, our tagged response - done = true; - else if (r.isBYE()) // outta here - done = true; - else // hmm .. unsolicited response here ?! - v.addElement(r); - } catch (Exception ioex) { - // convert this into a BYE response - r = Response.byeResponse(ioex); - done = true; - } - } - - } finally { - resumeTracing(); - } - - /* - * Dispatch untagged responses. - * NOTE: in our current upper level IMAP classes, we add the - * responseHandler to the Protocol object only *after* the - * connection has been authenticated. So, for now, the below - * code really ends up being just a no-op. - */ - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - notifyResponseHandlers(responses); - - // Handle the final OK, NO, BAD or BYE response - if (noauthdebug && isTracing()) - logger.fine("AUTHENTICATE NTLM command result: " + r); - handleResult(r); - // If the response includes a CAPABILITY response code, process it - setCapabilities(r); - // if we get this far without an exception, we're authenticated - authenticated = true; - } - - /** - * SASL-based login. - */ - public void sasllogin(String[] allowed, String realm, String authzid, - String u, String p) throws ProtocolException { - if (saslAuthenticator == null) { - try { - Class sac = Class.forName( - "com.sun.mail.imap.protocol.IMAPSaslAuthenticator"); - Constructor c = sac.getConstructor(new Class[] { - IMAPProtocol.class, - String.class, - Properties.class, - MailLogger.class, - String.class - }); - saslAuthenticator = (SaslAuthenticator)c.newInstance( - new Object[] { - this, - name, - props, - logger, - host - }); - } catch (Exception ex) { - logger.log(Level.FINE, "Can't load SASL authenticator", ex); - // probably because we're running on a system without SASL - return; // not authenticated, try without SASL - } - } - - // were any allowed mechanisms specified? - List v; - if (allowed != null && allowed.length > 0) { - // remove anything not supported by the server - v = new ArrayList(allowed.length); - for (int i = 0; i < allowed.length; i++) - if (authmechs.contains(allowed[i])) // XXX - case must match - v.add(allowed[i]); - } else { - // everything is allowed - v = authmechs; - } - String[] mechs = (String[])v.toArray(new String[v.size()]); - - try { - - if (noauthdebug && isTracing()) { - logger.fine("SASL authentication command trace suppressed"); - suspendTracing(); - } - - if (saslAuthenticator.authenticate(mechs, realm, authzid, u, p)) { - if (noauthdebug && isTracing()) - logger.fine("SASL authentication succeeded"); - authenticated = true; - } else { - if (noauthdebug && isTracing()) - logger.fine("SASL authentication failed"); - } - } finally { - resumeTracing(); - } - } - - // XXX - for IMAPSaslAuthenticator access to protected method - OutputStream getIMAPOutputStream() { - return getOutputStream(); - } - - /** - * PROXYAUTH Command. - * - * @see "Netscape/iPlanet/SunONE Messaging Server extension" - */ - public void proxyauth(String u) throws ProtocolException { - Argument args = new Argument(); - args.writeString(u); - - simpleCommand("PROXYAUTH", args); - } - - /** - * ID Command, for Yahoo! Mail IMAP server. - * - * See - * http://en.wikipedia.org/wiki/Yahoo%21_Mail#Free_IMAP_and_SMTPs_access - * - * @since JavaMail 1.4.4 - */ - public void id(String guid) throws ProtocolException { - /* - * XXX - need to be able to write a string instead - * of an astring for the following to work. - Argument garg = new Argument(); - garg.writeString("GUID"); - garg.writeString(guid); - Argument args = new Argument(); - args.writeArgument(garg); - simpleCommand("ID", args); - */ - simpleCommand("ID (\"GUID\" \"" + guid + "\")", null); - } - - /** - * STARTTLS Command. - * - * @see "RFC3501, section 6.2.1" - */ - public void startTLS() throws ProtocolException { - try { - super.startTLS("STARTTLS"); - } catch (ProtocolException pex) { - logger.log(Level.FINE, "STARTTLS ProtocolException", pex); - // ProtocolException just means the command wasn't recognized, - // or failed. This should never happen if we check the - // CAPABILITY first. - throw pex; - } catch (Exception ex) { - logger.log(Level.FINE, "STARTTLS Exception", ex); - // any other exception means we have to shut down the connection - // generate an artificial BYE response and disconnect - Response[] r = { Response.byeResponse(ex) }; - notifyResponseHandlers(r); - disconnect(); - throw new ProtocolException("STARTTLS failure", ex); - } - } - - /** - * SELECT Command. - * - * @see "RFC2060, section 6.3.1" - */ - public MailboxInfo select(String mbox) throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Response[] r = command("SELECT", args); - - // Note that MailboxInfo also removes those responses - // it knows about - MailboxInfo minfo = new MailboxInfo(r); - - // dispatch any remaining untagged responses - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - - if (response.isOK()) { // command succesful - if (response.toString().indexOf("READ-ONLY") != -1) - minfo.mode = Folder.READ_ONLY; - else - minfo.mode = Folder.READ_WRITE; - } - - handleResult(response); - return minfo; - } - - /** - * EXAMINE Command. - * - * @see "RFC2060, section 6.3.2" - */ - public MailboxInfo examine(String mbox) throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Response[] r = command("EXAMINE", args); - - // Note that MailboxInfo also removes those responses - // it knows about - MailboxInfo minfo = new MailboxInfo(r); - minfo.mode = Folder.READ_ONLY; // Obviously - - // dispatch any remaining untagged responses - notifyResponseHandlers(r); - - handleResult(r[r.length-1]); - return minfo; - } - - /** - * UNSELECT Command. - * - * @see "RFC 3691" - * @since JavaMail 1.4.4 - */ - public void unselect() throws ProtocolException { - if (!hasCapability("UNSELECT")) - throw new BadCommandException("UNSELECT not supported"); - simpleCommand("UNSELECT", null); - } - - /** - * STATUS Command. - * - * @see "RFC2060, section 6.3.10" - */ - public Status status(String mbox, String[] items) - throws ProtocolException { - if (!isREV1() && !hasCapability("IMAP4SUNVERSION")) - // STATUS is rev1 only, however the non-rev1 SIMS2.0 - // does support this. - throw new BadCommandException("STATUS not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Argument itemArgs = new Argument(); - if (items == null) - items = Status.standardItems; - - for (int i = 0, len = items.length; i < len; i++) - itemArgs.writeAtom(items[i]); - args.writeArgument(itemArgs); - - Response[] r = command("STATUS", args); - - Status status = null; - Response response = r[r.length-1]; - - // Grab all STATUS responses - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("STATUS")) { - if (status == null) - status = new Status(ir); - else // collect 'em all - Status.add(status, new Status(ir)); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return status; - } - - /** - * CREATE Command. - * - * @see "RFC2060, section 6.3.3" - */ - public void create(String mbox) throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - simpleCommand("CREATE", args); - } - - /** - * DELETE Command. - * - * @see "RFC2060, section 6.3.4" - */ - public void delete(String mbox) throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - simpleCommand("DELETE", args); - } - - /** - * RENAME Command. - * - * @see "RFC2060, section 6.3.5" - */ - public void rename(String o, String n) throws ProtocolException { - // encode the mbox as per RFC2060 - o = BASE64MailboxEncoder.encode(o); - n = BASE64MailboxEncoder.encode(n); - - Argument args = new Argument(); - args.writeString(o); - args.writeString(n); - - simpleCommand("RENAME", args); - } - - /** - * SUBSCRIBE Command. - * - * @see "RFC2060, section 6.3.6" - */ - public void subscribe(String mbox) throws ProtocolException { - Argument args = new Argument(); - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - args.writeString(mbox); - - simpleCommand("SUBSCRIBE", args); - } - - /** - * UNSUBSCRIBE Command. - * - * @see "RFC2060, section 6.3.7" - */ - public void unsubscribe(String mbox) throws ProtocolException { - Argument args = new Argument(); - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - args.writeString(mbox); - - simpleCommand("UNSUBSCRIBE", args); - } - - /** - * LIST Command. - * - * @see "RFC2060, section 6.3.8" - */ - public ListInfo[] list(String ref, String pattern) - throws ProtocolException { - return doList("LIST", ref, pattern); - } - - /** - * LSUB Command. - * - * @see "RFC2060, section 6.3.9" - */ - public ListInfo[] lsub(String ref, String pattern) - throws ProtocolException { - return doList("LSUB", ref, pattern); - } - - /** - * Execute the specified LIST-like command (e.g., "LIST" or "LSUB"), - * using the reference and pattern. - * - * @since JavaMail 1.4.6 - */ - protected ListInfo[] doList(String cmd, String ref, String pat) - throws ProtocolException { - // encode the mbox as per RFC2060 - ref = BASE64MailboxEncoder.encode(ref); - pat = BASE64MailboxEncoder.encode(pat); - - Argument args = new Argument(); - args.writeString(ref); - args.writeString(pat); - - Response[] r = command(cmd, args); - - ListInfo[] linfo = null; - Response response = r[r.length-1]; - - if (response.isOK()) { // command succesful - Vector v = new Vector(1); - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals(cmd)) { - v.addElement(new ListInfo(ir)); - r[i] = null; - } - } - if (v.size() > 0) { - linfo = new ListInfo[v.size()]; - v.copyInto(linfo); - } - } - - // Dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return linfo; - } - - /** - * APPEND Command. - * - * @see "RFC2060, section 6.3.11" - */ - public void append(String mbox, Flags f, Date d, - Literal data) throws ProtocolException { - appenduid(mbox, f, d, data, false); // ignore return value - } - - /** - * APPEND Command, return uid from APPENDUID response code. - * - * @see "RFC2060, section 6.3.11" - */ - public AppendUID appenduid(String mbox, Flags f, Date d, - Literal data) throws ProtocolException { - return appenduid(mbox, f, d, data, true); - } - - public AppendUID appenduid(String mbox, Flags f, Date d, - Literal data, boolean uid) throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - if (f != null) { // set Flags in appended message - // can't set the \Recent flag in APPEND - if (f.contains(Flags.Flag.RECENT)) { - f = new Flags(f); // copy, don't modify orig - f.remove(Flags.Flag.RECENT); // remove RECENT from copy - } - - /* - * HACK ALERT: We want the flag_list to be written out - * without any checking/processing of the bytes in it. If - * I use writeString(), the flag_list will end up being - * quoted since it contains "illegal" characters. So I - * am depending on implementation knowledge that writeAtom() - * does not do any checking/processing - it just writes out - * the bytes. What we really need is a writeFoo() that just - * dumps out its argument. - */ - args.writeAtom(createFlagList(f)); - } - if (d != null) // set INTERNALDATE in appended message - args.writeString(INTERNALDATE.format(d)); - - args.writeBytes(data); - - Response[] r = command("APPEND", args); - - // dispatch untagged responses - notifyResponseHandlers(r); - - // Handle result of this command - handleResult(r[r.length-1]); - - if (uid) - return getAppendUID(r[r.length-1]); - else - return null; - } - - /** - * If the response contains an APPENDUID response code, extract - * it and return an AppendUID object with the information. - */ - private AppendUID getAppendUID(Response r) { - if (!r.isOK()) - return null; - byte b; - while ((b = r.readByte()) > 0 && b != (byte)'[') - ; - if (b == 0) - return null; - String s; - s = r.readAtom(); - if (!s.equalsIgnoreCase("APPENDUID")) - return null; - - long uidvalidity = r.readLong(); - long uid = r.readLong(); - return new AppendUID(uidvalidity, uid); - } - - /** - * CHECK Command. - * - * @see "RFC2060, section 6.4.1" - */ - public void check() throws ProtocolException { - simpleCommand("CHECK", null); - } - - /** - * CLOSE Command. - * - * @see "RFC2060, section 6.4.2" - */ - public void close() throws ProtocolException { - simpleCommand("CLOSE", null); - } - - /** - * EXPUNGE Command. - * - * @see "RFC2060, section 6.4.3" - */ - public void expunge() throws ProtocolException { - simpleCommand("EXPUNGE", null); - } - - /** - * UID EXPUNGE Command. - * - * @see "RFC2359, section 4.1" - */ - public void uidexpunge(UIDSet[] set) throws ProtocolException { - if (!hasCapability("UIDPLUS")) - throw new BadCommandException("UID EXPUNGE not supported"); - simpleCommand("UID EXPUNGE " + UIDSet.toString(set), null); - } - - /** - * Fetch the BODYSTRUCTURE of the specified message. - */ - public BODYSTRUCTURE fetchBodyStructure(int msgno) - throws ProtocolException { - Response[] r = fetch(msgno, "BODYSTRUCTURE"); - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - if (response.isOK()) - return (BODYSTRUCTURE)FetchResponse.getItem(r, msgno, - BODYSTRUCTURE.class); - else if (response.isNO()) - return null; - else { - handleResult(response); - return null; - } - } - - /** - * Fetch given BODY section, without marking the message - * as SEEN. - */ - public BODY peekBody(int msgno, String section) - throws ProtocolException { - return fetchBody(msgno, section, true); - } - - /** - * Fetch given BODY section. - */ - public BODY fetchBody(int msgno, String section) - throws ProtocolException { - return fetchBody(msgno, section, false); - } - - protected BODY fetchBody(int msgno, String section, boolean peek) - throws ProtocolException { - Response[] r; - - if (peek) - r = fetch(msgno, - "BODY.PEEK[" + (section == null ? "]" : section + "]")); - else - r = fetch(msgno, - "BODY[" + (section == null ? "]" : section + "]")); - - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - if (response.isOK()) - return (BODY)FetchResponse.getItem(r, msgno, BODY.class); - else if (response.isNO()) - return null; - else { - handleResult(response); - return null; - } - } - - /** - * Partial FETCH of given BODY section, without setting SEEN flag. - */ - public BODY peekBody(int msgno, String section, int start, int size) - throws ProtocolException { - return fetchBody(msgno, section, start, size, true, null); - } - - /** - * Partial FETCH of given BODY section. - */ - public BODY fetchBody(int msgno, String section, int start, int size) - throws ProtocolException { - return fetchBody(msgno, section, start, size, false, null); - } - - /** - * Partial FETCH of given BODY section, without setting SEEN flag. - */ - public BODY peekBody(int msgno, String section, int start, int size, - ByteArray ba) throws ProtocolException { - return fetchBody(msgno, section, start, size, true, ba); - } - - /** - * Partial FETCH of given BODY section. - */ - public BODY fetchBody(int msgno, String section, int start, int size, - ByteArray ba) throws ProtocolException { - return fetchBody(msgno, section, start, size, false, ba); - } - - protected BODY fetchBody(int msgno, String section, int start, int size, - boolean peek, ByteArray ba) throws ProtocolException { - this.ba = ba; // save for later use by getResponseBuffer - Response[] r = fetch( - msgno, (peek ? "BODY.PEEK[" : "BODY[" ) + - (section == null ? "]<" : (section +"]<")) + - String.valueOf(start) + "." + - String.valueOf(size) + ">" - ); - - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - if (response.isOK()) - return (BODY)FetchResponse.getItem(r, msgno, BODY.class); - else if (response.isNO()) - return null; - else { - handleResult(response); - return null; - } - } - - /** - * Return a buffer to read a response into. - * The buffer is provided by fetchBody and is - * used only once. - */ - protected ByteArray getResponseBuffer() { - ByteArray ret = ba; - ba = null; - return ret; - } - - /** - * Fetch the specified RFC822 Data item. 'what' names - * the item to be fetched. 'what' can be null - * to fetch the whole message. - */ - public RFC822DATA fetchRFC822(int msgno, String what) - throws ProtocolException { - Response[] r = fetch(msgno, - what == null ? "RFC822" : "RFC822." + what - ); - - // dispatch untagged responses - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - if (response.isOK()) - return (RFC822DATA)FetchResponse.getItem(r, msgno, - RFC822DATA.class); - else if (response.isNO()) - return null; - else { - handleResult(response); - return null; - } - } - - /** - * Fetch the FLAGS for the given message. - */ - public Flags fetchFlags(int msgno) throws ProtocolException { - Flags flags = null; - Response[] r = fetch(msgno, "FLAGS"); - - // Search for our FLAGS response - for (int i = 0, len = r.length; i < len; i++) { - if (r[i] == null || - !(r[i] instanceof FetchResponse) || - ((FetchResponse)r[i]).getNumber() != msgno) - continue; - - FetchResponse fr = (FetchResponse)r[i]; - if ((flags = (Flags)fr.getItem(Flags.class)) != null) { - r[i] = null; // remove this response - break; - } - } - - // dispatch untagged responses - notifyResponseHandlers(r); - handleResult(r[r.length-1]); - return flags; - } - - /** - * Fetch the IMAP UID for the given message. - */ - public UID fetchUID(int msgno) throws ProtocolException { - Response[] r = fetch(msgno, "UID"); - - // dispatch untagged responses - notifyResponseHandlers(r); - - Response response = r[r.length-1]; - if (response.isOK()) - return (UID)FetchResponse.getItem(r, msgno, UID.class); - else if (response.isNO()) // XXX: Issue NOOP ? - return null; - else { - handleResult(response); - return null; // NOTREACHED - } - } - - /** - * Get the sequence number for the given UID. A UID object - * containing the sequence number is returned. If the given UID - * is invalid, null is returned. - */ - public UID fetchSequenceNumber(long uid) throws ProtocolException { - UID u = null; - Response[] r = fetch(String.valueOf(uid), "UID", true); - - for (int i = 0, len = r.length; i < len; i++) { - if (r[i] == null || !(r[i] instanceof FetchResponse)) - continue; - - FetchResponse fr = (FetchResponse)r[i]; - if ((u = (UID)fr.getItem(UID.class)) != null) { - if (u.uid == uid) // this is the one we want - break; - else - u = null; - } - } - - notifyResponseHandlers(r); - handleResult(r[r.length-1]); - return u; - } - - /** - * Get the sequence numbers for UIDs ranging from start till end. - * UID objects that contain the sequence numbers are returned. - * If no UIDs in the given range are found, an empty array is returned. - */ - public UID[] fetchSequenceNumbers(long start, long end) - throws ProtocolException { - Response[] r = fetch(String.valueOf(start) + ":" + - (end == UIDFolder.LASTUID ? "*" : - String.valueOf(end)), - "UID", true); - - UID u; - Vector v = new Vector(); - for (int i = 0, len = r.length; i < len; i++) { - if (r[i] == null || !(r[i] instanceof FetchResponse)) - continue; - - FetchResponse fr = (FetchResponse)r[i]; - if ((u = (UID)fr.getItem(UID.class)) != null) - v.addElement(u); - } - - notifyResponseHandlers(r); - handleResult(r[r.length-1]); - - UID[] ua = new UID[v.size()]; - v.copyInto(ua); - return ua; - } - - /** - * Get the sequence numbers for UIDs ranging from start till end. - * UID objects that contain the sequence numbers are returned. - * If no UIDs in the given range are found, an empty array is returned. - */ - public UID[] fetchSequenceNumbers(long[] uids) throws ProtocolException { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < uids.length; i++) { - if (i > 0) - sb.append(","); - sb.append(String.valueOf(uids[i])); - } - - Response[] r = fetch(sb.toString(), "UID", true); - - UID u; - Vector v = new Vector(); - for (int i = 0, len = r.length; i < len; i++) { - if (r[i] == null || !(r[i] instanceof FetchResponse)) - continue; - - FetchResponse fr = (FetchResponse)r[i]; - if ((u = (UID)fr.getItem(UID.class)) != null) - v.addElement(u); - } - - notifyResponseHandlers(r); - handleResult(r[r.length-1]); - - UID[] ua = new UID[v.size()]; - v.copyInto(ua); - return ua; - } - - public Response[] fetch(MessageSet[] msgsets, String what) - throws ProtocolException { - return fetch(MessageSet.toString(msgsets), what, false); - } - - public Response[] fetch(int start, int end, String what) - throws ProtocolException { - return fetch(String.valueOf(start) + ":" + String.valueOf(end), - what, false); - } - - public Response[] fetch(int msg, String what) - throws ProtocolException { - return fetch(String.valueOf(msg), what, false); - } - - private Response[] fetch(String msgSequence, String what, boolean uid) - throws ProtocolException { - if (uid) - return command("UID FETCH " + msgSequence +" (" + what + ")",null); - else - return command("FETCH " + msgSequence + " (" + what + ")", null); - } - - /** - * COPY command. - */ - public void copy(MessageSet[] msgsets, String mbox) - throws ProtocolException { - copy(MessageSet.toString(msgsets), mbox); - } - - public void copy(int start, int end, String mbox) - throws ProtocolException { - copy(String.valueOf(start) + ":" + String.valueOf(end), - mbox); - } - - private void copy(String msgSequence, String mbox) - throws ProtocolException { - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeAtom(msgSequence); - args.writeString(mbox); - - simpleCommand("COPY", args); - } - - public void storeFlags(MessageSet[] msgsets, Flags flags, boolean set) - throws ProtocolException { - storeFlags(MessageSet.toString(msgsets), flags, set); - } - - public void storeFlags(int start, int end, Flags flags, boolean set) - throws ProtocolException { - storeFlags(String.valueOf(start) + ":" + String.valueOf(end), - flags, set); - } - - /** - * Set the specified flags on this message.

    - */ - public void storeFlags(int msg, Flags flags, boolean set) - throws ProtocolException { - storeFlags(String.valueOf(msg), flags, set); - } - - private void storeFlags(String msgset, Flags flags, boolean set) - throws ProtocolException { - Response[] r; - if (set) - r = command("STORE " + msgset + " +FLAGS " + - createFlagList(flags), null); - else - r = command("STORE " + msgset + " -FLAGS " + - createFlagList(flags), null); - - // Dispatch untagged responses - notifyResponseHandlers(r); - handleResult(r[r.length-1]); - } - - /** - * Creates an IMAP flag_list from the given Flags object. - */ - private String createFlagList(Flags flags) { - StringBuffer sb = new StringBuffer(); - sb.append("("); // start of flag_list - - Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags - boolean first = true; - for (int i = 0; i < sf.length; i++) { - String s; - Flags.Flag f = sf[i]; - if (f == Flags.Flag.ANSWERED) - s = "\\Answered"; - else if (f == Flags.Flag.DELETED) - s = "\\Deleted"; - else if (f == Flags.Flag.DRAFT) - s = "\\Draft"; - else if (f == Flags.Flag.FLAGGED) - s = "\\Flagged"; - else if (f == Flags.Flag.RECENT) - s = "\\Recent"; - else if (f == Flags.Flag.SEEN) - s = "\\Seen"; - else - continue; // skip it - if (first) - first = false; - else - sb.append(' '); - sb.append(s); - } - - String[] uf = flags.getUserFlags(); // get the user flag strings - for (int i = 0; i < uf.length; i++) { - if (first) - first = false; - else - sb.append(' '); - sb.append(uf[i]); - } - - sb.append(")"); // terminate flag_list - return sb.toString(); - } - - /** - * Issue the given search criterion on the specified message sets. - * Returns array of matching sequence numbers. An empty array - * is returned if no matches are found. - * - * @param msgsets array of MessageSets - * @param term SearchTerm - * @return array of matching sequence numbers. - */ - public int[] search(MessageSet[] msgsets, SearchTerm term) - throws ProtocolException, SearchException { - return search(MessageSet.toString(msgsets), term); - } - - /** - * Issue the given search criterion on all messages in this folder. - * Returns array of matching sequence numbers. An empty array - * is returned if no matches are found. - * - * @param term SearchTerm - * @return array of matching sequence numbers. - */ - public int[] search(SearchTerm term) - throws ProtocolException, SearchException { - return search("ALL", term); - } - - /* - * Apply the given SearchTerm on the specified sequence. - * Returns array of matching sequence numbers. Note that an empty - * array is returned for no matches. - */ - private int[] search(String msgSequence, SearchTerm term) - throws ProtocolException, SearchException { - // Check if the search "text" terms contain only ASCII chars - if (getSearchSequence().isAscii(term)) { - try { - return issueSearch(msgSequence, term, null); - } catch (IOException ioex) { /* will not happen */ } - } - - /* - * The search "text" terms do contain non-ASCII chars. We need to - * use SEARCH CHARSET ... - * The charsets we try to use are UTF-8 and the locale's - * default charset. If the server supports UTF-8, great, - * always use it. Else we try to use the default charset. - */ - - // Cycle thru the list of charsets - for (int i = 0; i < searchCharsets.length; i++) { - if (searchCharsets[i] == null) - continue; - - try { - return issueSearch(msgSequence, term, searchCharsets[i]); - } catch (CommandFailedException cfx) { - /* - * Server returned NO. For now, I'll just assume that - * this indicates that this charset is unsupported. - * We can check the BADCHARSET response code once - * that's spec'd into the IMAP RFC .. - */ - searchCharsets[i] = null; - continue; - } catch (IOException ioex) { - /* Charset conversion failed. Try the next one */ - continue; - } catch (ProtocolException pex) { - throw pex; - } catch (SearchException sex) { - throw sex; - } - } - - // No luck. - throw new SearchException("Search failed"); - } - - /* Apply the given SearchTerm on the specified sequence, using the - * given charset.

    - * Returns array of matching sequence numbers. Note that an empty - * array is returned for no matches. - */ - private int[] issueSearch(String msgSequence, SearchTerm term, - String charset) - throws ProtocolException, SearchException, IOException { - - // Generate a search-sequence with the given charset - Argument args = getSearchSequence().generateSequence(term, - charset == null ? null : - MimeUtility.javaCharset(charset) - ); - args.writeAtom(msgSequence); - - Response[] r; - - if (charset == null) // text is all US-ASCII - r = command("SEARCH", args); - else - r = command("SEARCH CHARSET " + charset, args); - - Response response = r[r.length-1]; - int[] matches = null; - - // Grab all SEARCH responses - if (response.isOK()) { // command succesful - Vector v = new Vector(); - int num; - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - // There *will* be one SEARCH response. - if (ir.keyEquals("SEARCH")) { - while ((num = ir.readNumber()) != -1) - v.addElement(new Integer(num)); - r[i] = null; - } - } - - // Copy the vector into 'matches' - int vsize = v.size(); - matches = new int[vsize]; - for (int i = 0; i < vsize; i++) - matches[i] = ((Integer)v.elementAt(i)).intValue(); - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return matches; - } - - /** - * Get the SearchSequence object. - * The SearchSequence object instance is saved in the searchSequence - * field. Subclasses of IMAPProtocol may override this method to - * return a subclass of SearchSequence, in order to add support for - * product-specific search terms. - * - * @since JavaMail 1.4.6 - */ - protected SearchSequence getSearchSequence() { - if (searchSequence == null) - searchSequence = new SearchSequence(); - return searchSequence; - } - - /** - * Sort messages in the folder according to the specified sort criteria. - * If the search term is not null, limit the sort to only the messages - * that match the search term. - * Returns an array of sorted sequence numbers. An empty array - * is returned if no matches are found. - * - * @param term sort criteria - * @param sterm SearchTerm - * @return array of matching sequence numbers. - * - * @see "RFC 5256" - * @since JavaMail 1.4.4 - */ - public int[] sort(SortTerm[] term, SearchTerm sterm) - throws ProtocolException, SearchException { - if (!hasCapability("SORT*")) - throw new BadCommandException("SORT not supported"); - - if (term == null || term.length == 0) - throw new BadCommandException("Must have at least one sort term"); - - Argument args = new Argument(); - Argument sargs = new Argument(); - for (int i = 0; i < term.length; i++) - sargs.writeAtom(term[i].toString()); - args.writeArgument(sargs); // sort criteria - - args.writeAtom("UTF-8"); // charset specification - if (sterm != null) { - try { - args.append( - getSearchSequence().generateSequence(sterm, "UTF-8")); - } catch (IOException ioex) { - // should never happen - throw new SearchException(ioex.toString()); - } - } else - args.writeAtom("ALL"); - - Response[] r = command("SORT", args); - Response response = r[r.length-1]; - int[] matches = null; - - // Grab all SORT responses - if (response.isOK()) { // command succesful - Vector v = new Vector(); - int num; - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("SORT")) { - while ((num = ir.readNumber()) != -1) - v.addElement(new Integer(num)); - r[i] = null; - } - } - - // Copy the vector into 'matches' - int vsize = v.size(); - matches = new int[vsize]; - for (int i = 0; i < vsize; i++) - matches[i] = ((Integer)v.elementAt(i)).intValue(); - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return matches; - } - - /** - * NAMESPACE Command. - * - * @see "RFC2342" - */ - public Namespaces namespace() throws ProtocolException { - if (!hasCapability("NAMESPACE")) - throw new BadCommandException("NAMESPACE not supported"); - - Response[] r = command("NAMESPACE", null); - - Namespaces namespace = null; - Response response = r[r.length-1]; - - // Grab NAMESPACE response - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("NAMESPACE")) { - if (namespace == null) - namespace = new Namespaces(ir); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return namespace; - } - - /** - * GETQUOTAROOT Command. - * - * Returns an array of Quota objects, representing the quotas - * for this mailbox and, indirectly, the quotaroots for this - * mailbox. - * - * @see "RFC2087" - */ - public Quota[] getQuotaRoot(String mbox) throws ProtocolException { - if (!hasCapability("QUOTA")) - throw new BadCommandException("GETQUOTAROOT not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Response[] r = command("GETQUOTAROOT", args); - - Response response = r[r.length-1]; - - Hashtable tab = new Hashtable(); - - // Grab all QUOTAROOT and QUOTA responses - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("QUOTAROOT")) { - // quotaroot_response - // ::= "QUOTAROOT" SP astring *(SP astring) - - // read name of mailbox and throw away - ir.readAtomString(); - // for each quotaroot add a placeholder quota - String root = null; - while ((root = ir.readAtomString()) != null && - root.length() > 0) - tab.put(root, new Quota(root)); - r[i] = null; - } else if (ir.keyEquals("QUOTA")) { - Quota quota = parseQuota(ir); - Quota q = (Quota)tab.get(quota.quotaRoot); - if (q != null && q.resources != null) { - // XXX - should merge resources - } - tab.put(quota.quotaRoot, quota); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - - Quota[] qa = new Quota[tab.size()]; - Enumeration e = tab.elements(); - for (int i = 0; e.hasMoreElements(); i++) - qa[i] = (Quota)e.nextElement(); - return qa; - } - - /** - * GETQUOTA Command. - * - * Returns an array of Quota objects, representing the quotas - * for this quotaroot. - * - * @see "RFC2087" - */ - public Quota[] getQuota(String root) throws ProtocolException { - if (!hasCapability("QUOTA")) - throw new BadCommandException("QUOTA not supported"); - - Argument args = new Argument(); - args.writeString(root); - - Response[] r = command("GETQUOTA", args); - - Quota quota = null; - Vector v = new Vector(); - Response response = r[r.length-1]; - - // Grab all QUOTA responses - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("QUOTA")) { - quota = parseQuota(ir); - v.addElement(quota); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - Quota[] qa = new Quota[v.size()]; - v.copyInto(qa); - return qa; - } - - /** - * SETQUOTA Command. - * - * Set the indicated quota on the corresponding quotaroot. - * - * @see "RFC2087" - */ - public void setQuota(Quota quota) throws ProtocolException { - if (!hasCapability("QUOTA")) - throw new BadCommandException("QUOTA not supported"); - - Argument args = new Argument(); - args.writeString(quota.quotaRoot); - Argument qargs = new Argument(); - if (quota.resources != null) { - for (int i = 0; i < quota.resources.length; i++) { - qargs.writeAtom(quota.resources[i].name); - qargs.writeNumber(quota.resources[i].limit); - } - } - args.writeArgument(qargs); - - Response[] r = command("SETQUOTA", args); - Response response = r[r.length-1]; - - // XXX - It's not clear from the RFC whether the SETQUOTA command - // will provoke untagged QUOTA responses. If it does, perhaps - // we should grab them here and return them? - - /* - Quota quota = null; - Vector v = new Vector(); - - // Grab all QUOTA responses - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("QUOTA")) { - quota = parseQuota(ir); - v.addElement(quota); - r[i] = null; - } - } - } - */ - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - /* - Quota[] qa = new Quota[v.size()]; - v.copyInto(qa); - return qa; - */ - } - - /** - * Parse a QUOTA response. - */ - private Quota parseQuota(Response r) throws ParsingException { - // quota_response ::= "QUOTA" SP astring SP quota_list - String quotaRoot = r.readAtomString(); // quotaroot ::= astring - Quota q = new Quota(quotaRoot); - r.skipSpaces(); - // quota_list ::= "(" #quota_resource ")" - if (r.readByte() != '(') - throw new ParsingException("parse error in QUOTA"); - - Vector v = new Vector(); - while (r.peekByte() != ')') { - // quota_resource ::= atom SP number SP number - String name = r.readAtom(); - if (name != null) { - long usage = r.readLong(); - long limit = r.readLong(); - Quota.Resource res = new Quota.Resource(name, usage, limit); - v.addElement(res); - } - } - r.readByte(); - q.resources = new Quota.Resource[v.size()]; - v.copyInto(q.resources); - return q; - } - - - /** - * SETACL Command. - * - * @see "RFC2086" - */ - public void setACL(String mbox, char modifier, ACL acl) - throws ProtocolException { - if (!hasCapability("ACL")) - throw new BadCommandException("ACL not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - args.writeString(acl.getName()); - String rights = acl.getRights().toString(); - if (modifier == '+' || modifier == '-') - rights = modifier + rights; - args.writeString(rights); - - Response[] r = command("SETACL", args); - Response response = r[r.length-1]; - - // dispatch untagged responses - notifyResponseHandlers(r); - handleResult(response); - } - - /** - * DELETEACL Command. - * - * @see "RFC2086" - */ - public void deleteACL(String mbox, String user) throws ProtocolException { - if (!hasCapability("ACL")) - throw new BadCommandException("ACL not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - args.writeString(user); - - Response[] r = command("DELETEACL", args); - Response response = r[r.length-1]; - - // dispatch untagged responses - notifyResponseHandlers(r); - handleResult(response); - } - - /** - * GETACL Command. - * - * @see "RFC2086" - */ - public ACL[] getACL(String mbox) throws ProtocolException { - if (!hasCapability("ACL")) - throw new BadCommandException("ACL not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Response[] r = command("GETACL", args); - Response response = r[r.length-1]; - - // Grab all ACL responses - Vector v = new Vector(); - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("ACL")) { - // acl_data ::= "ACL" SPACE mailbox - // *(SPACE identifier SPACE rights) - // read name of mailbox and throw away - ir.readAtomString(); - String name = null; - while ((name = ir.readAtomString()) != null) { - String rights = ir.readAtomString(); - if (rights == null) - break; - ACL acl = new ACL(name, new Rights(rights)); - v.addElement(acl); - } - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - ACL[] aa = new ACL[v.size()]; - v.copyInto(aa); - return aa; - } - - /** - * LISTRIGHTS Command. - * - * @see "RFC2086" - */ - public Rights[] listRights(String mbox, String user) - throws ProtocolException { - if (!hasCapability("ACL")) - throw new BadCommandException("ACL not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - args.writeString(user); - - Response[] r = command("LISTRIGHTS", args); - Response response = r[r.length-1]; - - // Grab LISTRIGHTS response - Vector v = new Vector(); - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("LISTRIGHTS")) { - // listrights_data ::= "LISTRIGHTS" SPACE mailbox - // SPACE identifier SPACE rights *(SPACE rights) - // read name of mailbox and throw away - ir.readAtomString(); - // read identifier and throw away - ir.readAtomString(); - String rights; - while ((rights = ir.readAtomString()) != null) - v.addElement(new Rights(rights)); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - Rights[] ra = new Rights[v.size()]; - v.copyInto(ra); - return ra; - } - - /** - * MYRIGHTS Command. - * - * @see "RFC2086" - */ - public Rights myRights(String mbox) throws ProtocolException { - if (!hasCapability("ACL")) - throw new BadCommandException("ACL not supported"); - - // encode the mbox as per RFC2060 - mbox = BASE64MailboxEncoder.encode(mbox); - - Argument args = new Argument(); - args.writeString(mbox); - - Response[] r = command("MYRIGHTS", args); - Response response = r[r.length-1]; - - // Grab MYRIGHTS response - Rights rights = null; - if (response.isOK()) { // command succesful - for (int i = 0, len = r.length; i < len; i++) { - if (!(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - if (ir.keyEquals("MYRIGHTS")) { - // myrights_data ::= "MYRIGHTS" SPACE mailbox SPACE rights - // read name of mailbox and throw away - ir.readAtomString(); - String rs = ir.readAtomString(); - if (rights == null) - rights = new Rights(rs); - r[i] = null; - } - } - } - - // dispatch remaining untagged responses - notifyResponseHandlers(r); - handleResult(response); - return rights; - } - - /* - * The tag used on the IDLE command. Set by idleStart() and - * used in processIdleResponse() to determine if the response - * is the matching end tag. - */ - private volatile String idleTag; - - /** - * IDLE Command.

    - * - * If the server supports the IDLE command extension, the IDLE - * command is issued and this method blocks until a response has - * been received. Once the first response has been received, the - * IDLE command is terminated and all responses are collected and - * handled and this method returns.

    - * - * Note that while this method is blocked waiting for a response, - * no other threads may issue any commands to the server that would - * use this same connection. - * - * @see "RFC2177" - * @since JavaMail 1.4.1 - */ - public synchronized void idleStart() throws ProtocolException { - if (!hasCapability("IDLE")) - throw new BadCommandException("IDLE not supported"); - - Vector v = new Vector(); - boolean done = false; - Response r = null; - - // write the command - try { - idleTag = writeCommand("IDLE", null); - } catch (LiteralException lex) { - v.addElement(lex.getResponse()); - done = true; - } catch (Exception ex) { - // Convert this into a BYE response - v.addElement(Response.byeResponse(ex)); - done = true; - } - - while (!done) { - try { - r = readResponse(); - } catch (IOException ioex) { - // convert this into a BYE response - r = Response.byeResponse(ioex); - } catch (ProtocolException pex) { - continue; // skip this response - } - - v.addElement(r); - - if (r.isContinuation() || r.isBYE()) - done = true; - } - - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - r = responses[responses.length-1]; - - // dispatch remaining untagged responses - notifyResponseHandlers(responses); - if (!r.isContinuation()) - handleResult(r); - } - - /** - * While an IDLE command is in progress, read a response - * sent from the server. The response is read with no locks - * held so that when the read blocks waiting for the response - * from the server it's not holding locks that would prevent - * other threads from interrupting the IDLE command. - * - * @since JavaMail 1.4.1 - */ - public synchronized Response readIdleResponse() { - if (idleTag == null) - return null; // IDLE not in progress - Response r = null; - while (r == null) { - try { - r = readResponse(); - } catch (InterruptedIOException iioex) { - /* - * If a socket timeout was set, the read will timeout - * before the IDLE times out. In that case, just go - * back and read some more. After all, the point of - * IDLE is to sit here and wait until something happens. - */ - if (iioex.bytesTransferred == 0) - r = null; // keep trying - else - // convert this into a BYE response - r = Response.byeResponse(iioex); - } catch (IOException ioex) { - // convert this into a BYE response - r = Response.byeResponse(ioex); - } catch (ProtocolException pex) { - // convert this into a BYE response - r = Response.byeResponse(pex); - } - } - return r; - } - - /** - * Process a response returned by readIdleResponse(). - * This method will be called with appropriate locks - * held so that the processing of the response is safe. - * - * @since JavaMail 1.4.1 - */ - public boolean processIdleResponse(Response r) throws ProtocolException { - Response[] responses = new Response[1]; - responses[0] = r; - boolean done = false; // done reading responses? - notifyResponseHandlers(responses); - - if (r.isBYE()) // shouldn't wait for command completion response - done = true; - - // If this is a matching command completion response, we are done - if (r.isTagged() && r.getTag().equals(idleTag)) - done = true; - - if (done) - idleTag = null; // no longer in IDLE - - handleResult(r); - return !done; - } - - // the DONE command to break out of IDLE - private static final byte[] DONE = { 'D', 'O', 'N', 'E', '\r', '\n' }; - - /** - * Abort an IDLE command. While one thread is blocked in - * readIdleResponse(), another thread will use this method - * to abort the IDLE command, which will cause the server - * to send the closing tag for the IDLE command, which - * readIdleResponse() and processIdleResponse() will see - * and terminate the IDLE state. - * - * @since JavaMail 1.4.1 - */ - public void idleAbort() throws ProtocolException { - OutputStream os = getOutputStream(); - try { - os.write(DONE); - os.flush(); - } catch (IOException ex) { - // nothing to do, hope to detect it again later - } - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java b/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java deleted file mode 100644 index 6d8fc7b7..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.*; -import java.util.*; -import com.sun.mail.util.*; -import com.sun.mail.iap.*; - -/** - * This class represents a response obtained from the input stream - * of an IMAP server. - * - * @author John Mani - */ - -public class IMAPResponse extends Response { - private String key; - private int number; - - public IMAPResponse(Protocol c) throws IOException, ProtocolException { - super(c); - init(); - } - - private void init() throws IOException, ProtocolException { - // continue parsing if this is an untagged response - if (isUnTagged() && !isOK() && !isNO() && !isBAD() && !isBYE()) { - key = readAtom(); - - // Is this response of the form "* " - try { - number = Integer.parseInt(key); - key = readAtom(); - } catch (NumberFormatException ne) { } - } - } - - /** - * Copy constructor. - */ - public IMAPResponse(IMAPResponse r) { - super((Response)r); - key = r.key; - number = r.number; - } - - /** - * For testing. - */ - public IMAPResponse(String r) throws IOException, ProtocolException { - super(r); - init(); - } - - /** - * Read a list of space-separated "flag_extension" sequences and - * return the list as a array of Strings. An empty list is returned - * as null. This is an IMAP-ism, and perhaps this method should - * moved into the IMAP layer. - */ - public String[] readSimpleList() { - skipSpaces(); - - if (buffer[index] != '(') // not what we expected - return null; - index++; // skip '(' - - Vector v = new Vector(); - int start; - for (start = index; buffer[index] != ')'; index++) { - if (buffer[index] == ' ') { // got one item - v.addElement(ASCIIUtility.toString(buffer, start, index)); - start = index+1; // index gets incremented at the top - } - } - if (index > start) // get the last item - v.addElement(ASCIIUtility.toString(buffer, start, index)); - index++; // skip ')' - - int size = v.size(); - if (size > 0) { - String[] s = new String[size]; - v.copyInto(s); - return s; - } else // empty list - return null; - } - - public String getKey() { - return key; - } - - public boolean keyEquals(String k) { - if (key != null && key.equalsIgnoreCase(k)) - return true; - else - return false; - } - - public int getNumber() { - return number; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java b/src/main/java/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java deleted file mode 100644 index c3d49b9d..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.*; -import java.util.*; -import java.util.logging.Level; -import javax.security.sasl.*; -import javax.security.auth.callback.*; - -import com.sun.mail.iap.*; -import com.sun.mail.imap.*; -import com.sun.mail.util.*; - -/** - * This class contains a single method that does authentication using - * SASL. This is in a separate class so that it can be compiled with - * J2SE 1.5. Eventually it should be merged into IMAPProtocol.java. - */ - -public class IMAPSaslAuthenticator implements SaslAuthenticator { - - private IMAPProtocol pr; - private String name; - private Properties props; - private MailLogger logger; - private String host; - - public IMAPSaslAuthenticator(IMAPProtocol pr, String name, Properties props, - MailLogger logger, String host) { - this.pr = pr; - this.name = name; - this.props = props; - this.logger = logger; - this.host = host; - } - - public boolean authenticate(String[] mechs, final String realm, - final String authzid, final String u, - final String p) throws ProtocolException { - - synchronized (pr) { // authenticate method should be synchronized - Vector v = new Vector(); - String tag = null; - Response r = null; - boolean done = false; - if (logger.isLoggable(Level.FINE)) { - logger.fine("SASL Mechanisms:"); - for (int i = 0; i < mechs.length; i++) - logger.fine(" " + mechs[i]); - logger.fine(""); - } - - SaslClient sc; - CallbackHandler cbh = new CallbackHandler() { - public void handle(Callback[] callbacks) { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL callback length: " + callbacks.length); - for (int i = 0; i < callbacks.length; i++) { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL callback " + i + ": " + callbacks[i]); - if (callbacks[i] instanceof NameCallback) { - NameCallback ncb = (NameCallback)callbacks[i]; - ncb.setName(u); - } else if (callbacks[i] instanceof PasswordCallback) { - PasswordCallback pcb = (PasswordCallback)callbacks[i]; - pcb.setPassword(p.toCharArray()); - } else if (callbacks[i] instanceof RealmCallback) { - RealmCallback rcb = (RealmCallback)callbacks[i]; - rcb.setText(realm != null ? - realm : rcb.getDefaultText()); - } else if (callbacks[i] instanceof RealmChoiceCallback) { - RealmChoiceCallback rcb = - (RealmChoiceCallback)callbacks[i]; - if (realm == null) - rcb.setSelectedIndex(rcb.getDefaultChoice()); - else { - // need to find specified realm in list - String[] choices = rcb.getChoices(); - for (int k = 0; k < choices.length; k++) { - if (choices[k].equals(realm)) { - rcb.setSelectedIndex(k); - break; - } - } - } - } - } - } - }; - - try { - sc = Sasl.createSaslClient(mechs, authzid, name, host, - (Map)props, cbh); - } catch (SaslException sex) { - logger.log(Level.FINE, "Failed to create SASL client", sex); - return false; - } - if (sc == null) { - logger.fine("No SASL support"); - return false; - } - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL client " + sc.getMechanismName()); - - try { - tag = pr.writeCommand("AUTHENTICATE " + sc.getMechanismName(), - null); - } catch (Exception ex) { - logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex); - return false; - } - - OutputStream os = pr.getIMAPOutputStream(); // stream to IMAP server - - /* - * Wrap a BASE64Encoder around a ByteArrayOutputstream - * to craft b64 encoded username and password strings - * - * Note that the encoded bytes should be sent "as-is" to the - * server, *not* as literals or quoted-strings. - * - * Also note that unlike the B64 definition in MIME, CRLFs - * should *not* be inserted during the encoding process. So, I - * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, - * which should be sufficiently large ! - */ - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] CRLF = { (byte)'\r', (byte)'\n'}; - - // Hack for Novell GroupWise XGWTRUSTEDAPP authentication mechanism - boolean isXGWTRUSTEDAPP = - sc.getMechanismName().equals("XGWTRUSTEDAPP") && - PropUtil.getBooleanProperty(props, - "mail." + name + ".sasl.xgwtrustedapphack.enable", true); - while (!done) { // loop till we are done - try { - r = pr.readResponse(); - if (r.isContinuation()) { - byte[] ba = null; - if (!sc.isComplete()) { - ba = r.readByteArray().getNewBytes(); - if (ba.length > 0) - ba = BASE64DecoderStream.decode(ba); - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL challenge: " + - ASCIIUtility.toString(ba, 0, ba.length) + " :"); - ba = sc.evaluateChallenge(ba); - } - if (ba == null) { - logger.fine("SASL no response"); - os.write(CRLF); // write out empty line - os.flush(); // flush the stream - bos.reset(); // reset buffer - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL response: " + - ASCIIUtility.toString(ba, 0, ba.length) + " :"); - ba = BASE64EncoderStream.encode(ba); - if (isXGWTRUSTEDAPP) - bos.write(ASCIIUtility.getBytes("XGWTRUSTEDAPP ")); - bos.write(ba); - - bos.write(CRLF); // CRLF termination - os.write(bos.toByteArray()); // write out line - os.flush(); // flush the stream - bos.reset(); // reset buffer - } - } else if (r.isTagged() && r.getTag().equals(tag)) - // Ah, our tagged response - done = true; - else if (r.isBYE()) // outta here - done = true; - else // hmm .. unsolicited response here ?! - v.addElement(r); - } catch (Exception ioex) { - logger.log(Level.FINE, "SASL Exception", ioex); - // convert this into a BYE response - r = Response.byeResponse(ioex); - done = true; - // XXX - ultimately return true??? - } - } - - if (sc.isComplete() /*&& res.status == SUCCESS*/) { - String qop = (String)sc.getNegotiatedProperty(Sasl.QOP); - if (qop != null && (qop.equalsIgnoreCase("auth-int") || - qop.equalsIgnoreCase("auth-conf"))) { - // XXX - NOT SUPPORTED!!! - logger.fine( - "SASL Mechanism requires integrity or confidentiality"); - return false; - } - } - - /* Dispatch untagged responses. - * NOTE: in our current upper level IMAP classes, we add the - * responseHandler to the Protocol object only *after* the - * connection has been authenticated. So, for now, the below - * code really ends up being just a no-op. - */ - Response[] responses = new Response[v.size()]; - v.copyInto(responses); - pr.notifyResponseHandlers(responses); - - // Handle the final OK, NO, BAD or BYE response - pr.handleResult(r); - pr.setCapabilities(r); - - /* - * If we're using the Novell Groupwise XGWTRUSTEDAPP mechanism - * we always have to issue a LOGIN command to select the user - * we want to operate as. - */ - if (isXGWTRUSTEDAPP) { - Argument args = new Argument(); - args.writeString(authzid != null ? authzid : u); - - responses = pr.command("LOGIN", args); - - // dispatch untagged responses - pr.notifyResponseHandlers(responses); - - // Handle result of this command - pr.handleResult(responses[responses.length-1]); - // If the response includes a CAPABILITY response code, process it - pr.setCapabilities(responses[responses.length-1]); - } - return true; - } - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java b/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java deleted file mode 100644 index 244cc1d4..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Date; -import java.util.TimeZone; -import java.util.Locale; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.text.FieldPosition; - -import javax.mail.internet.MailDateFormat; - -import com.sun.mail.iap.*; - - -/** - * This class - * - * @author John Mani - */ - -public class INTERNALDATE implements Item { - - static final char[] name = - {'I','N','T','E','R','N','A','L','D','A','T','E'}; - public int msgno; - protected Date date; - - /* - * Used to parse dates only. The parse method is thread safe - * so we only need to create a single object for use by all - * instances. We depend on the fact that the MailDateFormat - * class will parse dates in INTERNALDATE format as well as - * dates in RFC 822 format. - */ - private static MailDateFormat mailDateFormat = new MailDateFormat(); - - /** - * Constructor - */ - public INTERNALDATE(FetchResponse r) throws ParsingException { - msgno = r.getNumber(); - r.skipSpaces(); - String s = r.readString(); - if (s == null) - throw new ParsingException("INTERNALDATE is NIL"); - try { - date = mailDateFormat.parse(s); - } catch (ParseException pex) { - throw new ParsingException("INTERNALDATE parse error"); - } - } - - public Date getDate() { - return date; - } - - // INTERNALDATE formatter - - private static SimpleDateFormat df = - // Need Locale.US, the "MMM" field can produce unexpected values - // in non US locales ! - new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss ", Locale.US); - - /** - * Format given Date object into INTERNALDATE string - */ - public static String format(Date d) { - /* - * SimpleDateFormat objects aren't thread safe, so rather - * than create a separate such object for each request, - * we create one object and synchronize its use here - * so that only one thread is using it at a time. This - * trades off some potential concurrency for speed in the - * common case. - * - * This method is only used when formatting the date in a - * message that's being appended to a folder. - */ - StringBuffer sb = new StringBuffer(); - synchronized (df) { - df.format(d, sb, new FieldPosition(0)); - } - - // compute timezone offset string - TimeZone tz = TimeZone.getDefault(); - int offset = tz.getOffset(d.getTime()); // get offset from GMT - int rawOffsetInMins = offset / 60 / 1000; // offset from GMT in mins - if (rawOffsetInMins < 0) { - sb.append('-'); - rawOffsetInMins = (-rawOffsetInMins); - } else - sb.append('+'); - - int offsetInHrs = rawOffsetInMins / 60; - int offsetInMins = rawOffsetInMins % 60; - - sb.append(Character.forDigit((offsetInHrs/10), 10)); - sb.append(Character.forDigit((offsetInHrs%10), 10)); - sb.append(Character.forDigit((offsetInMins/10), 10)); - sb.append(Character.forDigit((offsetInMins%10), 10)); - - return sb.toString(); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/Item.java b/src/main/java/com/sun/mail/imap/protocol/Item.java deleted file mode 100644 index a119fe81..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/Item.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -/** - * A tagging interface for all IMAP data items. - * Note that the "name" field of all IMAP items MUST be in uppercase.

    - * - * See the BODY, BODYSTRUCTURE, ENVELOPE, FLAGS, INTERNALDATE, RFC822DATA, - * RFC822SIZE, and UID classes. - * - * @author John Mani - */ - -public interface Item { -} diff --git a/src/main/java/com/sun/mail/imap/protocol/ListInfo.java b/src/main/java/com/sun/mail/imap/protocol/ListInfo.java deleted file mode 100644 index 9cde3d63..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/ListInfo.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Vector; - -import com.sun.mail.iap.*; - -/** - * A LIST response. - * - * @author John Mani - * @author Bill Shannon - */ - -public class ListInfo { - public String name = null; - public char separator = '/'; - public boolean hasInferiors = true; - public boolean canOpen = true; - public int changeState = INDETERMINATE; - public String[] attrs; - - public static final int CHANGED = 1; - public static final int UNCHANGED = 2; - public static final int INDETERMINATE = 3; - - public ListInfo(IMAPResponse r) throws ParsingException { - String[] s = r.readSimpleList(); - - Vector v = new Vector(); // accumulate attributes - if (s != null) { - // non-empty attribute list - for (int i = 0; i < s.length; i++) { - if (s[i].equalsIgnoreCase("\\Marked")) - changeState = CHANGED; - else if (s[i].equalsIgnoreCase("\\Unmarked")) - changeState = UNCHANGED; - else if (s[i].equalsIgnoreCase("\\Noselect")) - canOpen = false; - else if (s[i].equalsIgnoreCase("\\Noinferiors")) - hasInferiors = false; - v.addElement(s[i]); - } - } - attrs = new String[v.size()]; - v.copyInto(attrs); - - r.skipSpaces(); - if (r.readByte() == '"') { - if ((separator = (char)r.readByte()) == '\\') - // escaped separator character - separator = (char)r.readByte(); - r.skip(1); // skip <"> - } else // NIL - r.skip(2); - - r.skipSpaces(); - name = r.readAtomString(); - - // decode the name (using RFC2060's modified UTF7) - name = BASE64MailboxDecoder.decode(name); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java b/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java deleted file mode 100644 index bc02a47e..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import javax.mail.Flags; -import com.sun.mail.iap.*; - -/** - * This class - * - * @author John Mani - */ - -public class MailboxInfo { - public Flags availableFlags = null; - public Flags permanentFlags = null; - public int total = -1; - public int recent = -1; - public int first = -1; - public long uidvalidity = -1; - public long uidnext = -1; - public int mode; - - public MailboxInfo(Response[] r) throws ParsingException { - for (int i = 0; i < r.length; i++) { - if (r[i] == null || !(r[i] instanceof IMAPResponse)) - continue; - - IMAPResponse ir = (IMAPResponse)r[i]; - - if (ir.keyEquals("EXISTS")) { - total = ir.getNumber(); - r[i] = null; // remove this response - } - else if (ir.keyEquals("RECENT")) { - recent = ir.getNumber(); - r[i] = null; // remove this response - } - else if (ir.keyEquals("FLAGS")) { - availableFlags = new FLAGS(ir); - r[i] = null; // remove this response - } - else if (ir.isUnTagged() && ir.isOK()) { - /* should be one of: - * OK [UNSEEN 12] - * OK [UIDVALIDITY 3857529045] - * OK [PERMANENTFLAGS (\Deleted)] - * OK [UIDNEXT 44] - */ - ir.skipSpaces(); - - if (ir.readByte() != '[') { // huh ??? - ir.reset(); - continue; - } - - boolean handled = true; - String s = ir.readAtom(); - if (s.equalsIgnoreCase("UNSEEN")) - first = ir.readNumber(); - else if (s.equalsIgnoreCase("UIDVALIDITY")) - uidvalidity = ir.readLong(); - else if (s.equalsIgnoreCase("PERMANENTFLAGS")) - permanentFlags = new FLAGS(ir); - else if (s.equalsIgnoreCase("UIDNEXT")) - uidnext = ir.readLong(); - else - handled = false; // possibly an ALERT - - if (handled) - r[i] = null; // remove this response - else - ir.reset(); // so ALERT can be read - } - } - - /* - * The PERMANENTFLAGS response code is optional, and if - * not present implies that all flags in the required FLAGS - * response can be changed permanently. - */ - if (permanentFlags == null) { - if (availableFlags != null) - permanentFlags = new Flags(availableFlags); - else - permanentFlags = new Flags(); - } - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/MessageSet.java b/src/main/java/com/sun/mail/imap/protocol/MessageSet.java deleted file mode 100644 index 971428d0..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/MessageSet.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Vector; - -/** - * This class holds the 'start' and 'end' for a range of messages - */ -public class MessageSet { - - public int start; - public int end; - - public MessageSet() { } - - public MessageSet(int start, int end) { - this.start = start; - this.end = end; - } - - /** - * Count the total number of elements in a MessageSet - **/ - public int size() { - return end - start + 1; - } - - /* - * Convert an array of integers into an array of MessageSets - */ - public static MessageSet[] createMessageSets(int[] msgs) { - Vector v = new Vector(); - int i,j; - - for (i=0; i < msgs.length; i++) { - MessageSet ms = new MessageSet(); - ms.start = msgs[i]; - - // Look for contiguous elements - for (j=i+1; j < msgs.length; j++) { - if (msgs[j] != msgs[j-1] +1) - break; - } - ms.end = msgs[j-1]; - v.addElement(ms); - i = j-1; // i gets incremented @ top of the loop - } - MessageSet[] msgsets = new MessageSet[v.size()]; - v.copyInto(msgsets); - return msgsets; - } - - /** - * Convert an array of MessageSets into an IMAP sequence range - */ - public static String toString(MessageSet[] msgsets) { - if (msgsets == null || msgsets.length == 0) // Empty msgset - return null; - - int i = 0; // msgset index - StringBuffer s = new StringBuffer(); - int size = msgsets.length; - int start, end; - - for (;;) { - start = msgsets[i].start; - end = msgsets[i].end; - - if (end > start) - s.append(start).append(':').append(end); - else // end == start means only one element - s.append(start); - - i++; // Next MessageSet - if (i >= size) // No more MessageSets - break; - else - s.append(','); - } - return s.toString(); - } - - - /* - * Count the total number of elements in an array of MessageSets - */ - public static int size(MessageSet[] msgsets) { - int count = 0; - - if (msgsets == null) // Null msgset - return 0; - - for (int i=0; i < msgsets.length; i++) - count += msgsets[i].size(); - - return count; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/Namespaces.java b/src/main/java/com/sun/mail/imap/protocol/Namespaces.java deleted file mode 100644 index d1ae451c..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/Namespaces.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.*; -import com.sun.mail.iap.*; - -/** - * This class and its inner class represent the response to the - * NAMESPACE command.

    - * - * See RFC 2342. - * - * @author Bill Shannon - */ - -public class Namespaces { - - /** - * A single namespace entry. - */ - public static class Namespace { - /** - * Prefix string for the namespace. - */ - public String prefix; - - /** - * Delimiter between names in this namespace. - */ - public char delimiter; - - /** - * Parse a namespace element out of the response. - */ - public Namespace(Response r) throws ProtocolException { - // Namespace_Element = "(" string SP (<"> QUOTED_CHAR <"> / nil) - // *(Namespace_Response_Extension) ")" - if (r.readByte() != '(') - throw new ProtocolException( - "Missing '(' at start of Namespace"); - // first, the prefix - prefix = BASE64MailboxDecoder.decode(r.readString()); - r.skipSpaces(); - // delimiter is a quoted character or NIL - if (r.peekByte() == '"') { - r.readByte(); - delimiter = (char)r.readByte(); - if (delimiter == '\\') - delimiter = (char)r.readByte(); - if (r.readByte() != '"') - throw new ProtocolException( - "Missing '\"' at end of QUOTED_CHAR"); - } else { - String s = r.readAtom(); - if (s == null) - throw new ProtocolException("Expected NIL, got null"); - if (!s.equalsIgnoreCase("NIL")) - throw new ProtocolException("Expected NIL, got " + s); - delimiter = 0; - } - // at end of Namespace data? - if (r.peekByte() != ')') { - // otherwise, must be a Namespace_Response_Extension - // Namespace_Response_Extension = SP string SP - // "(" string *(SP string) ")" - r.skipSpaces(); - r.readString(); - r.skipSpaces(); - r.readStringList(); - } - if (r.readByte() != ')') - throw new ProtocolException("Missing ')' at end of Namespace"); - } - }; - - /** - * The personal namespaces. - * May be null. - */ - public Namespace[] personal; - - /** - * The namespaces for other users. - * May be null. - */ - public Namespace[] otherUsers; - - /** - * The shared namespace. - * May be null. - */ - public Namespace[] shared; - - /** - * Parse out all the namespaces. - */ - public Namespaces(Response r) throws ProtocolException { - personal = getNamespaces(r); - otherUsers = getNamespaces(r); - shared = getNamespaces(r); - } - - /** - * Parse out one of the three sets of namespaces. - */ - private Namespace[] getNamespaces(Response r) throws ProtocolException { - r.skipSpaces(); - // Namespace = nil / "(" 1*( Namespace_Element) ")" - if (r.peekByte() == '(') { - Vector v = new Vector(); - r.readByte(); - do { - Namespace ns = new Namespace(r); - v.addElement(ns); - } while (r.peekByte() != ')'); - r.readByte(); - Namespace[] nsa = new Namespace[v.size()]; - v.copyInto(nsa); - return nsa; - } else { - String s = r.readAtom(); - if (s == null) - throw new ProtocolException("Expected NIL, got null"); - if (!s.equalsIgnoreCase("NIL")) - throw new ProtocolException("Expected NIL, got " + s); - return null; - } - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java b/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java deleted file mode 100644 index 5278be65..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.io.ByteArrayInputStream; -import com.sun.mail.iap.*; -import com.sun.mail.util.ASCIIUtility; - -/** - * This class - * - * @author John Mani - */ - -public class RFC822DATA implements Item { - - static final char[] name = {'R','F','C','8','2','2'}; - public int msgno; - public ByteArray data; - - /** - * Constructor - */ - public RFC822DATA(FetchResponse r) throws ParsingException { - msgno = r.getNumber(); - r.skipSpaces(); - data = r.readByteArray(); - } - - public ByteArray getByteArray() { - return data; - } - - public ByteArrayInputStream getByteArrayInputStream() { - if (data != null) - return data.toByteArrayInputStream(); - else - return null; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java b/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java deleted file mode 100644 index 2bc7b147..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import com.sun.mail.iap.*; - -/** - * This class - * - * @author John Mani - */ - -public class RFC822SIZE implements Item { - - static final char[] name = {'R','F','C','8','2','2','.','S','I','Z','E'}; - public int msgno; - - public int size; - - /** - * Constructor - */ - public RFC822SIZE(FetchResponse r) throws ParsingException { - msgno = r.getNumber(); - r.skipSpaces(); - size = r.readNumber(); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java b/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java deleted file mode 100644 index 21c0bbeb..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import com.sun.mail.iap.ProtocolException; - -/** - * Interface to make it easier to call IMAPSaslAuthenticator. - */ - -public interface SaslAuthenticator { - public boolean authenticate(String[] mechs, String realm, String authzid, - String u, String p) throws ProtocolException; - -} diff --git a/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java b/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java deleted file mode 100644 index bf4628b2..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.*; -import java.io.IOException; - -import javax.mail.*; -import javax.mail.search.*; -import com.sun.mail.iap.*; - -/** - * This class traverses a search-tree and generates the - * corresponding IMAP search sequence. - * - * Each IMAPProtocol instance contains an instance of this class, - * which might be subclassed by subclasses of IMAPProtocol to add - * support for additional product-specific search terms. - * - * @author John Mani - */ -public class SearchSequence { - - /** - * Generate the IMAP search sequence for the given search expression. - */ - public Argument generateSequence(SearchTerm term, String charset) - throws SearchException, IOException { - /* - * Call the appropriate handler depending on the type of - * the search-term ... - */ - if (term instanceof AndTerm) // AND - return and((AndTerm)term, charset); - else if (term instanceof OrTerm) // OR - return or((OrTerm)term, charset); - else if (term instanceof NotTerm) // NOT - return not((NotTerm)term, charset); - else if (term instanceof HeaderTerm) // HEADER - return header((HeaderTerm)term, charset); - else if (term instanceof FlagTerm) // FLAG - return flag((FlagTerm)term); - else if (term instanceof FromTerm) { // FROM - FromTerm fterm = (FromTerm)term; - return from(fterm.getAddress().toString(), charset); - } - else if (term instanceof FromStringTerm) { // FROM - FromStringTerm fterm = (FromStringTerm)term; - return from(fterm.getPattern(), charset); - } - else if (term instanceof RecipientTerm) { // RECIPIENT - RecipientTerm rterm = (RecipientTerm)term; - return recipient(rterm.getRecipientType(), - rterm.getAddress().toString(), - charset); - } - else if (term instanceof RecipientStringTerm) { // RECIPIENT - RecipientStringTerm rterm = (RecipientStringTerm)term; - return recipient(rterm.getRecipientType(), - rterm.getPattern(), - charset); - } - else if (term instanceof SubjectTerm) // SUBJECT - return subject((SubjectTerm)term, charset); - else if (term instanceof BodyTerm) // BODY - return body((BodyTerm)term, charset); - else if (term instanceof SizeTerm) // SIZE - return size((SizeTerm)term); - else if (term instanceof SentDateTerm) // SENTDATE - return sentdate((SentDateTerm)term); - else if (term instanceof ReceivedDateTerm) // INTERNALDATE - return receiveddate((ReceivedDateTerm)term); - else if (term instanceof MessageIDTerm) // MessageID - return messageid((MessageIDTerm)term, charset); - else - throw new SearchException("Search too complex"); - } - - /* * - * Check if the "text" terms in the given SearchTerm contain - * non US-ASCII characters. - */ - public static boolean isAscii(SearchTerm term) { - if (term instanceof AndTerm || term instanceof OrTerm) { - SearchTerm[] terms; - if (term instanceof AndTerm) - terms = ((AndTerm)term).getTerms(); - else - terms = ((OrTerm)term).getTerms(); - - for (int i = 0; i < terms.length; i++) - if (!isAscii(terms[i])) // outta here ! - return false; - } else if (term instanceof NotTerm) - return isAscii(((NotTerm)term).getTerm()); - else if (term instanceof StringTerm) - return isAscii(((StringTerm)term).getPattern()); - else if (term instanceof AddressTerm) - return isAscii(((AddressTerm)term).getAddress().toString()); - - // Any other term returns true. - return true; - } - - /** - * Does this string contain only ASCII characters? - */ - public static boolean isAscii(String s) { - int l = s.length(); - - for (int i=0; i < l; i++) { - if ((int)s.charAt(i) > 0177) // non-ascii - return false; - } - return true; - } - - protected Argument and(AndTerm term, String charset) - throws SearchException, IOException { - // Combine the sequences for both terms - SearchTerm[] terms = term.getTerms(); - // Generate the search sequence for the first term - Argument result = generateSequence(terms[0], charset); - // Append other terms - for (int i = 1; i < terms.length; i++) - result.append(generateSequence(terms[i], charset)); - return result; - } - - protected Argument or(OrTerm term, String charset) - throws SearchException, IOException { - SearchTerm[] terms = term.getTerms(); - - /* The IMAP OR operator takes only two operands. So if - * we have more than 2 operands, group them into 2-operand - * OR Terms. - */ - if (terms.length > 2) { - SearchTerm t = terms[0]; - - // Include rest of the terms - for (int i = 1; i < terms.length; i++) - t = new OrTerm(t, terms[i]); - - term = (OrTerm)t; // set 'term' to the new jumbo OrTerm we - // just created - terms = term.getTerms(); - } - - // 'term' now has only two operands - Argument result = new Argument(); - - // Add the OR search-key, if more than one term - if (terms.length > 1) - result.writeAtom("OR"); - - /* If this term is an AND expression, we need to enclose it - * within paranthesis. - * - * AND expressions are either AndTerms or FlagTerms - */ - if (terms[0] instanceof AndTerm || terms[0] instanceof FlagTerm) - result.writeArgument(generateSequence(terms[0], charset)); - else - result.append(generateSequence(terms[0], charset)); - - // Repeat the above for the second term, if there is one - if (terms.length > 1) { - if (terms[1] instanceof AndTerm || terms[1] instanceof FlagTerm) - result.writeArgument(generateSequence(terms[1], charset)); - else - result.append(generateSequence(terms[1], charset)); - } - - return result; - } - - protected Argument not(NotTerm term, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - - // Add the NOT search-key - result.writeAtom("NOT"); - - /* If this term is an AND expression, we need to enclose it - * within paranthesis. - * - * AND expressions are either AndTerms or FlagTerms - */ - SearchTerm nterm = term.getTerm(); - if (nterm instanceof AndTerm || nterm instanceof FlagTerm) - result.writeArgument(generateSequence(nterm, charset)); - else - result.append(generateSequence(nterm, charset)); - - return result; - } - - protected Argument header(HeaderTerm term, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - result.writeAtom("HEADER"); - result.writeString(term.getHeaderName()); - result.writeString(term.getPattern(), charset); - return result; - } - - protected Argument messageid(MessageIDTerm term, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - result.writeAtom("HEADER"); - result.writeString("Message-ID"); - // XXX confirm that charset conversion ought to be done - result.writeString(term.getPattern(), charset); - return result; - } - - protected Argument flag(FlagTerm term) throws SearchException { - boolean set = term.getTestSet(); - - Argument result = new Argument(); - - Flags flags = term.getFlags(); - Flags.Flag[] sf = flags.getSystemFlags(); - String[] uf = flags.getUserFlags(); - if (sf.length == 0 && uf.length == 0) - throw new SearchException("Invalid FlagTerm"); - - for (int i = 0; i < sf.length; i++) { - if (sf[i] == Flags.Flag.DELETED) - result.writeAtom(set ? "DELETED": "UNDELETED"); - else if (sf[i] == Flags.Flag.ANSWERED) - result.writeAtom(set ? "ANSWERED": "UNANSWERED"); - else if (sf[i] == Flags.Flag.DRAFT) - result.writeAtom(set ? "DRAFT": "UNDRAFT"); - else if (sf[i] == Flags.Flag.FLAGGED) - result.writeAtom(set ? "FLAGGED": "UNFLAGGED"); - else if (sf[i] == Flags.Flag.RECENT) - result.writeAtom(set ? "RECENT": "OLD"); - else if (sf[i] == Flags.Flag.SEEN) - result.writeAtom(set ? "SEEN": "UNSEEN"); - } - - for (int i = 0; i < uf.length; i++) { - result.writeAtom(set ? "KEYWORD" : "UNKEYWORD"); - result.writeAtom(uf[i]); - } - - return result; - } - - protected Argument from(String address, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - result.writeAtom("FROM"); - result.writeString(address, charset); - return result; - } - - protected Argument recipient(Message.RecipientType type, - String address, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - - if (type == Message.RecipientType.TO) - result.writeAtom("TO"); - else if (type == Message.RecipientType.CC) - result.writeAtom("CC"); - else if (type == Message.RecipientType.BCC) - result.writeAtom("BCC"); - else - throw new SearchException("Illegal Recipient type"); - - result.writeString(address, charset); - return result; - } - - protected Argument subject(SubjectTerm term, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - - result.writeAtom("SUBJECT"); - result.writeString(term.getPattern(), charset); - return result; - } - - protected Argument body(BodyTerm term, String charset) - throws SearchException, IOException { - Argument result = new Argument(); - - result.writeAtom("BODY"); - result.writeString(term.getPattern(), charset); - return result; - } - - protected Argument size(SizeTerm term) - throws SearchException { - Argument result = new Argument(); - - switch (term.getComparison()) { - case ComparisonTerm.GT: - result.writeAtom("LARGER"); - break; - case ComparisonTerm.LT: - result.writeAtom("SMALLER"); - break; - default: - // GT and LT is all we get from IMAP for size - throw new SearchException("Cannot handle Comparison"); - } - - result.writeNumber(term.getNumber()); - return result; - } - - // Date SEARCH stuff ... - - /** - * Print an IMAP Date string, that is suitable for the Date - * SEARCH commands. - * - * The IMAP Date string is : - * date ::= date_day "-" date_month "-" date_year - * - * Note that this format does not contain the TimeZone - */ - private static String monthTable[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - // A GregorianCalendar object in the current timezone - protected Calendar cal = new GregorianCalendar(); - - protected String toIMAPDate(Date date) { - StringBuffer s = new StringBuffer(); - - cal.setTime(date); - - s.append(cal.get(Calendar.DATE)).append("-"); - s.append(monthTable[cal.get(Calendar.MONTH)]).append('-'); - s.append(cal.get(Calendar.YEAR)); - - return s.toString(); - } - - protected Argument sentdate(DateTerm term) - throws SearchException { - Argument result = new Argument(); - String date = toIMAPDate(term.getDate()); - - switch (term.getComparison()) { - case ComparisonTerm.GT: - result.writeAtom("SENTSINCE " + date); - break; - case ComparisonTerm.EQ: - result.writeAtom("SENTON " + date); - break; - case ComparisonTerm.LT: - result.writeAtom("SENTBEFORE " + date); - break; - case ComparisonTerm.GE: - result.writeAtom("OR SENTSINCE " + date + " SENTON " + date); - break; - case ComparisonTerm.LE: - result.writeAtom("OR SENTBEFORE " + date + " SENTON " + date); - break; - case ComparisonTerm.NE: - result.writeAtom("NOT SENTON " + date); - break; - default: - throw new SearchException("Cannot handle Date Comparison"); - } - - return result; - } - - protected Argument receiveddate(DateTerm term) - throws SearchException { - Argument result = new Argument(); - String date = toIMAPDate(term.getDate()); - - switch (term.getComparison()) { - case ComparisonTerm.GT: - result.writeAtom("SINCE " + date); - break; - case ComparisonTerm.EQ: - result.writeAtom("ON " + date); - break; - case ComparisonTerm.LT: - result.writeAtom("BEFORE " + date); - break; - case ComparisonTerm.GE: - result.writeAtom("OR SINCE " + date + " ON " + date); - break; - case ComparisonTerm.LE: - result.writeAtom("OR BEFORE " + date + " ON " + date); - break; - case ComparisonTerm.NE: - result.writeAtom("NOT ON " + date); - break; - default: - throw new SearchException("Cannot handle Date Comparison"); - } - - return result; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/Status.java b/src/main/java/com/sun/mail/imap/protocol/Status.java deleted file mode 100644 index 58b21bc7..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/Status.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import com.sun.mail.iap.*; - -/** - * STATUS response. - * - * @author John Mani - */ - -public class Status { - public String mbox = null; - public int total = -1; - public int recent = -1; - public long uidnext = -1; - public long uidvalidity = -1; - public int unseen = -1; - - static final String[] standardItems = - { "MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY" }; - - public Status(Response r) throws ParsingException { - mbox = r.readAtomString(); // mailbox := astring - - // Workaround buggy IMAP servers that don't quote folder names - // with spaces. - final StringBuffer buffer = new StringBuffer(); - boolean onlySpaces = true; - - while (r.peekByte() != '(' && r.peekByte() != 0) { - final char next = (char)r.readByte(); - - buffer.append(next); - - if (next != ' ') { - onlySpaces = false; - } - } - - if (!onlySpaces) { - mbox = (mbox + buffer).trim(); - } - - if (r.readByte() != '(') - throw new ParsingException("parse error in STATUS"); - - do { - String attr = r.readAtom(); - if (attr.equalsIgnoreCase("MESSAGES")) - total = r.readNumber(); - else if (attr.equalsIgnoreCase("RECENT")) - recent = r.readNumber(); - else if (attr.equalsIgnoreCase("UIDNEXT")) - uidnext = r.readLong(); - else if (attr.equalsIgnoreCase("UIDVALIDITY")) - uidvalidity = r.readLong(); - else if (attr.equalsIgnoreCase("UNSEEN")) - unseen = r.readNumber(); - } while (r.readByte() != ')'); - } - - public static void add(Status s1, Status s2) { - if (s2.total != -1) - s1.total = s2.total; - if (s2.recent != -1) - s1.recent = s2.recent; - if (s2.uidnext != -1) - s1.uidnext = s2.uidnext; - if (s2.uidvalidity != -1) - s1.uidvalidity = s2.uidvalidity; - if (s2.unseen != -1) - s1.unseen = s2.unseen; - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/UID.java b/src/main/java/com/sun/mail/imap/protocol/UID.java deleted file mode 100644 index 83113e39..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/UID.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import com.sun.mail.iap.*; - -/** - * This class represents the UID data item - * - * @author John Mani - */ - -public class UID implements Item { - - static final char[] name = {'U','I','D'}; - public int seqnum; - - public long uid; - - /** - * Constructor - */ - public UID(FetchResponse r) throws ParsingException { - seqnum = r.getNumber(); - r.skipSpaces(); - uid = r.readLong(); - } -} diff --git a/src/main/java/com/sun/mail/imap/protocol/UIDSet.java b/src/main/java/com/sun/mail/imap/protocol/UIDSet.java deleted file mode 100644 index 961d4644..00000000 --- a/src/main/java/com/sun/mail/imap/protocol/UIDSet.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.imap.protocol; - -import java.util.Vector; - -/** - * This class holds the 'start' and 'end' for a range of UIDs. - * Just like MessageSet except using long instead of int. - */ -public class UIDSet { - - public long start; - public long end; - - public UIDSet() { } - - public UIDSet(long start, long end) { - this.start = start; - this.end = end; - } - - /** - * Count the total number of elements in a UIDSet - **/ - public long size() { - return end - start + 1; - } - - /* - * Convert an array of longs into an array of UIDSets - */ - public static UIDSet[] createUIDSets(long[] msgs) { - Vector v = new Vector(); - int i,j; - - for (i=0; i < msgs.length; i++) { - UIDSet ms = new UIDSet(); - ms.start = msgs[i]; - - // Look for contiguous elements - for (j=i+1; j < msgs.length; j++) { - if (msgs[j] != msgs[j-1] +1) - break; - } - ms.end = msgs[j-1]; - v.addElement(ms); - i = j-1; // i gets incremented @ top of the loop - } - UIDSet[] msgsets = new UIDSet[v.size()]; - v.copyInto(msgsets); - return msgsets; - } - - /** - * Convert an array of UIDSets into an IMAP sequence range - */ - public static String toString(UIDSet[] msgsets) { - if (msgsets == null || msgsets.length == 0) // Empty msgset - return null; - - int i = 0; // msgset index - StringBuffer s = new StringBuffer(); - int size = msgsets.length; - long start, end; - - for (;;) { - start = msgsets[i].start; - end = msgsets[i].end; - - if (end > start) - s.append(start).append(':').append(end); - else // end == start means only one element - s.append(start); - - i++; // Next UIDSet - if (i >= size) // No more UIDSets - break; - else - s.append(','); - } - return s.toString(); - } - - - /* - * Count the total number of elements in an array of UIDSets - */ - public static long size(UIDSet[] msgsets) { - long count = 0; - - if (msgsets == null) // Null msgset - return 0; - - for (int i=0; i < msgsets.length; i++) - count += msgsets[i].size(); - - return count; - } -} diff --git a/src/main/java/com/sun/mail/pop3/DefaultFolder.java b/src/main/java/com/sun/mail/pop3/DefaultFolder.java deleted file mode 100644 index 71a62991..00000000 --- a/src/main/java/com/sun/mail/pop3/DefaultFolder.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import javax.mail.*; - -/** - * The POP3 DefaultFolder. Only contains the "INBOX" folder. - * - * @author Christopher Cotton - */ -public class DefaultFolder extends Folder { - - DefaultFolder(POP3Store store) { - super(store); - } - - public String getName() { - return ""; - } - - public String getFullName() { - return ""; - } - - public Folder getParent() { - return null; - } - - public boolean exists() { - return true; - } - - public Folder[] list(String pattern) throws MessagingException { - Folder[] f = { getInbox() }; - return f; - } - - public char getSeparator() { - return '/'; - } - - public int getType() { - return HOLDS_FOLDERS; - } - - public boolean create(int type) throws MessagingException { - return false; - } - - public boolean hasNewMessages() throws MessagingException { - return false; - } - - public Folder getFolder(String name) throws MessagingException { - if (!name.equalsIgnoreCase("INBOX")) { - throw new MessagingException("only INBOX supported"); - } else { - return getInbox(); - } - } - - protected Folder getInbox() throws MessagingException { - return getStore().getFolder("INBOX"); - } - - - public boolean delete(boolean recurse) throws MessagingException { - throw new MethodNotSupportedException("delete"); - } - - public boolean renameTo(Folder f) throws MessagingException { - throw new MethodNotSupportedException("renameTo"); - } - - public void open(int mode) throws MessagingException { - throw new MethodNotSupportedException("open"); - } - - public void close(boolean expunge) throws MessagingException { - throw new MethodNotSupportedException("close"); - } - - public boolean isOpen() { - return false; - } - - public Flags getPermanentFlags() { - return new Flags(); // empty flags object - } - - public int getMessageCount() throws MessagingException { - return 0; - } - - public Message getMessage(int msgno) throws MessagingException { - throw new MethodNotSupportedException("getMessage"); - } - - public void appendMessages(Message[] msgs) throws MessagingException { - throw new MethodNotSupportedException("Append not supported"); - } - - public Message[] expunge() throws MessagingException { - throw new MethodNotSupportedException("expunge"); - } -} diff --git a/src/main/java/com/sun/mail/pop3/POP3Folder.java b/src/main/java/com/sun/mail/pop3/POP3Folder.java deleted file mode 100644 index 6e20e1a4..00000000 --- a/src/main/java/com/sun/mail/pop3/POP3Folder.java +++ /dev/null @@ -1,599 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import javax.mail.*; -import javax.mail.internet.*; -import javax.mail.event.*; -import java.io.InputStream; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.EOFException; -import java.util.Vector; -import java.util.StringTokenizer; -import java.util.logging.Level; -import java.lang.reflect.Constructor; - -import com.sun.mail.util.LineInputStream; -import com.sun.mail.util.MailLogger; - -/** - * A POP3 Folder (can only be "INBOX"). - * - * See the com.sun.mail.pop3 package - * documentation for further information on the POP3 protocol provider.

    - * - * @author Bill Shannon - * @author John Mani (ported to the javax.mail APIs) - */ -public class POP3Folder extends Folder { - - private String name; - private POP3Store store; - private volatile Protocol port; - private int total; - private int size; - private boolean exists = false; - private volatile boolean opened = false; - private Vector message_cache; - private boolean doneUidl = false; - private volatile TempFile fileCache = null; - - MailLogger logger; // package private, for POP3Message - - POP3Folder(POP3Store store, String name) { - super(store); - this.name = name; - this.store = store; - if (name.equalsIgnoreCase("INBOX")) - exists = true; - logger = new MailLogger(this.getClass(), - "DEBUG POP3", store.getSession()); - } - - public String getName() { - return name; - } - - public String getFullName() { - return name; - } - - public Folder getParent() { - return new DefaultFolder(store); - } - - /** - * Always true for the folder "INBOX", always false for - * any other name. - * - * @return true for INBOX, false otherwise - */ - public boolean exists() { - return exists; - } - - /** - * Always throws MessagingException because no POP3 folders - * can contain subfolders. - * - * @exception MessagingException always - */ - public Folder[] list(String pattern) throws MessagingException { - throw new MessagingException("not a directory"); - } - - /** - * Always returns a NUL character because POP3 doesn't support a hierarchy. - * - * @return NUL - */ - public char getSeparator() { - return '\0'; - } - - /** - * Always returns Folder.HOLDS_MESSAGES. - * - * @return Folder.HOLDS_MESSAGES - */ - public int getType() { - return HOLDS_MESSAGES; - } - - /** - * Always returns false; the POP3 protocol doesn't - * support creating folders. - * - * @return false - */ - public boolean create(int type) throws MessagingException { - return false; - } - - /** - * Always returns false; the POP3 protocol provides - * no way to determine when a new message arrives. - * - * @return false - */ - public boolean hasNewMessages() throws MessagingException { - return false; // no way to know - } - - /** - * Always throws MessagingException because no POP3 folders - * can contain subfolders. - * - * @exception MessagingException always - */ - public Folder getFolder(String name) throws MessagingException { - throw new MessagingException("not a directory"); - } - - /** - * Always throws MethodNotSupportedException - * because the POP3 protocol doesn't allow the INBOX to - * be deleted. - * - * @exception MethodNotSupportedException always - */ - public boolean delete(boolean recurse) throws MessagingException { - throw new MethodNotSupportedException("delete"); - } - - /** - * Always throws MethodNotSupportedException - * because the POP3 protocol doesn't support multiple folders. - * - * @exception MethodNotSupportedException always - */ - public boolean renameTo(Folder f) throws MessagingException { - throw new MethodNotSupportedException("renameTo"); - } - - /** - * Throws FolderNotFoundException unless this - * folder is named "INBOX". - * - * @exception FolderNotFoundException if not INBOX - * @exception AuthenticationException authentication failures - * @exception MessagingException other open failures - */ - public synchronized void open(int mode) throws MessagingException { - checkClosed(); - if (!exists) - throw new FolderNotFoundException(this, "folder is not INBOX"); - - try { - port = store.getPort(this); - Status s = port.stat(); - total = s.total; - size = s.size; - this.mode = mode; - if (store.useFileCache) { - try { - fileCache = new TempFile(store.fileCacheDir); - } catch (IOException ex) { - logger.log(Level.FINE, "failed to create file cache", ex); - throw ex; // caught below - } - } - opened = true; - } catch (IOException ioex) { - try { - if (port != null) - port.quit(); - } catch (IOException ioex2) { - // ignore - } finally { - port = null; - store.closePort(this); - } - throw new MessagingException("Open failed", ioex); - } - - // Create the message cache vector of appropriate size - message_cache = new Vector(total); - message_cache.setSize(total); - doneUidl = false; - - notifyConnectionListeners(ConnectionEvent.OPENED); - } - - public synchronized void close(boolean expunge) throws MessagingException { - checkOpen(); - - try { - /* - * Some POP3 servers will mark messages for deletion when - * they're read. To prevent such messages from being - * deleted before the client deletes them, you can set - * the mail.pop3.rsetbeforequit property to true. This - * causes us to issue a POP3 RSET command to clear all - * the "marked for deletion" flags. We can then explicitly - * delete messages as desired. - */ - if (store.rsetBeforeQuit) - port.rset(); - POP3Message m; - if (expunge && mode == READ_WRITE) { - // find all messages marked deleted and issue DELE commands - for (int i = 0; i < message_cache.size(); i++) { - if ((m = (POP3Message)message_cache.elementAt(i)) != null) { - if (m.isSet(Flags.Flag.DELETED)) - try { - port.dele(i + 1); - } catch (IOException ioex) { - throw new MessagingException( - "Exception deleting messages during close", - ioex); - } - } - } - } - - /* - * Flush and free all cached data for the messages. - */ - for (int i = 0; i < message_cache.size(); i++) { - if ((m = (POP3Message)message_cache.elementAt(i)) != null) - m.invalidate(true); - } - - port.quit(); - } catch (IOException ex) { - // do nothing - } finally { - port = null; - store.closePort(this); - message_cache = null; - opened = false; - notifyConnectionListeners(ConnectionEvent.CLOSED); - if (fileCache != null) { - fileCache.close(); - fileCache = null; - } - } - } - - public synchronized boolean isOpen() { - if (!opened) - return false; - try { - if (!port.noop()) - throw new IOException("NOOP failed"); - } catch (IOException ioex) { - try { - close(false); - } catch (MessagingException mex) { - // ignore it - } finally { - return false; - } - } - return true; - } - - /** - * Always returns an empty Flags object because - * the POP3 protocol doesn't support any permanent flags. - * - * @return empty Flags object - */ - public Flags getPermanentFlags() { - return new Flags(); // empty flags object - } - - /** - * Will not change while the folder is open because the POP3 - * protocol doesn't support notification of new messages - * arriving in open folders. - */ - public synchronized int getMessageCount() throws MessagingException { - if (!opened) - return -1; - checkReadable(); - return total; - } - - public synchronized Message getMessage(int msgno) - throws MessagingException { - checkOpen(); - - POP3Message m; - - // Assuming that msgno is <= total - if ((m = (POP3Message)message_cache.elementAt(msgno-1)) == null) { - m = createMessage(this, msgno); - message_cache.setElementAt(m, msgno-1); - } - return m; - } - - protected POP3Message createMessage(Folder f, int msgno) - throws MessagingException { - POP3Message m = null; - Constructor cons = store.messageConstructor; - if (cons != null) { - try { - Object[] o = { this, new Integer(msgno) }; - m = (POP3Message)cons.newInstance(o); - } catch (Exception ex) { - // ignore - } - } - if (m == null) - m = new POP3Message(this, msgno); - return m; - } - - /** - * Always throws MethodNotSupportedException - * because the POP3 protocol doesn't support appending messages. - * - * @exception MethodNotSupportedException always - */ - public void appendMessages(Message[] msgs) throws MessagingException { - throw new MethodNotSupportedException("Append not supported"); - } - - /** - * Always throws MethodNotSupportedException - * because the POP3 protocol doesn't support expunging messages - * without closing the folder; call the {@link #close close} method - * with the expunge argument set to true - * instead. - * - * @exception MethodNotSupportedException always - */ - public Message[] expunge() throws MessagingException { - throw new MethodNotSupportedException("Expunge not supported"); - } - - /** - * Prefetch information about POP3 messages. - * If the FetchProfile contains UIDFolder.FetchProfileItem.UID, - * POP3 UIDs for all messages in the folder are fetched using the POP3 - * UIDL command. - * If the FetchProfile contains FetchProfile.Item.ENVELOPE, - * the headers and size of all messages are fetched using the POP3 TOP - * and LIST commands. - */ - public synchronized void fetch(Message[] msgs, FetchProfile fp) - throws MessagingException { - checkReadable(); - if (!doneUidl && store.supportsUidl && - fp.contains(UIDFolder.FetchProfileItem.UID)) { - /* - * Since the POP3 protocol only lets us fetch the UID - * for a single message or for all messages, we go ahead - * and fetch UIDs for all messages here, ignoring the msgs - * parameter. We could be more intelligent and base this - * decision on the number of messages fetched, or the - * percentage of the total number of messages fetched. - */ - String[] uids = new String[message_cache.size()]; - try { - if (!port.uidl(uids)) - return; - } catch (EOFException eex) { - close(false); - throw new FolderClosedException(this, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error getting UIDL", ex); - } - for (int i = 0; i < uids.length; i++) { - if (uids[i] == null) - continue; - POP3Message m = (POP3Message)getMessage(i + 1); - m.uid = uids[i]; - } - doneUidl = true; // only do this once - } - if (fp.contains(FetchProfile.Item.ENVELOPE)) { - for (int i = 0; i < msgs.length; i++) { - try { - POP3Message msg = (POP3Message)msgs[i]; - // fetch headers - msg.getHeader(""); - // fetch message size - msg.getSize(); - } catch (MessageRemovedException mex) { - // should never happen, but ignore it if it does - } - } - } - } - - /** - * Return the unique ID string for this message, or null if - * not available. Uses the POP3 UIDL command. - * - * @return unique ID string - * @exception MessagingException - */ - public synchronized String getUID(Message msg) throws MessagingException { - checkOpen(); - POP3Message m = (POP3Message)msg; - try { - if (!store.supportsUidl) - return null; - if (m.uid == POP3Message.UNKNOWN) - m.uid = port.uidl(m.getMessageNumber()); - return m.uid; - } catch (EOFException eex) { - close(false); - throw new FolderClosedException(this, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error getting UIDL", ex); - } - } - - /** - * Return the size of this folder, as was returned by the POP3 STAT - * command when this folder was opened. - * - * @return folder size - * @exception IllegalStateException if the folder isn't open - */ - public synchronized int getSize() throws MessagingException { - checkOpen(); - return size; - } - - /** - * Return the sizes of all messages in this folder, as returned - * by the POP3 LIST command. Each entry in the array corresponds - * to a message; entry i corresponds to message number i+1. - * - * @return array of message sizes - * @exception IllegalStateException if the folder isn't open - * @since JavaMail 1.3.3 - */ - public synchronized int[] getSizes() throws MessagingException { - checkOpen(); - int sizes[] = new int[total]; - InputStream is = null; - LineInputStream lis = null; - try { - is = port.list(); - lis = new LineInputStream(is); - String line; - while ((line = lis.readLine()) != null) { - try { - StringTokenizer st = new StringTokenizer(line); - int msgnum = Integer.parseInt(st.nextToken()); - int size = Integer.parseInt(st.nextToken()); - if (msgnum > 0 && msgnum <= total) - sizes[msgnum - 1] = size; - } catch (Exception e) { - } - } - } catch (IOException ex) { - // ignore it? - } finally { - try { - if (lis != null) - lis.close(); - } catch (IOException cex) { } - try { - if (is != null) - is.close(); - } catch (IOException cex) { } - } - return sizes; - } - - /** - * Return the raw results of the POP3 LIST command with no arguments. - * - * @return InputStream containing results - * @exception IllegalStateException if the folder isn't open - * @since JavaMail 1.3.3 - */ - public synchronized InputStream listCommand() - throws MessagingException, IOException { - checkOpen(); - return port.list(); - } - - /** - * Close the folder when we're finalized. - */ - protected void finalize() throws Throwable { - super.finalize(); - close(false); - } - - /* Ensure the folder is open */ - private void checkOpen() throws IllegalStateException { - if (!opened) - throw new IllegalStateException("Folder is not Open"); - } - - /* Ensure the folder is not open */ - private void checkClosed() throws IllegalStateException { - if (opened) - throw new IllegalStateException("Folder is Open"); - } - - /* Ensure the folder is open & readable */ - private void checkReadable() throws IllegalStateException { - if (!opened || (mode != READ_ONLY && mode != READ_WRITE)) - throw new IllegalStateException("Folder is not Readable"); - } - - /* Ensure the folder is open & writable */ - /* - private void checkWritable() throws IllegalStateException { - if (!opened || mode != READ_WRITE) - throw new IllegalStateException("Folder is not Writable"); - } - */ - - /** - * Centralize access to the Protocol object by POP3Message - * objects so that they will fail appropriately when the folder - * is closed. - */ - Protocol getProtocol() throws MessagingException { - Protocol p = port; // read it before close() can set it to null - checkOpen(); - // close() might happen here - return p; - } - - /* - * Only here to make accessible to POP3Message. - */ - protected void notifyMessageChangedListeners(int type, Message m) { - super.notifyMessageChangedListeners(type, m); - } - - /** - * Used by POP3Message. - */ - TempFile getFileCache() { - return fileCache; - } -} diff --git a/src/main/java/com/sun/mail/pop3/POP3Message.java b/src/main/java/com/sun/mail/pop3/POP3Message.java deleted file mode 100644 index f69d1650..00000000 --- a/src/main/java/com/sun/mail/pop3/POP3Message.java +++ /dev/null @@ -1,648 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import java.io.*; -import java.util.Enumeration; -import java.util.logging.Level; -import java.lang.ref.SoftReference; -import javax.mail.*; -import javax.mail.internet.*; -import javax.mail.event.*; -import com.sun.mail.util.ReadableMime; - -/** - * A POP3 Message. Just like a MimeMessage except that - * some things are not supported. - * - * @author Bill Shannon - */ -public class POP3Message extends MimeMessage implements ReadableMime { - - /* - * Our locking strategy is to always lock the POP3Folder before the - * POP3Message so we have to be careful to drop our lock before calling - * back to the folder to close it and notify of connection lost events. - */ - - // flag to indicate we haven't tried to fetch the UID yet - static final String UNKNOWN = "UNKNOWN"; - - private POP3Folder folder; // overrides folder in MimeMessage - private int hdrSize = -1; - private int msgSize = -1; - String uid = UNKNOWN; // controlled by folder lock - - // rawData itself is never null - private SoftReference rawData = new SoftReference(null); - - public POP3Message(Folder folder, int msgno) - throws MessagingException { - super(folder, msgno); - this.folder = (POP3Folder)folder; - } - - /** - * Set the specified flags on this message to the specified value. - * - * @param newFlags the flags to be set - * @param set the value to be set - */ - public synchronized void setFlags(Flags newFlags, boolean set) - throws MessagingException { - Flags oldFlags = (Flags)flags.clone(); - super.setFlags(newFlags, set); - if (!flags.equals(oldFlags)) - folder.notifyMessageChangedListeners( - MessageChangedEvent.FLAGS_CHANGED, this); - } - - /** - * Return the size of the content of this message in bytes. - * Returns -1 if the size cannot be determined.

    - * - * Note that this number may not be an exact measure of the - * content size and may or may not account for any transfer - * encoding of the content.

    - * - * @return size of content in bytes - * @exception MessagingException - */ - public int getSize() throws MessagingException { - try { - synchronized (this) { - // if we already have the size, return it - if (msgSize > 0) - return msgSize; - } - - /* - * Use LIST to determine the entire message - * size and subtract out the header size - * (which may involve loading the headers, - * which may load the content as a side effect). - * If the content is loaded as a side effect of - * loading the headers, it will set the size. - * - * Make sure to call loadHeaders() outside of the - * synchronization block. There's a potential race - * condition here but synchronization will occur in - * loadHeaders() to make sure the headers are only - * loaded once, and again in the following block to - * only compute msgSize once. - */ - if (headers == null) - loadHeaders(); - - synchronized (this) { - if (msgSize < 0) - msgSize = folder.getProtocol().list(msgnum) - hdrSize; - return msgSize; - } - } catch (EOFException eex) { - folder.close(false); - throw new FolderClosedException(folder, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error getting size", ex); - } - } - - /** - * Produce the raw bytes of the message. The data is fetched using - * the POP3 RETR command. If skipHeader is true, just the content - * is returned. - */ - private InputStream getRawStream(boolean skipHeader) - throws MessagingException { - InputStream rawcontent = null; - try { - synchronized(this) { - rawcontent = (InputStream)rawData.get(); - if (rawcontent == null) { - TempFile cache = folder.getFileCache(); - if (cache != null) { - Session s = ((POP3Store)(folder.getStore())).getSession(); - if (folder.logger.isLoggable(Level.FINE)) - folder.logger.fine("caching message #" + msgnum + - " in temp file"); - AppendStream os = cache.getAppendStream(); - BufferedOutputStream bos = new BufferedOutputStream(os); - try { - folder.getProtocol().retr(msgnum, bos); - } finally { - bos.close(); - } - rawcontent = os.getInputStream(); - } else { - rawcontent = folder.getProtocol().retr(msgnum, - msgSize > 0 ? msgSize + hdrSize : 0); - } - if (rawcontent == null) { - expunged = true; - throw new MessageRemovedException( - "can't retrieve message #" + msgnum + - " in POP3Message.getContentStream"); // XXX - what else? - } - - if (headers == null || - ((POP3Store)(folder.getStore())).forgetTopHeaders) { - headers = new InternetHeaders(rawcontent); - hdrSize = - (int)((SharedInputStream)rawcontent).getPosition(); - } else { - /* - * Already have the headers, have to skip the headers - * in the content array and return the body. - * - * XXX - It seems that some mail servers return slightly - * different headers in the RETR results than were returned - * in the TOP results, so we can't depend on remembering - * the size of the headers from the TOP command and just - * skipping that many bytes. Instead, we have to process - * the content, skipping over the header until we come to - * the empty line that separates the header from the body. - */ - int offset = 0; - for (;;) { - int len = 0; // number of bytes in this line - int c1; - while ((c1 = rawcontent.read()) >= 0) { - if (c1 == '\n') // end of line - break; - else if (c1 == '\r') { - // got CR, is the next char LF? - if (rawcontent.available() > 0) { - rawcontent.mark(1); - if (rawcontent.read() != '\n') - rawcontent.reset(); - } - break; // in any case, end of line - } - - // not CR, NL, or CRLF, count the byte - len++; - } - // here when end of line or out of data - - // if out of data, we're done - if (rawcontent.available() == 0) - break; - - // if it was an empty line, we're done - if (len == 0) - break; - } - hdrSize = - (int)((SharedInputStream)rawcontent).getPosition(); - } - - // skipped the header, the message is what's left - msgSize = rawcontent.available(); - - rawData = new SoftReference(rawcontent); - } - } - } catch (EOFException eex) { - folder.close(false); - throw new FolderClosedException(folder, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error fetching POP3 content", ex); - } - - /* - * We have a cached stream, but we need to return - * a fresh stream to read from the beginning and - * that can be safely closed. - */ - rawcontent = ((SharedInputStream)rawcontent).newStream( - skipHeader ? hdrSize : 0, -1); - return rawcontent; - } - - /** - * Produce the raw bytes of the content. The data is fetched using - * the POP3 RETR command. - * - * @see #contentStream - */ - protected synchronized InputStream getContentStream() - throws MessagingException { - if (contentStream != null) - return ((SharedInputStream)contentStream).newStream(0, -1); - - InputStream cstream = getRawStream(true); - - /* - * Keep a hard reference to the data if we're using a file - * cache or if the "mail.pop3.keepmessagecontent" prop is set. - */ - TempFile cache = folder.getFileCache(); - if (cache != null || - ((POP3Store)(folder.getStore())).keepMessageContent) - contentStream = ((SharedInputStream)cstream).newStream(0, -1); - return cstream; - } - - /** - * Return the MIME format stream corresponding to this message part. - * - * @return the MIME format stream - * @since JavaMail 1.4.5 - */ - public InputStream getMimeStream() throws MessagingException { - return getRawStream(false); - } - - /** - * Invalidate the cache of content for this message object, causing - * it to be fetched again from the server the next time it is needed. - * If invalidateHeaders is true, invalidate the headers - * as well. - * - * @param invalidateHeaders invalidate the headers as well? - */ - public synchronized void invalidate(boolean invalidateHeaders) { - content = null; - InputStream rstream = (InputStream)rawData.get(); - if (rstream != null) { - // note that if the content is in the file cache, it will be lost - // and fetched from the server if it's needed again - try { - rstream.close(); - } catch (IOException ex) { - // ignore it - } - rawData = new SoftReference(null); - } - if (contentStream != null) { - try { - contentStream.close(); - } catch (IOException ex) { - // ignore it - } - contentStream = null; - } - msgSize = -1; - if (invalidateHeaders) { - headers = null; - hdrSize = -1; - } - } - - /** - * Fetch the header of the message and the first n lines - * of the raw content of the message. The headers and data are - * available in the returned InputStream. - * - * @param n number of lines of content to fetch - * @return InputStream containing the message headers and n content lines - */ - public InputStream top(int n) throws MessagingException { - try { - synchronized (this) { - return folder.getProtocol().top(msgnum, n); - } - } catch (EOFException eex) { - folder.close(false); - throw new FolderClosedException(folder, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error getting size", ex); - } - } - - /** - * Get all the headers for this header_name. Note that certain - * headers may be encoded as per RFC 2047 if they contain - * non US-ASCII characters and these should be decoded.

    - * - * @param name name of header - * @return array of headers - * @exception MessagingException - * @see javax.mail.internet.MimeUtility - */ - public String[] getHeader(String name) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getHeader(name); - } - - /** - * Get all the headers for this header name, returned as a single - * String, with headers separated by the delimiter. If the - * delimiter is null, only the first header is - * returned. - * - * @param name the name of this header - * @param delimiter delimiter between returned headers - * @return the value fields for all headers with - * this name - * @exception MessagingException - */ - public String getHeader(String name, String delimiter) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getHeader(name, delimiter); - } - - /** - * Set the value for this header_name. Throws IllegalWriteException - * because POP3 messages are read-only. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - * @exception IllegalWriteException because the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public void setHeader(String name, String value) - throws MessagingException { - // XXX - should check for read-only folder? - throw new IllegalWriteException("POP3 messages are read-only"); - } - - /** - * Add this value to the existing values for this header_name. - * Throws IllegalWriteException because POP3 messages are read-only. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - * @exception IllegalWriteException because the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public void addHeader(String name, String value) - throws MessagingException { - // XXX - should check for read-only folder? - throw new IllegalWriteException("POP3 messages are read-only"); - } - - /** - * Remove all headers with this name. - * Throws IllegalWriteException because POP3 messages are read-only. - * - * @exception IllegalWriteException because the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public void removeHeader(String name) - throws MessagingException { - // XXX - should check for read-only folder? - throw new IllegalWriteException("POP3 messages are read-only"); - } - - /** - * Return all the headers from this Message as an enumeration - * of Header objects.

    - * - * Note that certain headers may be encoded as per RFC 2047 - * if they contain non US-ASCII characters and these should - * be decoded.

    - * - * @return array of header objects - * @exception MessagingException - * @see javax.mail.internet.MimeUtility - */ - public Enumeration getAllHeaders() throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getAllHeaders(); - } - - /** - * Return matching headers from this Message as an Enumeration of - * Header objects. - * - * @exception MessagingException - */ - public Enumeration getMatchingHeaders(String[] names) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getMatchingHeaders(names); - } - - /** - * Return non-matching headers from this Message as an - * Enumeration of Header objects. - * - * @exception MessagingException - */ - public Enumeration getNonMatchingHeaders(String[] names) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getNonMatchingHeaders(names); - } - - /** - * Add a raw RFC822 header-line. - * Throws IllegalWriteException because POP3 messages are read-only. - * - * @exception IllegalWriteException because the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public void addHeaderLine(String line) throws MessagingException { - // XXX - should check for read-only folder? - throw new IllegalWriteException("POP3 messages are read-only"); - } - - /** - * Get all header lines as an Enumeration of Strings. A Header - * line is a raw RFC822 header-line, containing both the "name" - * and "value" field. - * - * @exception MessagingException - */ - public Enumeration getAllHeaderLines() throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getAllHeaderLines(); - } - - /** - * Get matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC822 header-line, containing both - * the "name" and "value" field. - * - * @exception MessagingException - */ - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getMatchingHeaderLines(names); - } - - /** - * Get non-matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC822 header-line, containing both - * the "name" and "value" field. - * - * @exception MessagingException - */ - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException { - if (headers == null) - loadHeaders(); - return headers.getNonMatchingHeaderLines(names); - } - - /** - * POP3 message can't be changed. This method throws - * IllegalWriteException. - * - * @exception IllegalWriteException because the underlying - * implementation does not support modification - */ - public void saveChanges() throws MessagingException { - // POP3 Messages are read-only - throw new IllegalWriteException("POP3 messages are read-only"); - } - - /** - * Output the message as an RFC 822 format stream, without - * specified headers. If the property "mail.pop3.cachewriteto" - * is set to "true", and ignoreList is null, and the message hasn't - * already been cached as a side effect of other operations, the message - * content is cached before being written. Otherwise, the message is - * streamed directly to the output stream without being cached. - * - * @exception javax.mail.MessagingException - * @exception IOException if an error occurs writing to the stream - * or if an error is generated by the - * javax.activation layer. - * @see javax.activation.DataHandler#writeTo - */ - public synchronized void writeTo(OutputStream os, String[] ignoreList) - throws IOException, MessagingException { - InputStream rawcontent = (InputStream)rawData.get(); - if (rawcontent == null && ignoreList == null && - !((POP3Store)(folder.getStore())).cacheWriteTo) { - Session s = ((POP3Store)(folder.getStore())).getSession(); - if (folder.logger.isLoggable(Level.FINE)) - folder.logger.fine("streaming msg " + msgnum); - if (!folder.getProtocol().retr(msgnum, os)) { - expunged = true; - throw new MessageRemovedException("can't retrieve message #" + - msgnum + " in POP3Message.writeTo"); // XXX - what else? - } - } else if (rawcontent != null && ignoreList == null) { - // can just copy the cached data - InputStream in = ((SharedInputStream)rawcontent).newStream(0, -1); - try { - byte[] buf = new byte[16*1024]; - int len; - while ((len = in.read(buf)) > 0) - os.write(buf, 0, len); - } finally { - try { - if (in != null) - in.close(); - } catch (IOException ex) { } - } - } else - super.writeTo(os, ignoreList); - } - - /** - * Load the headers for this message into the InternetHeaders object. - * The headers are fetched using the POP3 TOP command. - */ - private void loadHeaders() throws MessagingException { - assert !Thread.holdsLock(this); - try { - boolean fetchContent = false; - synchronized (this) { - if (headers != null) // check again under lock - return; - InputStream hdrs = null; - if (((POP3Store)(folder.getStore())).disableTop || - (hdrs = folder.getProtocol().top(msgnum, 0)) == null) { - // possibly because the TOP command isn't supported, - // load headers as a side effect of loading the entire - // content. - fetchContent = true; - } else { - try { - hdrSize = hdrs.available(); - headers = new InternetHeaders(hdrs); - } finally { - hdrs.close(); - } - } - } - - /* - * Outside the synchronization block... - * - * Do we need to fetch the entire mesage content in order to - * load the headers as a side effect? Yes, there's a race - * condition here - multiple threads could decide that the - * content needs to be fetched. Fortunately, they'll all - * synchronize in the getContentStream method and the content - * will only be loaded once. - */ - if (fetchContent) { - InputStream cs = null; - try { - cs = getContentStream(); - } finally { - if (cs != null) - cs.close(); - } - } - } catch (EOFException eex) { - folder.close(false); - throw new FolderClosedException(folder, eex.toString()); - } catch (IOException ex) { - throw new MessagingException("error loading POP3 headers", ex); - } - } -} diff --git a/src/main/java/com/sun/mail/pop3/POP3SSLStore.java b/src/main/java/com/sun/mail/pop3/POP3SSLStore.java deleted file mode 100644 index 57bd23ed..00000000 --- a/src/main/java/com/sun/mail/pop3/POP3SSLStore.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import javax.mail.*; - -/** - * A POP3 Message Store using SSL. Contains only one folder, "INBOX". - * - * @author Bill Shannon - */ -public class POP3SSLStore extends POP3Store { - - public POP3SSLStore(Session session, URLName url) { - super(session, url, "pop3s", true); - } -} diff --git a/src/main/java/com/sun/mail/pop3/POP3Store.java b/src/main/java/com/sun/mail/pop3/POP3Store.java deleted file mode 100644 index e0e267c0..00000000 --- a/src/main/java/com/sun/mail/pop3/POP3Store.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import java.util.Properties; -import java.util.logging.Level; -import java.lang.reflect.*; - -import javax.mail.*; -import javax.mail.internet.*; -import java.io.File; -import java.io.PrintStream; -import java.io.IOException; -import java.io.EOFException; -import java.util.Collections; -import java.util.Map; - -import com.sun.mail.util.PropUtil; -import com.sun.mail.util.MailLogger; -import com.sun.mail.util.SocketConnectException; -import com.sun.mail.util.MailConnectException; - -/** - * A POP3 Message Store. Contains only one folder, "INBOX". - * - * See the com.sun.mail.pop3 package - * documentation for further information on the POP3 protocol provider.

    - * - * @author Bill Shannon - * @author John Mani - */ -public class POP3Store extends Store { - - private String name = "pop3"; // my protocol name - private int defaultPort = 110; // default POP3 port - private boolean isSSL = false; // use SSL? - - private Protocol port = null; // POP3 port for self - private POP3Folder portOwner = null; // folder owning port - private String host = null; // host - private int portNum = -1; - private String user = null; - private String passwd = null; - private boolean useStartTLS = false; - private boolean requireStartTLS = false; - private boolean usingSSL = false; - private Map capabilities; - private MailLogger logger; - - // following set here and accessed by other classes in this package - volatile Constructor messageConstructor = null; - volatile boolean rsetBeforeQuit = false; - volatile boolean disableTop = false; - volatile boolean forgetTopHeaders = false; - volatile boolean supportsUidl = true; - volatile boolean cacheWriteTo = false; - volatile boolean useFileCache = false; - volatile File fileCacheDir = null; - volatile boolean keepMessageContent = false; - - public POP3Store(Session session, URLName url) { - this(session, url, "pop3", false); - } - - public POP3Store(Session session, URLName url, - String name, boolean isSSL) { - super(session, url); - if (url != null) - name = url.getProtocol(); - this.name = name; - logger = new MailLogger(this.getClass(), - "DEBUG POP3", session); - - if (!isSSL) - isSSL = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".ssl.enable", false); - if (isSSL) - this.defaultPort = 995; - else - this.defaultPort = 110; - this.isSSL = isSSL; - - rsetBeforeQuit = getBoolProp("rsetbeforequit"); - disableTop = getBoolProp("disabletop"); - forgetTopHeaders = getBoolProp("forgettopheaders"); - cacheWriteTo = getBoolProp("cachewriteto"); - useFileCache = getBoolProp("filecache.enable"); - String dir = session.getProperty("mail." + name + ".filecache.dir"); - if (dir != null && logger.isLoggable(Level.CONFIG)) - logger.config("mail." + name + ".filecache.dir: " + dir); - if (dir != null) - fileCacheDir = new File(dir); - keepMessageContent = getBoolProp("keepmessagecontent"); - - // mail.pop3.starttls.enable enables use of STLS command - useStartTLS = getBoolProp("starttls.enable"); - - // mail.pop3.starttls.required requires use of STLS command - requireStartTLS = getBoolProp("starttls.required"); - - String s = session.getProperty("mail." + name + ".message.class"); - if (s != null) { - logger.log(Level.CONFIG, "message class: {0}", s); - try { - ClassLoader cl = this.getClass().getClassLoader(); - - // now load the class - Class messageClass = null; - try { - // First try the "application's" class loader. - // This should eventually be replaced by - // Thread.currentThread().getContextClassLoader(). - messageClass = Class.forName(s, false, cl); - } catch (ClassNotFoundException ex1) { - // That didn't work, now try the "system" class loader. - // (Need both of these because JDK 1.1 class loaders - // may not delegate to their parent class loader.) - messageClass = Class.forName(s); - } - - Class[] c = {javax.mail.Folder.class, int.class}; - messageConstructor = messageClass.getConstructor(c); - } catch (Exception ex) { - logger.log(Level.CONFIG, "failed to load message class", ex); - } - } - } - - /** - * Get the value of a boolean property. - * Print out the value if logging is enabled. - */ - private final synchronized boolean getBoolProp(String prop) { - prop = "mail." + name + "." + prop; - boolean val = PropUtil.getBooleanSessionProperty(session, prop, false); - if (logger.isLoggable(Level.CONFIG)) - logger.config(prop + ": " + val); - return val; - } - - /** - * Get a reference to the session. - */ - synchronized Session getSession() { - return session; - } - - protected synchronized boolean protocolConnect(String host, int portNum, - String user, String passwd) throws MessagingException { - - // check for non-null values of host, password, user - if (host == null || passwd == null || user == null) - return false; - - // if port is not specified, set it to value of mail.pop3.port - // property if it exists, otherwise default to 110 - if (portNum == -1) - portNum = PropUtil.getIntSessionProperty(session, - "mail." + name + ".port", -1); - - if (portNum == -1) - portNum = defaultPort; - - this.host = host; - this.portNum = portNum; - this.user = user; - this.passwd = passwd; - try { - port = getPort(null); - } catch (EOFException eex) { - throw new AuthenticationFailedException(eex.getMessage()); - } catch (SocketConnectException scex) { - throw new MailConnectException(scex); - } catch (IOException ioex) { - throw new MessagingException("Connect failed", ioex); - } - - return true; - } - - /** - * Check whether this store is connected. Override superclass - * method, to actually ping our server connection.

    - */ - /* - * Note that we maintain somewhat of an illusion of being connected - * even if we're not really connected. This is because a Folder - * can use the connection and close it when it's done. If we then - * ask whether the Store's connected we want the answer to be true, - * as long as we can reconnect at that point. This means that we - * need to be able to reconnect the Store on demand. - */ - public synchronized boolean isConnected() { - if (!super.isConnected()) - // if we haven't been connected at all, don't bother with - // the NOOP. - return false; - try { - if (port == null) - port = getPort(null); - else if (!port.noop()) - throw new IOException("NOOP failed"); - return true; - } catch (IOException ioex) { - // no longer connected, close it down - try { - super.close(); // notifies listeners - } catch (MessagingException mex) { - // ignore it - } finally { - return false; - } - } - } - - synchronized Protocol getPort(POP3Folder owner) throws IOException { - Protocol p; - - // if we already have a port, remember who's using it - if (port != null && portOwner == null) { - portOwner = owner; - return port; - } - - // need a new port, create it and try to login - p = new Protocol(host, portNum, logger, - session.getProperties(), "mail." + name, isSSL); - - if (useStartTLS || requireStartTLS) { - if (p.hasCapability("STLS")) { - if (p.stls()) { - // success, refresh capabilities - p.setCapabilities(p.capa()); - } else if (requireStartTLS) { - logger.fine("STLS required but failed"); - try { - p.quit(); - } catch (IOException ioex) { - } finally { - throw new EOFException("STLS required but failed"); - } - } - } else if (requireStartTLS) { - logger.fine("STLS required but not supported"); - try { - p.quit(); - } catch (IOException ioex) { - } finally { - throw new EOFException("STLS required but not supported"); - } - } - } - - capabilities = p.getCapabilities(); // save for later, may be null - usingSSL = p.isSSL(); // in case anyone asks - - /* - * If we haven't explicitly disabled use of the TOP command, - * and the server has provided its capabilities, - * and the server doesn't support the TOP command, - * disable the TOP command. - */ - if (!disableTop && - capabilities != null && !capabilities.containsKey("TOP")) { - disableTop = true; - logger.fine("server doesn't support TOP, disabling it"); - } - - supportsUidl = capabilities == null || capabilities.containsKey("UIDL"); - - String msg = null; - if ((msg = p.login(user, passwd)) != null) { - try { - p.quit(); - } catch (IOException ioex) { - } finally { - throw new EOFException(msg); - } - } - - /* - * If a Folder closes the port, and then a Folder - * is opened, the Store won't have a port. In that - * case, the getPort call will come from Folder.open, - * but we need to keep track of the port in the Store - * so that a later call to Folder.isOpen, which calls - * Store.isConnected, will use the same port. - */ - if (port == null && owner != null) { - port = p; - portOwner = owner; - } - if (portOwner == null) - portOwner = owner; - return p; - } - - synchronized void closePort(POP3Folder owner) { - if (portOwner == owner) { - port = null; - portOwner = null; - } - } - - public synchronized void close() throws MessagingException { - try { - if (port != null) - port.quit(); - } catch (IOException ioex) { - } finally { - port = null; - - // to set the state and send the closed connection event - super.close(); - } - } - - public Folder getDefaultFolder() throws MessagingException { - checkConnected(); - return new DefaultFolder(this); - } - - /** - * Only the name "INBOX" is supported. - */ - public Folder getFolder(String name) throws MessagingException { - checkConnected(); - return new POP3Folder(this, name); - } - - public Folder getFolder(URLName url) throws MessagingException { - checkConnected(); - return new POP3Folder(this, url.getFile()); - } - - /** - * Return a Map of the capabilities the server provided, - * as per RFC 2449. If the server doesn't support RFC 2449, - * an emtpy Map is returned. The returned Map can not be modified. - * The key to the Map is the upper case capability name as - * a String. The value of the entry is the entire String - * capability line returned by the server.

    - * - * For example, to check if the server supports the STLS capability, use: - * if (store.capabilities().containsKey("STLS")) ... - * - * @return Map of capabilities - * @since JavaMail 1.4.3 - */ - public Map capabilities() throws MessagingException { - Map c; - synchronized (this) { - c = capabilities; - } - if (c != null) - return Collections.unmodifiableMap(c); - else - return Collections.EMPTY_MAP; - } - - /** - * Is this POP3Store using SSL to connect to the server? - * - * @return true if using SSL - * @since JavaMail 1.4.6 - */ - public boolean isSSL() { - return usingSSL; - } - - protected void finalize() throws Throwable { - super.finalize(); - - if (port != null) // don't force a connection attempt - close(); - } - - private void checkConnected() throws MessagingException { - if (!super.isConnected()) - throw new MessagingException("Not connected"); - } -} diff --git a/src/main/java/com/sun/mail/pop3/Protocol.java b/src/main/java/com/sun/mail/pop3/Protocol.java deleted file mode 100644 index bb6cd697..00000000 --- a/src/main/java/com/sun/mail/pop3/Protocol.java +++ /dev/null @@ -1,847 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import java.util.*; -import java.net.*; -import java.io.*; -import java.security.*; -import java.util.logging.Level; -import javax.net.ssl.SSLSocket; - -import com.sun.mail.util.*; - -class Response { - boolean ok = false; // true if "+OK" - String data = null; // rest of line after "+OK" or "-ERR" - InputStream bytes = null; // all the bytes from a multi-line response -} - -/** - * This class provides a POP3 connection and implements - * the POP3 protocol requests. - * - * APOP support courtesy of "chamness". - * - * @author Bill Shannon - */ -class Protocol { - private Socket socket; // POP3 socket - private String host; // host we're connected to - private Properties props; // session properties - private String prefix; // protocol name prefix, for props - private DataInputStream input; // input buf - private PrintWriter output; // output buf - private TraceInputStream traceInput; - private TraceOutputStream traceOutput; - private MailLogger logger; - private MailLogger traceLogger; - private String apopChallenge = null; - private Map capabilities = null; - private boolean pipelining; - private boolean noauthdebug = true; // hide auth info in debug output - private boolean traceSuspended; // temporarily suspend tracing - - private static final int POP3_PORT = 110; // standard POP3 port - private static final String CRLF = "\r\n"; - // sometimes the returned size isn't quite big enough - private static final int SLOP = 128; - - /** - * Open a connection to the POP3 server. - */ - Protocol(String host, int port, MailLogger logger, - Properties props, String prefix, boolean isSSL) - throws IOException { - this.host = host; - this.props = props; - this.prefix = prefix; - this.logger = logger; - traceLogger = logger.getSubLogger("protocol", null); - noauthdebug = !PropUtil.getBooleanProperty(props, - "mail.debug.auth", false); - - Response r; - boolean enableAPOP = getBoolProp(props, prefix + ".apop.enable"); - boolean disableCapa = getBoolProp(props, prefix + ".disablecapa"); - try { - if (port == -1) - port = POP3_PORT; - if (logger.isLoggable(Level.FINE)) - logger.fine("connecting to host \"" + host + - "\", port " + port + ", isSSL " + isSSL); - - socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL); - initStreams(); - r = simpleCommand(null); - } catch (IOException ioe) { - try { - socket.close(); - } finally { - throw ioe; - } - } - - if (!r.ok) { - try { - socket.close(); - } finally { - throw new IOException("Connect failed"); - } - } - if (enableAPOP) { - int challStart = r.data.indexOf('<'); // start of challenge - int challEnd = r.data.indexOf('>', challStart); // end of challenge - if (challStart != -1 && challEnd != -1) - apopChallenge = r.data.substring(challStart, challEnd + 1); - logger.log(Level.FINE, "APOP challenge: {0}", apopChallenge); - } - - // if server supports RFC 2449, set capabilities - if (!disableCapa) - setCapabilities(capa()); - - pipelining = hasCapability("PIPELINING") || - PropUtil.getBooleanProperty(props, prefix + ".pipelining", false); - if (pipelining) - logger.config("PIPELINING enabled"); - } - - /** - * Get the value of a boolean property. - * Print out the value if logging is enabled. - */ - private final synchronized boolean getBoolProp(Properties props, - String prop) { - boolean val = PropUtil.getBooleanProperty(props, prop, false); - if (logger.isLoggable(Level.CONFIG)) - logger.config(prop + ": " + val); - return val; - } - - private void initStreams() throws IOException { - boolean quote = PropUtil.getBooleanProperty(props, - "mail.debug.quote", false); - traceInput = - new TraceInputStream(socket.getInputStream(), traceLogger); - traceInput.setQuote(quote); - - traceOutput = - new TraceOutputStream(socket.getOutputStream(), traceLogger); - traceOutput.setQuote(quote); - - input = new DataInputStream(new BufferedInputStream(traceInput)); - output = new PrintWriter( - new BufferedWriter( - new OutputStreamWriter(traceOutput, "iso-8859-1"))); - // should be US-ASCII, but not all JDK's support - } - - protected void finalize() throws Throwable { - super.finalize(); - if (socket != null) { // Forgot to logout ?! - quit(); - } - } - - /** - * Parse the capabilities from a CAPA response. - */ - synchronized void setCapabilities(InputStream in) { - if (in == null) { - capabilities = null; - return; - } - - capabilities = new HashMap(10); - BufferedReader r = null; - try { - r = new BufferedReader(new InputStreamReader(in, "us-ascii")); - } catch (UnsupportedEncodingException ex) { - // should never happen - assert false; - } - String s; - try { - while ((s = r.readLine()) != null) { - String cap = s; - int i = cap.indexOf(' '); - if (i > 0) - cap = cap.substring(0, i); - capabilities.put(cap.toUpperCase(Locale.ENGLISH), s); - } - } catch (IOException ex) { - // should never happen - } finally { - try { - in.close(); - } catch (IOException ex) { } - } - } - - /** - * Check whether the given capability is supported by - * this server. Returns true if so, otherwise - * returns false. - */ - synchronized boolean hasCapability(String c) { - return capabilities != null && - capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); - } - - /** - * Return the map of capabilities returned by the server. - */ - synchronized Map getCapabilities() { - return capabilities; - } - - /** - * Login to the server, using the USER and PASS commands. - */ - synchronized String login(String user, String password) - throws IOException { - Response r; - // only pipeline password if connection is secure - boolean batch = pipelining && socket instanceof SSLSocket; - - try { - - if (noauthdebug && isTracing()) { - logger.fine("authentication command trace suppressed"); - suspendTracing(); - } - String dpw = null; - if (apopChallenge != null) - dpw = getDigest(password); - if (apopChallenge != null && dpw != null) { - r = simpleCommand("APOP " + user + " " + dpw); - } else if (batch) { - String cmd = "USER " + user; - batchCommandStart(cmd); - issueCommand(cmd); - cmd = "PASS " + password; - batchCommandContinue(cmd); - issueCommand(cmd); - r = readResponse(); - if (!r.ok) { - String err = r.data != null ? r.data : "USER command failed"; - r = readResponse(); - batchCommandEnd(); - return err; - } - r = readResponse(); - batchCommandEnd(); - } else { - r = simpleCommand("USER " + user); - if (!r.ok) - return r.data != null ? r.data : "USER command failed"; - r = simpleCommand("PASS " + password); - } - if (noauthdebug && isTracing()) - logger.log(Level.FINE, "authentication command {0}", - (r.ok ? "succeeded" : "failed")); - if (!r.ok) - return r.data != null ? r.data : "login failed"; - return null; - - } finally { - resumeTracing(); - } - } - - /** - * Gets the APOP message digest. - * From RFC 1939: - * - * The 'digest' parameter is calculated by applying the MD5 - * algorithm [RFC1321] to a string consisting of the timestamp - * (including angle-brackets) followed by a shared secret. - * The 'digest' parameter itself is a 16-octet value which is - * sent in hexadecimal format, using lower-case ASCII characters. - * - * @param password The APOP password - * @return The APOP digest or an empty string if an error occurs. - */ - private String getDigest(String password) { - String key = apopChallenge + password; - byte[] digest; - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - digest = md.digest(key.getBytes("iso-8859-1")); // XXX - } catch (NoSuchAlgorithmException nsae) { - return null; - } catch (UnsupportedEncodingException uee) { - return null; - } - return toHex(digest); - } - - private static char[] digits = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - /** - * Convert a byte array to a string of hex digits representing the bytes. - */ - private static String toHex(byte[] bytes) { - char[] result = new char[bytes.length * 2]; - - for (int index = 0, i = 0; index < bytes.length; index++) { - int temp = bytes[index] & 0xFF; - result[i++] = digits[temp >> 4]; - result[i++] = digits[temp & 0xF]; - } - return new String(result); - } - - /** - * Close down the connection, sending the QUIT command. - */ - synchronized boolean quit() throws IOException { - boolean ok = false; - try { - Response r = simpleCommand("QUIT"); - ok = r.ok; - } finally { - try { - socket.close(); - } finally { - socket = null; - input = null; - output = null; - } - } - return ok; - } - - /** - * Return the total number of messages and mailbox size, - * using the STAT command. - */ - synchronized Status stat() throws IOException { - Response r = simpleCommand("STAT"); - Status s = new Status(); - - /* - * Normally the STAT command shouldn't fail but apparently it - * does when accessing Hotmail too often, returning: - * -ERR login allowed only every 15 minutes - * (Why it doesn't just fail the login, I don't know.) - * This is a serious failure that we don't want to hide - * from the user. - */ - if (!r.ok) - throw new IOException("STAT command failed: " + r.data); - - if (r.data != null) { - try { - StringTokenizer st = new StringTokenizer(r.data); - s.total = Integer.parseInt(st.nextToken()); - s.size = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - } - } - return s; - } - - /** - * Return the size of the message using the LIST command. - */ - synchronized int list(int msg) throws IOException { - Response r = simpleCommand("LIST " + msg); - int size = -1; - if (r.ok && r.data != null) { - try { - StringTokenizer st = new StringTokenizer(r.data); - st.nextToken(); // skip message number - size = Integer.parseInt(st.nextToken()); - } catch (Exception e) { - } - } - return size; - } - - /** - * Return the size of all messages using the LIST command. - */ - synchronized InputStream list() throws IOException { - Response r = multilineCommand("LIST", 128); // 128 == output size est - return r.bytes; - } - - /** - * Retrieve the specified message. - * Given an estimate of the message's size we can be more efficient, - * preallocating the array and returning a SharedInputStream to allow - * us to share the array. - */ - synchronized InputStream retr(int msg, int size) throws IOException { - Response r; - String cmd; - boolean batch = size == 0 && pipelining; - if (batch) { - cmd = "LIST " + msg; - batchCommandStart(cmd); - issueCommand(cmd); - cmd = "RETR " + msg; - batchCommandContinue(cmd); - issueCommand(cmd); - r = readResponse(); - if (r.ok && r.data != null) { - // parse the LIST response to get the message size - try { - StringTokenizer st = new StringTokenizer(r.data); - st.nextToken(); // skip message number - size = Integer.parseInt(st.nextToken()); - // don't allow ridiculous sizes - if (size > 1024*1024*1024 || size < 0) - size = 0; - else { - if (logger.isLoggable(Level.FINE)) - logger.fine("pipeline message size " + size); - size += SLOP; - } - } catch (Exception e) { - } - } - r = readResponse(); - if (r.ok) - r.bytes = readMultilineResponse(size + SLOP); - batchCommandEnd(); - } else { - cmd = "RETR " + msg; - multilineCommandStart(cmd); - issueCommand(cmd); - r = readResponse(); - if (!r.ok) { - multilineCommandEnd(); - return null; - } - - /* - * Many servers return a response to the RETR command of the form: - * +OK 832 octets - * If we don't have a size guess already, try to parse the response - * for data in that format and use it if found. It's only a guess, - * but it might be a good guess. - */ - if (size <= 0 && r.data != null) { - try { - StringTokenizer st = new StringTokenizer(r.data); - String s = st.nextToken(); - String octets = st.nextToken(); - if (octets.equals("octets")) { - size = Integer.parseInt(s); - // don't allow ridiculous sizes - if (size > 1024*1024*1024 || size < 0) - size = 0; - else { - if (logger.isLoggable(Level.FINE)) - logger.fine("guessing message size: " + size); - size += SLOP; - } - } - } catch (Exception e) { - } - } - r.bytes = readMultilineResponse(size); - multilineCommandEnd(); - } - if (r.ok) { - if (size > 0 && logger.isLoggable(Level.FINE)) - logger.fine("got message size " + r.bytes.available()); - } - return r.bytes; - } - - /** - * Retrieve the specified message and stream the content to the - * specified OutputStream. Return true on success. - */ - synchronized boolean retr(int msg, OutputStream os) throws IOException { - String cmd = "RETR " + msg; - multilineCommandStart(cmd); - issueCommand(cmd); - Response r = readResponse(); - if (!r.ok) { - multilineCommandEnd(); - return false; - } - - Throwable terr = null; - int b, lastb = '\n'; - try { - while ((b = input.read()) >= 0) { - if (lastb == '\n' && b == '.') { - b = input.read(); - if (b == '\r') { - // end of response, consume LF as well - b = input.read(); - break; - } - } - - /* - * Keep writing unless we get an error while writing, - * which we defer until all of the data has been read. - */ - if (terr == null) { - try { - os.write(b); - } catch (IOException ex) { - logger.log(Level.FINE, "exception while streaming", ex); - terr = ex; - } catch (RuntimeException ex) { - logger.log(Level.FINE, "exception while streaming", ex); - terr = ex; - } - } - lastb = b; - } - } catch (InterruptedIOException iioex) { - /* - * As above in simpleCommand, close the socket to recover. - */ - try { - socket.close(); - } catch (IOException cex) { } - throw iioex; - } - if (b < 0) - throw new EOFException("EOF on socket"); - - // was there a deferred error? - if (terr != null) { - if (terr instanceof IOException) - throw (IOException)terr; - if (terr instanceof RuntimeException) - throw (RuntimeException)terr; - assert false; // can't get here - } - multilineCommandEnd(); - return true; - } - - /** - * Return the message header and the first n lines of the message. - */ - synchronized InputStream top(int msg, int n) throws IOException { - Response r = multilineCommand("TOP " + msg + " " + n, 0); - return r.bytes; - } - - /** - * Delete (permanently) the specified message. - */ - synchronized boolean dele(int msg) throws IOException { - Response r = simpleCommand("DELE " + msg); - return r.ok; - } - - /** - * Return the UIDL string for the message. - */ - synchronized String uidl(int msg) throws IOException { - Response r = simpleCommand("UIDL " + msg); - if (!r.ok) - return null; - int i = r.data.indexOf(' '); - if (i > 0) - return r.data.substring(i + 1); - else - return null; - } - - /** - * Return the UIDL strings for all messages. - * The UID for msg #N is returned in uids[N-1]. - */ - synchronized boolean uidl(String[] uids) throws IOException { - Response r = multilineCommand("UIDL", 15 * uids.length); - if (!r.ok) - return false; - LineInputStream lis = new LineInputStream(r.bytes); - String line = null; - while ((line = lis.readLine()) != null) { - int i = line.indexOf(' '); - if (i < 1 || i >= line.length()) - continue; - int n = Integer.parseInt(line.substring(0, i)); - if (n > 0 && n <= uids.length) - uids[n - 1] = line.substring(i + 1); - } - try { - r.bytes.close(); - } catch (IOException ex) { } - return true; - } - - /** - * Do a NOOP. - */ - synchronized boolean noop() throws IOException { - Response r = simpleCommand("NOOP"); - return r.ok; - } - - /** - * Do an RSET. - */ - synchronized boolean rset() throws IOException { - Response r = simpleCommand("RSET"); - return r.ok; - } - - /** - * Start TLS using STLS command specified by RFC 2595. - * If already using SSL, this is a nop and the STLS command is not issued. - */ - synchronized boolean stls() throws IOException { - if (socket instanceof SSLSocket) - return true; // nothing to do - Response r = simpleCommand("STLS"); - if (r.ok) { - // it worked, now switch the socket into TLS mode - try { - socket = SocketFetcher.startTLS(socket, host, props, prefix); - initStreams(); - } catch (IOException ioex) { - try { - socket.close(); - } finally { - socket = null; - input = null; - output = null; - } - IOException sioex = - new IOException("Could not convert socket to TLS"); - sioex.initCause(ioex); - throw sioex; - } - } - return r.ok; - } - - /** - * Is this connection using SSL? - */ - synchronized boolean isSSL() { - return socket instanceof SSLSocket; - } - - /** - * Get server capabilities using CAPA command specified by RFC 2449. - * Returns null if not supported. - */ - synchronized InputStream capa() throws IOException { - Response r = multilineCommand("CAPA", 128); // 128 == output size est - if (!r.ok) - return null; - return r.bytes; - } - - /** - * Issue a simple POP3 command and return the response. - */ - private Response simpleCommand(String cmd) throws IOException { - simpleCommandStart(cmd); - issueCommand(cmd); - Response r = readResponse(); - simpleCommandEnd(); - return r; - } - - /** - * Send the specified command. - */ - private void issueCommand(String cmd) throws IOException { - if (socket == null) - throw new IOException("Folder is closed"); // XXX - - if (cmd != null) { - cmd += CRLF; - output.print(cmd); // do it in one write - output.flush(); - } - } - - /** - * Read the response to a command. - */ - private Response readResponse() throws IOException { - String line = null; - try { - line = input.readLine(); // XXX - readLine is deprecated - } catch (InterruptedIOException iioex) { - /* - * If we get a timeout while using the socket, we have no idea - * what state the connection is in. The server could still be - * alive, but slow, and could still be sending data. The only - * safe way to recover is to drop the connection. - */ - try { - socket.close(); - } catch (IOException cex) { } - throw new EOFException(iioex.getMessage()); - } catch (SocketException ex) { - /* - * If we get an error while using the socket, we have no idea - * what state the connection is in. The server could still be - * alive, but slow, and could still be sending data. The only - * safe way to recover is to drop the connection. - */ - try { - socket.close(); - } catch (IOException cex) { } - throw new EOFException(ex.getMessage()); - } - - if (line == null) { - traceLogger.finest(""); - throw new EOFException("EOF on socket"); - } - Response r = new Response(); - if (line.startsWith("+OK")) - r.ok = true; - else if (line.startsWith("-ERR")) - r.ok = false; - else - throw new IOException("Unexpected response: " + line); - int i; - if ((i = line.indexOf(' ')) >= 0) - r.data = line.substring(i + 1); - return r; - } - - /** - * Issue a POP3 command that expects a multi-line response. - * size is an estimate of the response size. - */ - private Response multilineCommand(String cmd, int size) throws IOException { - multilineCommandStart(cmd); - issueCommand(cmd); - Response r = readResponse(); - if (!r.ok) { - multilineCommandEnd(); - return r; - } - r.bytes = readMultilineResponse(size); - multilineCommandEnd(); - return r; - } - - /** - * Read the response to a multiline command after the command response. - * The size parameter indicates the expected size of the response; - * the actual size can be different. Returns an InputStream to the - * response bytes. - */ - private InputStream readMultilineResponse(int size) throws IOException { - SharedByteArrayOutputStream buf = new SharedByteArrayOutputStream(size); - int b, lastb = '\n'; - try { - while ((b = input.read()) >= 0) { - if (lastb == '\n' && b == '.') { - b = input.read(); - if (b == '\r') { - // end of response, consume LF as well - b = input.read(); - break; - } - } - buf.write(b); - lastb = b; - } - } catch (InterruptedIOException iioex) { - /* - * As above in readResponse, close the socket to recover. - */ - try { - socket.close(); - } catch (IOException cex) { } - throw iioex; - } - if (b < 0) - throw new EOFException("EOF on socket"); - return buf.toStream(); - } - - /** - * Is protocol tracing enabled? - */ - protected boolean isTracing() { - return traceLogger.isLoggable(Level.FINEST); - } - - /** - * Temporarily turn off protocol tracing, e.g., to prevent - * tracing the authentication sequence, including the password. - */ - private void suspendTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(false); - traceOutput.setTrace(false); - } - } - - /** - * Resume protocol tracing, if it was enabled to begin with. - */ - private void resumeTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(true); - traceOutput.setTrace(true); - } - } - - /* - * Probe points for GlassFish monitoring. - */ - private void simpleCommandStart(String command) { } - private void simpleCommandEnd() { } - private void multilineCommandStart(String command) { } - private void multilineCommandEnd() { } - private void batchCommandStart(String command) { } - private void batchCommandContinue(String command) { } - private void batchCommandEnd() { } -} diff --git a/src/main/java/com/sun/mail/pop3/Status.java b/src/main/java/com/sun/mail/pop3/Status.java deleted file mode 100644 index 309aa752..00000000 --- a/src/main/java/com/sun/mail/pop3/Status.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -/** - * Result of POP3 STAT command. - */ -class Status { - int total = 0; // number of messages in the mailbox - int size = 0; // size of the mailbox -}; diff --git a/src/main/java/com/sun/mail/pop3/TempFile.java b/src/main/java/com/sun/mail/pop3/TempFile.java deleted file mode 100644 index 73863d89..00000000 --- a/src/main/java/com/sun/mail/pop3/TempFile.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.pop3; - -import java.util.*; -import java.net.*; -import java.io.*; -import java.security.*; - -import com.sun.mail.util.PropUtil; -import javax.mail.util.SharedFileInputStream; - -/** - * A temporary file used to cache POP3 messages. - */ -class TempFile { - - private File file; // the temp file name - private WritableSharedFile sf; - - /** - * Create a temp file in the specified directory (if not null). - * The file will be deleted when the JVM exits. - */ - public TempFile(File dir) throws IOException { - file = File.createTempFile("pop3.", ".mbox", dir); - // XXX - need JDK 6 to set permissions on the file to owner-only - file.deleteOnExit(); - sf = new WritableSharedFile(file); - } - - /** - * Return a stream for appending to the temp file. - */ - public AppendStream getAppendStream() throws IOException { - return sf.getAppendStream(); - } - - /** - * Close and remove this temp file. - */ - public void close() { - try { - sf.close(); - } catch (IOException ex) { - // ignore it - } - file.delete(); - } - - protected void finalize() throws Throwable { - super.finalize(); - close(); - } -} - -/** - * A subclass of SharedFileInputStream that also allows writing. - */ -class WritableSharedFile extends SharedFileInputStream { - private RandomAccessFile raf; - private AppendStream af; - - public WritableSharedFile(File file) throws IOException { - super(file); - try { - raf = new RandomAccessFile(file, "rw"); - } catch (IOException ex) { - // if anything goes wrong opening the writable file, - // close the readable file too - super.close(); - } - } - - /** - * Return the writable version of this file. - */ - public RandomAccessFile getWritableFile() { - return raf; - } - - /** - * Close the readable and writable files. - */ - public void close() throws IOException { - try { - super.close(); - } finally { - raf.close(); - } - } - - /** - * Update the size of the readable file after writing - * to the file. Updates the length to be the current - * size of the file. - */ - synchronized long updateLength() throws IOException { - datalen = in.length(); - af = null; - return datalen; - } - - /** - * Return a new AppendStream, but only if one isn't in active use. - */ - public synchronized AppendStream getAppendStream() throws IOException { - if (af != null) - throw new IOException( - "POP3 file cache only supports single threaded access"); - af = new AppendStream(this); - return af; - } -} - -/** - * A stream for writing to the temp file, and when done - * can return a stream for reading the data just written. - * NOTE: We assume that only one thread is writing to the - * file at a time. - */ -class AppendStream extends OutputStream { - private final WritableSharedFile tf; - private RandomAccessFile raf; - private final long start; - private long end; - - public AppendStream(WritableSharedFile tf) throws IOException { - this.tf = tf; - raf = tf.getWritableFile(); - start = raf.length(); - raf.seek(start); - } - - public void write(int b) throws IOException { - raf.write(b); - } - - public void write(byte[] b) throws IOException { - raf.write(b); - } - - public void write(byte[] b, int off, int len) throws IOException { - raf.write(b, off, len); - } - - public synchronized void close() throws IOException { - end = tf.updateLength(); - raf = null; // no more writing allowed - } - - public synchronized InputStream getInputStream() throws IOException { - return tf.newStream(start, end); - } -} diff --git a/src/main/java/com/sun/mail/pop3/package.html b/src/main/java/com/sun/mail/pop3/package.html deleted file mode 100644 index a1eb497a..00000000 --- a/src/main/java/com/sun/mail/pop3/package.html +++ /dev/null @@ -1,607 +0,0 @@ - - - - - - - - -A POP3 protocol provider for the JavaMail API -that provides access to a POP3 message store. -Refer to -RFC 1939 -for more information. -

    -The POP3 provider provides a Store object that contains a single Folder -named "INBOX". Due to the limitations of the POP3 protocol, many of -the JavaMail API capabilities like event notification, folder management, -flag management, etc. are not allowed. The corresponding methods throw -the MethodNotSupportedException exception; see below for details. -

    -Note that JavaMail does not include a local store into -which messages can be downloaded and stored. See our - -Third Party Products -web page for availability of "mbox" and "MH" local store providers. -

    -The POP3 provider is accessed through the JavaMail APIs by using the protocol -name "pop3" or a URL of the form "pop3://user:password@host:port/INBOX". -

    -POP3 supports only a single folder named "INBOX". -

    -POP3 supports no permanent flags (see -{@link javax.mail.Folder#getPermanentFlags Folder.getPermanentFlags()}). -In particular, the Flags.Flag.RECENT flag will never be set -for POP3 -messages. It's up to the application to determine which messages in a -POP3 mailbox are "new". There are several strategies to accomplish -this, depending on the needs of the application and the environment: -

    -

      -
    • -A simple approach would be to keep track of the newest -message seen by the application. -
    • -
    • -An alternative would be to keep track of the UIDs (see below) -of all messages that have been seen. -
    • -
    • -Another approach is to download all messages into a local -mailbox, so that all messages in the POP3 mailbox are, by -definition, new. -
    • -
    -

    -All approaches will require some permanent storage associated with the client. -

    -POP3 does not support the Folder.expunge() method. To delete and -expunge messages, set the Flags.Flag.DELETED flag on the messages -and close the folder using the Folder.close(true) method. You -cannot expunge without closing the folder. -

    -POP3 does not provide a "received date", so the getReceivedDate -method will return null. -It may be possible to examine other message headers (e.g., the -"Received" headers) to estimate the received date, but these techniques -are error-prone at best. -

    -The POP3 provider supports the POP3 UIDL command, see -{@link com.sun.mail.pop3.POP3Folder#getUID POP3Folder.getUID()}. -You can use it as follows: -

    -

    -if (folder instanceof com.sun.mail.pop3.POP3Folder) {
    -    com.sun.mail.pop3.POP3Folder pf =
    -	(com.sun.mail.pop3.POP3Folder)folder;
    -    String uid = pf.getUID(msg);
    -    if (uid != null)
    -	... // use it
    -}
    -
    -

    -You can also pre-fetch all the UIDs for all messages like this: -

    -

    -FetchProfile fp = new FetchProfile();
    -fp.add(UIDFolder.FetchProfileItem.UID);
    -folder.fetch(folder.getMessages(), fp);
    -
    -

    -Then use the technique above to get the UID for each message. This is -similar to the technique used with the UIDFolder interface supported by -IMAP, but note that POP3 UIDs are strings, not integers like IMAP -UIDs. See the POP3 spec for details. -

    -When the headers of a POP3 message are accessed, the POP3 provider uses -the TOP command to fetch all headers, which are then cached. Use of the -TOP command can be disabled with the mail.pop3.disabletop -property, in which case the entire message content is fetched with the -RETR command. -

    -When the content of a POP3 message is accessed, the POP3 provider uses -the RETR command to fetch the entire message. Normally the message -content is cached in memory. By setting the -mail.pop3.filecache.enable property, the message content -will instead be cached in a temporary file. The file will be removed -when the folder is closed. Caching message content in a file is generally -slower, but uses substantially less memory and may be helpful when dealing -with very large messages. -

    -The {@link com.sun.mail.pop3.POP3Message#invalidate POP3Message.invalidate} -method can be used to invalidate cached data without closing the folder. -Note that if the file cache is being used the data in the file will be -forgotten and fetched from the server if it's needed again, and stored again -in the file cache. -

    -The POP3 CAPA command (defined by -RFC 2449) -will be used to determine the capabilities supported by the server. -Some servers don't implement the CAPA command, and some servers don't -return correct information, so various properties are available to -disable use of certain POP3 commands, including CAPA. -

    -If the server advertises the PIPELINING capability (defined by -RFC 2449), -or the mail.pop3.pipelining property is set, the POP3 -provider will send some commands in batches, which can significantly -improve performance and memory use. -Some servers that don't support the CAPA command or don't advertise -PIPELINING may still support pipelining; experimentation may be required. -

    -If pipelining is supported and the connection is using -SSL, the USER and PASS commands will be sent as a batch. -(If SSL is not being used, the PASS command isn't sent -until the user is verified to avoid exposing the password -if the user name is bad.) -

    -If pipelining is supported, when fetching a message with the RETR command, -the LIST command will be sent as well, and the result will be used to size -the I/O buffer, greatly reducing memory usage when fetching messages. -

    -The POP3 protocol provider supports the following properties, -which may be set in the JavaMail Session object. -The properties are always set as strings; the Type column describes -how the string is interpreted. For example, use -

    -	props.put("mail.pop3.port", "888");
    -
    -to set the mail.pop3.port property, which is of type int. -

    -Note that if you're using the "pop3s" protocol to access POP3 over SSL, -all the properties would be named "mail.pop3s.*". -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    mail.pop3.userStringDefault user name for POP3.
    mail.pop3.hostStringThe POP3 server to connect to.
    mail.pop3.portintThe POP3 server port to connect to, if the connect() method doesn't -explicitly specify one. Defaults to 110.
    mail.pop3.connectiontimeoutintSocket connection timeout value in milliseconds. -Default is infinite timeout.
    mail.pop3.timeoutintSocket I/O timeout value in milliseconds. Default is infinite timeout.
    mail.pop3.rsetbeforequitboolean -Send a POP3 RSET command when closing the folder, before sending the -QUIT command. Useful with POP3 servers that implicitly mark all -messages that are read as "deleted"; this will prevent such messages -from being deleted and expunged unless the client requests so. Default -is false. -
    mail.pop3.message.classString -Class name of a subclass of com.sun.mail.pop3.POP3Message. -The subclass can be used to handle (for example) non-standard -Content-Type headers. The subclass must have a public constructor -of the form MyPOP3Message(Folder f, int msgno) -throws MessagingException. -
    mail.pop3.localaddressString -Local address (host name) to bind to when creating the POP3 socket. -Defaults to the address picked by the Socket class. -Should not normally need to be set, but useful with multi-homed hosts -where it's important to pick a particular local address to bind to. -
    mail.pop3.localportint -Local port number to bind to when creating the POP3 socket. -Defaults to the port number picked by the Socket class. -
    mail.pop3.apop.enableboolean -If set to true, use APOP instead of USER/PASS to login to the -POP3 server, if the POP3 server supports APOP. APOP sends a -digest of the password rather than the clear text password. -Defaults to false. -
    mail.pop3.socketFactorySocketFactory -If set to a class that implements the -javax.net.SocketFactory interface, this class -will be used to create POP3 sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.pop3.socketFactory.classString -If set, specifies the name of a class that implements the -javax.net.SocketFactory interface. This class -will be used to create POP3 sockets. -
    mail.pop3.socketFactory.fallbackboolean -If set to true, failure to create a socket using the specified -socket factory class will cause the socket to be created using -the java.net.Socket class. -Defaults to true. -
    mail.pop3.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.pop3.ssl.enableboolean -If set to true, use SSL to connect and use the SSL port by default. -Defaults to false for the "pop3" protocol and true for the "pop3s" protocol. -
    mail.pop3.ssl.checkserveridentityboolean -If set to true, check the server identity as specified by -RFC 2595. -These additional checks based on the content of the server's certificate -are intended to prevent man-in-the-middle attacks. -Defaults to false. -
    mail.pop3.ssl.trustString -If set, and a socket factory hasn't been specified, enables use of a -{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. -If set to "*", all hosts are trusted. -If set to a whitespace separated list of hosts, those hosts are trusted. -Otherwise, trust depends on the certificate the server presents. -
    mail.pop3.ssl.socketFactorySSLSocketFactory -If set to a class that extends the -javax.net.ssl.SSLSocketFactory class, this class -will be used to create POP3 SSL sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.pop3.ssl.socketFactory.classString -If set, specifies the name of a class that extends the -javax.net.ssl.SSLSocketFactory class. This class -will be used to create POP3 SSL sockets. -
    mail.pop3.ssl.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.pop3.ssl.protocolsstring -Specifies the SSL protocols that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledProtocols method. -
    mail.pop3.ssl.ciphersuitesstring -Specifies the SSL cipher suites that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. -
    mail.pop3.starttls.enableboolean -If true, enables the use of the STLS command (if -supported by the server) to switch the connection to a TLS-protected -connection before issuing any login commands. Note that an appropriate -trust store must configured so that the client will trust the server's -certificate. -Defaults to false. -
    mail.pop3.starttls.requiredboolean -If true, requires the use of the STLS command. -If the server doesn't support the STLS command, or the command -fails, the connect method will fail. -Defaults to false. -
    mail.pop3.socks.hoststring -Specifies the host name of a SOCKS5 proxy server that will be used for -connections to the mail server. -(Note that this only works on JDK 1.5 or newer.) -
    mail.pop3.socks.portstring -Specifies the port number for the SOCKS5 proxy server. -This should only need to be used if the proxy server is not using -the standard port number of 1080. -
    mail.pop3.disabletopboolean -If set to true, the POP3 TOP command will not be used to fetch -message headers. This is useful for POP3 servers that don't -properly implement the TOP command, or that provide incorrect -information in the TOP command results. -Defaults to false. -
    mail.pop3.disablecapaboolean -If set to true, the POP3 CAPA command will not be used to fetch -server capabilities. This is useful for POP3 servers that don't -properly implement the CAPA command, or that provide incorrect -information in the CAPA command results. -Defaults to false. -
    mail.pop3.forgettopheadersboolean -If set to true, the headers that might have been retrieved using -the POP3 TOP command will be forgotten and replaced by headers -retrieved as part of the POP3 RETR command. Some servers, such -as some versions of Microsft Exchange and IBM Lotus Notes, -will return slightly different -headers each time the TOP or RETR command is used. To allow the -POP3 provider to properly parse the message content returned from -the RETR command, the headers also returned by the RETR command -must be used. Setting this property to true will cause these -headers to be used, even if they differ from the headers returned -previously as a result of using the TOP command. -Defaults to false. -
    mail.pop3.filecache.enableboolean -If set to true, the POP3 provider will cache message data in a temporary -file rather than in memory. Messages are only added to the cache when -accessing the message content. Message headers are always cached in -memory (on demand). The file cache is removed when the folder is closed -or the JVM terminates. -Defaults to false. -
    mail.pop3.filecache.dirString -If the file cache is enabled, this property can be used to override the -default directory used by the JDK for temporary files. -
    mail.pop3.cachewritetoboolean -Controls the behavior of the -{@link com.sun.mail.pop3.POP3Message#writeTo writeTo} method -on a POP3 message object. -If set to true, and the message content hasn't yet been cached, -and ignoreList is null, the message is cached before being written. -Otherwise, the message is streamed directly -to the output stream without being cached. -Defaults to false. -
    mail.pop3.keepmessagecontentboolean -The content of a message is cached when it is first fetched. -Normally this cache uses a {@link java.lang.ref.SoftReference SoftReference} -to refer to the cached content. This allows the cached content to be purged -if memory is low, in which case the content will be fetched again if it's -needed. -If this property is set to true, a hard reference to the cached content -will be kept, preventing the memory from being reused until the folder -is closed or the cached content is explicitly invalidated (using the -{@link com.sun.mail.pop3.POP3Message#invalidate invalidate} method). -(This was the behavior in previous versions of JavaMail.) -Defaults to false. -
    -

    -In general, applications should not need to use the classes in this -package directly. Instead, they should use the APIs defined by -javax.mail package (and subpackages). Applications should -never construct instances of POP3Store or -POP3Folder directly. Instead, they should use the -Session method getStore to acquire an -appropriate Store object, and from that acquire -Folder objects. -

    -In addition to printing debugging output as controlled by the -{@link javax.mail.Session Session} configuration, -the com.sun.mail.pop3 provider logs the same information using -{@link java.util.logging.Logger} as described in the following table: -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    Logger NameLogging LevelPurpose
    com.sun.mail.pop3CONFIGConfiguration of the POP3Store
    com.sun.mail.pop3FINEGeneral debugging output
    com.sun.mail.pop3.protocolFINESTComplete protocol trace
    - -

    -WARNING: The APIs unique to this package should be -considered EXPERIMENTAL. They may be changed in the -future in ways that are incompatible with applications using the -current APIs. - - - diff --git a/src/main/java/com/sun/mail/smtp/DigestMD5.java b/src/main/java/com/sun/mail/smtp/DigestMD5.java deleted file mode 100644 index 85cdd54d..00000000 --- a/src/main/java/com/sun/mail/smtp/DigestMD5.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import java.io.*; -import java.util.*; -import java.util.logging.Level; -import java.security.*; - -import com.sun.mail.util.*; - -/** - * DIGEST-MD5 authentication support. - * - * @author Dean Gibson - * @author Bill Shannon - */ - -public class DigestMD5 { - - private MailLogger logger; - private MessageDigest md5; - private String uri; - private String clientResponse; - - public DigestMD5(MailLogger logger) { - this.logger = logger.getLogger(this.getClass(), "DEBUG DIGEST-MD5"); - logger.config("DIGEST-MD5 Loaded"); - } - - /** - * Return client's authentication response to server's challenge. - * - * @return byte array with client's response - */ - public byte[] authClient(String host, String user, String passwd, - String realm, String serverChallenge) - throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); - SecureRandom random; - try { - //random = SecureRandom.getInstance("SHA1PRNG"); - random = new SecureRandom(); - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ex) { - logger.log(Level.FINE, "NoSuchAlgorithmException", ex); - throw new IOException(ex.toString()); - } - StringBuffer result = new StringBuffer(); - - uri = "smtp/" + host; - String nc = "00000001"; - String qop = "auth"; - byte[] bytes = new byte[32]; // arbitrary size ... - int resp; - - logger.fine("Begin authentication ..."); - - // Code based on http://www.ietf.org/rfc/rfc2831.txt - Hashtable map = tokenize(serverChallenge); - - if (realm == null) { - String text = (String)map.get("realm"); - realm = text != null ? new StringTokenizer(text, ",").nextToken() - : host; - } - - // server challenge random value - String nonce = (String)map.get("nonce"); - - random.nextBytes(bytes); - b64os.write(bytes); - b64os.flush(); - - // client challenge random value - String cnonce = bos.toString("iso-8859-1"); // really ASCII? - bos.reset(); - - // DIGEST-MD5 computation, common portion (order critical) - md5.update(md5.digest( - ASCIIUtility.getBytes(user + ":" + realm + ":" + passwd))); - md5.update(ASCIIUtility.getBytes(":" + nonce + ":" + cnonce)); - clientResponse = toHex(md5.digest()) - + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":"; - - // DIGEST-MD5 computation, client response (order critical) - md5.update(ASCIIUtility.getBytes("AUTHENTICATE:" + uri)); - md5.update(ASCIIUtility.getBytes(clientResponse + toHex(md5.digest()))); - - // build response text (order not critical) - result.append("username=\"" + user + "\""); - result.append(",realm=\"" + realm + "\""); - result.append(",qop=" + qop); - result.append(",nc=" + nc); - result.append(",nonce=\"" + nonce + "\""); - result.append(",cnonce=\"" + cnonce + "\""); - result.append(",digest-uri=\"" + uri + "\""); - result.append(",response=" + toHex(md5.digest())); - - if (logger.isLoggable(Level.FINE)) - logger.fine("Response => " + result.toString()); - b64os.write(ASCIIUtility.getBytes(result.toString())); - b64os.flush(); - return bos.toByteArray(); - } - - /** - * Allow the client to authenticate the server based on its - * response. - * - * @return true if server is authenticated - */ - public boolean authServer(String serverResponse) throws IOException { - Hashtable map = tokenize(serverResponse); - // DIGEST-MD5 computation, server response (order critical) - md5.update(ASCIIUtility.getBytes(":" + uri)); - md5.update(ASCIIUtility.getBytes(clientResponse + toHex(md5.digest()))); - String text = toHex(md5.digest()); - if (!text.equals((String)map.get("rspauth"))) { - if (logger.isLoggable(Level.FINE)) - logger.fine("Expected => rspauth=" + text); - return false; // server NOT authenticated by client !!! - } - return true; - } - - /** - * Tokenize a response from the server. - * - * @return Hashtable containing key/value pairs from server - */ - private Hashtable tokenize(String serverResponse) throws IOException { - Hashtable map = new Hashtable(); - byte[] bytes = serverResponse.getBytes("iso-8859-1"); // really ASCII? - String key = null; - int ttype; - StreamTokenizer tokens - = new StreamTokenizer( - new InputStreamReader( - new BASE64DecoderStream( - new ByteArrayInputStream(bytes, 4, bytes.length - 4) - ), "iso-8859-1" // really ASCII? - ) - ); - - tokens.ordinaryChars('0', '9'); // reset digits - tokens.wordChars('0', '9'); // digits may start words - while ((ttype = tokens.nextToken()) != StreamTokenizer.TT_EOF) { - switch (ttype) { - case StreamTokenizer.TT_WORD: - if (key == null) { - key = tokens.sval; - break; - } - // fall-thru - case '"': - if (logger.isLoggable(Level.FINE)) - logger.fine("Received => " + - key + "='" + tokens.sval + "'"); - if (map.containsKey(key)) { // concatenate multiple values - map.put(key, map.get(key) + "," + tokens.sval); - } else { - map.put(key, tokens.sval); - } - key = null; - break; - } - } - return map; - } - - private static char[] digits = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - /** - * Convert a byte array to a string of hex digits representing the bytes. - */ - private static String toHex(byte[] bytes) { - char[] result = new char[bytes.length * 2]; - - for (int index = 0, i = 0; index < bytes.length; index++) { - int temp = bytes[index] & 0xFF; - result[i++] = digits[temp >> 4]; - result[i++] = digits[temp & 0xF]; - } - return new String(result); - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java b/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java deleted file mode 100644 index 632604eb..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.SendFailedException; -import javax.mail.internet.InternetAddress; - -/** - * This exception is thrown when the message cannot be sent.

    - * - * The exception includes the address to which the message could not be - * sent. This will usually appear in a chained list of exceptions, - * one per address, attached to a top level SendFailedException that - * aggregates all the addresses. - * - * @since JavaMail 1.3.2 - */ - -public class SMTPAddressFailedException extends SendFailedException { - protected InternetAddress addr; // address that failed - protected String cmd; // command issued to server - protected int rc; // return code from SMTP server - - private static final long serialVersionUID = 804831199768630097L; - - /** - * Constructs an SMTPAddressFailedException with the specified - * address, return code, and error string. - * - * @param addr the address that failed - * @param cmd the command that was sent to the SMTP server - * @param rc the SMTP return code indicating the failure - * @param err the error string from the SMTP server - */ - public SMTPAddressFailedException(InternetAddress addr, String cmd, int rc, - String err) { - super(err); - this.addr = addr; - this.cmd = cmd; - this.rc = rc; - } - - /** - * Return the address that failed. - */ - public InternetAddress getAddress() { - return addr; - } - - /** - * Return the command that failed. - */ - public String getCommand() { - return cmd; - } - - - /** - * Return the return code from the SMTP server that indicates the - * reason for the failure. See - * RFC 821 - * for interpretation of the return code. - */ - public int getReturnCode() { - return rc; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java b/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java deleted file mode 100644 index b569ed53..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.MessagingException; -import javax.mail.internet.InternetAddress; - -/** - * This exception is chained off a SendFailedException when the - * mail.smtp.reportsuccess property is true. It - * indicates an address to which the message was sent. The command - * will be an SMTP RCPT command and the return code will be the - * return code from that command. - * - * @since JavaMail 1.3.2 - */ - -public class SMTPAddressSucceededException extends MessagingException { - protected InternetAddress addr; // address that succeeded - protected String cmd; // command issued to server - protected int rc; // return code from SMTP server - - private static final long serialVersionUID = -1168335848623096749L; - - /** - * Constructs an SMTPAddressSucceededException with the specified - * address, return code, and error string. - * - * @param addr the address that succeeded - * @param cmd the command that was sent to the SMTP server - * @param rc the SMTP return code indicating the success - * @param err the error string from the SMTP server - */ - public SMTPAddressSucceededException(InternetAddress addr, - String cmd, int rc, String err) { - super(err); - this.addr = addr; - this.cmd = cmd; - this.rc = rc; - } - - /** - * Return the address that succeeded. - */ - public InternetAddress getAddress() { - return addr; - } - - /** - * Return the command that succeeded. - */ - public String getCommand() { - return cmd; - } - - - /** - * Return the return code from the SMTP server that indicates the - * reason for the success. See - * RFC 821 - * for interpretation of the return code. - */ - public int getReturnCode() { - return rc; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPMessage.java b/src/main/java/com/sun/mail/smtp/SMTPMessage.java deleted file mode 100644 index 859130da..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPMessage.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import java.io.*; -import javax.mail.*; -import javax.mail.internet.*; - -/** - * This class is a specialization of the MimeMessage class that allows - * you to specify various SMTP options and parameters that will be - * used when this message is sent over SMTP. Simply use this class - * instead of MimeMessage and set SMTP options using the methods on - * this class.

    - * - * See the com.sun.mail.smtp package - * documentation for further information on the SMTP protocol provider.

    - * - * @author Bill Shannon - * @see javax.mail.internet.MimeMessage - */ - -public class SMTPMessage extends MimeMessage { - - /** Never notify of delivery status */ - public static final int NOTIFY_NEVER = -1; - /** Notify of delivery success */ - public static final int NOTIFY_SUCCESS = 1; - /** Notify of delivery failure */ - public static final int NOTIFY_FAILURE = 2; - /** Notify of delivery delay */ - public static final int NOTIFY_DELAY = 4; - - /** Return full message with delivery status notification */ - public static final int RETURN_FULL = 1; - /** Return only message headers with delivery status notification */ - public static final int RETURN_HDRS = 2; - - private static final String[] returnOptionString = { null, "FULL", "HDRS" }; - - private String envelopeFrom; // the string to use in the MAIL FROM: command - private int notifyOptions = 0; - private int returnOption = 0; - private boolean sendPartial = false; - private boolean allow8bitMIME = false; - private String submitter = null; // RFC 2554 AUTH=submitter - private String extension = null; // extensions to use with MAIL command - - /** - * Default constructor. An empty message object is created. - * The headers field is set to an empty InternetHeaders - * object. The flags field is set to an empty Flags - * object. The modified flag is set to true. - */ - public SMTPMessage(Session session) { - super(session); - } - - /** - * Constructs an SMTPMessage by reading and parsing the data from the - * specified MIME InputStream. The InputStream will be left positioned - * at the end of the data for the message. Note that the input stream - * parse is done within this constructor itself. - * - * @param session Session object for this message - * @param is the message input stream - * @exception MessagingException - */ - public SMTPMessage(Session session, InputStream is) - throws MessagingException { - super(session, is); - } - - /** - * Constructs a new SMTPMessage with content initialized from the - * source MimeMessage. The new message is independent - * of the original.

    - * - * Note: The current implementation is rather inefficient, copying - * the data more times than strictly necessary. - * - * @param source the message to copy content from - * @exception MessagingException - */ - public SMTPMessage(MimeMessage source) throws MessagingException { - super(source); - } - - /** - * Set the From address to appear in the SMTP envelope. Note that this - * is different than the From address that appears in the message itself. - * The envelope From address is typically used when reporting errors. - * See RFC 821 for - * details.

    - * - * If set, overrides the mail.smtp.from property. - * - * @param from the envelope From address - */ - public void setEnvelopeFrom(String from) { - envelopeFrom = from; - } - - /** - * Return the envelope From address. - * - * @return the envelope From address, or null if not set - */ - public String getEnvelopeFrom() { - return envelopeFrom; - } - - /** - * Set notification options to be used if the server supports - * Delivery Status Notification - * (RFC 1891). - * Either NOTIFY_NEVER or some combination of - * NOTIFY_SUCCESS, NOTIFY_FAILURE, and - * NOTIFY_DELAY.

    - * - * If set, overrides the mail.smtp.dsn.notify property. - * - * @param options notification options - */ - public void setNotifyOptions(int options) { - if (options < -1 || options >= 8) - throw new IllegalArgumentException("Bad return option"); - notifyOptions = options; - } - - /** - * Get notification options. Returns zero if no options set. - * - * @return notification options - */ - public int getNotifyOptions() { - return notifyOptions; - } - - /** - * Return notification options as an RFC 1891 string. - * Returns null if no options set. - */ - String getDSNNotify() { - if (notifyOptions == 0) - return null; - if (notifyOptions == NOTIFY_NEVER) - return "NEVER"; - StringBuffer sb = new StringBuffer(); - if ((notifyOptions & NOTIFY_SUCCESS) != 0) - sb.append("SUCCESS"); - if ((notifyOptions & NOTIFY_FAILURE) != 0) { - if (sb.length() != 0) - sb.append(','); - sb.append("FAILURE"); - } - if ((notifyOptions & NOTIFY_DELAY) != 0) { - if (sb.length() != 0) - sb.append(','); - sb.append("DELAY"); - } - return sb.toString(); - } - - /** - * Set return option to be used if server supports - * Delivery Status Notification - * (RFC 1891). - * Either RETURN_FULL or RETURN_HDRS.

    - * - * If set, overrides the mail.smtp.dsn.ret property. - * - * @param option return option - */ - public void setReturnOption(int option) { - if (option < 0 || option > RETURN_HDRS) - throw new IllegalArgumentException("Bad return option"); - returnOption = option; - } - - /** - * Return return option. Returns zero if no option set. - * - * @return return option - */ - public int getReturnOption() { - return returnOption; - } - - /** - * Return return option as an RFC 1891 string. - * Returns null if no option set. - */ - String getDSNRet() { - return returnOptionString[returnOption]; - } - - /** - * If set to true, and the server supports the 8BITMIME extension, text - * parts of this message that use the "quoted-printable" or "base64" - * encodings are converted to use "8bit" encoding if they follow the - * RFC 2045 rules for 8bit text.

    - * - * If true, overrides the mail.smtp.allow8bitmime property. - * - * @param allow allow 8-bit flag - */ - public void setAllow8bitMIME(boolean allow) { - allow8bitMIME = allow; - } - - /** - * Is use of the 8BITMIME extension is allowed? - * - * @return allow 8-bit flag - */ - public boolean getAllow8bitMIME() { - return allow8bitMIME; - } - - /** - * If set to true, and this message has some valid and some invalid - * addresses, send the message anyway, reporting the partial failure with - * a SendFailedException. If set to false (the default), the message is - * not sent to any of the recipients if there is an invalid recipient - * address.

    - * - * If true, overrides the mail.smtp.sendpartial property. - * - * @param partial send partial flag - */ - public void setSendPartial(boolean partial) { - sendPartial = partial; - } - - /** - * Send message if some addresses are invalid? - * - * @return send partial flag - */ - public boolean getSendPartial() { - return sendPartial; - } - - /** - * Gets the submitter to be used for the RFC 2554 AUTH= value - * in the MAIL FROM command. - * - * @return the name of the submitter. - */ - public String getSubmitter() { - return submitter; - } - - /** - * Sets the submitter to be used for the RFC 2554 AUTH= value - * in the MAIL FROM command. Normally only used by a server - * that's relaying a message. Clients will typically not - * set a submitter. See - * RFC 2554 - * for details. - * - * @param submitter the name of the submitter - */ - public void setSubmitter(String submitter) { - this.submitter = submitter; - } - - /** - * Gets the extension string to use with the MAIL command. - * - * @return the extension string - * - * @since JavaMail 1.3.2 - */ - public String getMailExtension() { - return extension; - } - - /** - * Set the extension string to use with the MAIL command. - * The extension string can be used to specify standard SMTP - * service extensions as well as vendor-specific extensions. - * Typically the application should use the - * {@link com.sun.mail.smtp.SMTPTransport SMTPTransport} - * method {@link com.sun.mail.smtp.SMTPTransport#supportsExtension - * supportsExtension} - * to verify that the server supports the desired service extension. - * See RFC 1869 - * and other RFCs that define specific extensions.

    - * - * For example:

    - * - *

    -     * if (smtpTransport.supportsExtension("DELIVERBY"))
    -     *    smtpMsg.setMailExtension("BY=60;R");
    -     * 
    - * - * @since JavaMail 1.3.2 - */ - public void setMailExtension(String extension) { - this.extension = extension; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java b/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java deleted file mode 100644 index 0a4f04f9..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import java.io.*; -import com.sun.mail.util.CRLFOutputStream; - -/** - * In addition to converting lines into the canonical format, - * i.e., terminating lines with the CRLF sequence, escapes the "." - * by adding another "." to any "." that appears in the beginning - * of a line. See RFC821 section 4.5.2. - * - * @author Max Spivak - * @see CRLFOutputStream - */ -public class SMTPOutputStream extends CRLFOutputStream { - public SMTPOutputStream(OutputStream os) { - super(os); - } - - public void write(int b) throws IOException { - // if that last character was a newline, and the current - // character is ".", we always write out an extra ".". - if ((lastb == '\n' || lastb == '\r' || lastb == -1) && b == '.') { - out.write('.'); - } - - super.write(b); - } - - /* - * This method has been added to improve performance. - */ - public void write(byte b[], int off, int len) throws IOException { - int lastc = (lastb == -1) ? '\n' : lastb; - int start = off; - - len += off; - for (int i = off; i < len; i++) { - if ((lastc == '\n' || lastc == '\r') && b[i] == '.') { - super.write(b, start, i - start); - out.write('.'); - start = i; - } - lastc = b[i]; - } - if ((len - start) > 0) - super.write(b, start, len - start); - } - - /** - * Override flush method in FilterOutputStream. - * - * The MimeMessage writeTo method flushes its buffer at the end, - * but we don't want to flush data out to the socket until we've - * also written the terminating "\r\n.\r\n". - * - * We buffer nothing so there's nothing to flush. We depend - * on the fact that CRLFOutputStream also buffers nothing. - * SMTPTransport will manually flush the socket before reading - * the response. - */ - public void flush() { - // do nothing - } - - /** - * Ensure we're at the beginning of a line. - * Write CRLF if not. - */ - public void ensureAtBOL() throws IOException { - if (!atBOL) - super.writeln(); - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java b/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java deleted file mode 100644 index a86304fc..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.*; - -/** - * This class implements the Transport abstract class using SMTP - * over SSL for message submission and transport. - * - * @author Bill Shannon - */ - -public class SMTPSSLTransport extends SMTPTransport { - - /** Constructor */ - public SMTPSSLTransport(Session session, URLName urlname) { - super(session, urlname, "smtps", true); - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPSaslAuthenticator.java b/src/main/java/com/sun/mail/smtp/SMTPSaslAuthenticator.java deleted file mode 100644 index db0283ae..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPSaslAuthenticator.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import java.io.*; -import java.util.*; -import java.util.logging.Level; -import javax.security.sasl.*; -import javax.security.auth.callback.*; -import javax.mail.MessagingException; - -import com.sun.mail.util.*; - -/** - * This class contains a single method that does authentication using - * SASL. This is in a separate class so that it can be compiled with - * J2SE 1.5. Eventually it should be merged into SMTPTransport.java. - */ - -public class SMTPSaslAuthenticator implements SaslAuthenticator { - - private SMTPTransport pr; - private String name; - private Properties props; - private MailLogger logger; - private String host; - - public SMTPSaslAuthenticator(SMTPTransport pr, String name, - Properties props, MailLogger logger, String host) { - this.pr = pr; - this.name = name; - this.props = props; - this.logger = logger; - this.host = host; - } - - public boolean authenticate(String[] mechs, final String realm, - final String authzid, final String u, - final String p) throws MessagingException { - - boolean done = false; - if (logger.isLoggable(Level.FINE)) { - logger.fine("SASL Mechanisms:"); - for (int i = 0; i < mechs.length; i++) - logger.fine(" " + mechs[i]); - logger.fine(""); - } - - SaslClient sc; - CallbackHandler cbh = new CallbackHandler() { - public void handle(Callback[] callbacks) { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL callback length: " + callbacks.length); - for (int i = 0; i < callbacks.length; i++) { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL callback " + i + ": " + callbacks[i]); - if (callbacks[i] instanceof NameCallback) { - NameCallback ncb = (NameCallback)callbacks[i]; - ncb.setName(u); - } else if (callbacks[i] instanceof PasswordCallback) { - PasswordCallback pcb = (PasswordCallback)callbacks[i]; - pcb.setPassword(p.toCharArray()); - } else if (callbacks[i] instanceof RealmCallback) { - RealmCallback rcb = (RealmCallback)callbacks[i]; - rcb.setText(realm != null ? - realm : rcb.getDefaultText()); - } else if (callbacks[i] instanceof RealmChoiceCallback) { - RealmChoiceCallback rcb = - (RealmChoiceCallback)callbacks[i]; - if (realm == null) - rcb.setSelectedIndex(rcb.getDefaultChoice()); - else { - // need to find specified realm in list - String[] choices = rcb.getChoices(); - for (int k = 0; k < choices.length; k++) { - if (choices[k].equals(realm)) { - rcb.setSelectedIndex(k); - break; - } - } - } - } - } - } - }; - - try { - sc = Sasl.createSaslClient(mechs, authzid, name, host, - (Map)props, cbh); - } catch (SaslException sex) { - logger.log(Level.FINE, "Failed to create SASL client: ", sex); - return false; - } - if (sc == null) { - logger.fine("No SASL support"); - return false; - } - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL client " + sc.getMechanismName()); - - int resp; - try { - String mech = sc.getMechanismName(); - String ir = null; - if (sc.hasInitialResponse()) { - byte[] ba = sc.evaluateChallenge(new byte[0]); - ba = BASE64EncoderStream.encode(ba); - ir = ASCIIUtility.toString(ba, 0, ba.length); - } - if (ir != null) - resp = pr.simpleCommand("AUTH " + mech + " " + ir); - else - resp = pr.simpleCommand("AUTH " + mech); - - /* - * A 530 response indicates that the server wants us to - * issue a STARTTLS command first. Do that and try again. - */ - if (resp == 530) { - pr.startTLS(); - if (ir != null) - resp = pr.simpleCommand("AUTH " + mech + " " + ir); - else - resp = pr.simpleCommand("AUTH " + mech); - } - - if (resp == 235) - return true; // success already! - - if (resp != 334) - return false; - } catch (Exception ex) { - logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex); - return false; - } - - while (!done) { // loop till we are done - try { - if (resp == 334) { - byte[] ba = null; - if (!sc.isComplete()) { - ba = ASCIIUtility.getBytes(responseText(pr)); - if (ba.length > 0) - ba = BASE64DecoderStream.decode(ba); - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL challenge: " + - ASCIIUtility.toString(ba, 0, ba.length) + " :"); - ba = sc.evaluateChallenge(ba); - } - if (ba == null) { - logger.fine("SASL: no response"); - resp = pr.simpleCommand("*"); - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL response: " + - ASCIIUtility.toString(ba, 0, ba.length) + " :"); - ba = BASE64EncoderStream.encode(ba); - resp = pr.simpleCommand(ba); - } - } else - done = true; - } catch (Exception ioex) { - logger.log(Level.FINE, "SASL Exception", ioex); - done = true; - // XXX - ultimately return true??? - } - } - - if (sc.isComplete() /*&& res.status == SUCCESS*/) { - String qop = (String)sc.getNegotiatedProperty(Sasl.QOP); - if (qop != null && (qop.equalsIgnoreCase("auth-int") || - qop.equalsIgnoreCase("auth-conf"))) { - // XXX - NOT SUPPORTED!!! - logger.fine( - "SASL Mechanism requires integrity or confidentiality"); - return false; - } - } - - return true; - } - - private static final String responseText(SMTPTransport pr) { - String resp = pr.getLastServerResponse().trim(); - if (resp.length() > 4) - return resp.substring(4); - else - return ""; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java b/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java deleted file mode 100644 index 1df8f6a1..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.Address; -import javax.mail.SendFailedException; -import javax.mail.internet.InternetAddress; - -/** - * This exception is thrown when the message cannot be sent.

    - * - * This exception will usually appear first in a chained list of exceptions, - * followed by SMTPAddressFailedExceptions and/or - * SMTPAddressSucceededExceptions, * one per address. - * This exception corresponds to one of the SMTP commands used to - * send a message, such as the MAIL, DATA, and "end of data" commands, - * but not including the RCPT command. - * - * @since JavaMail 1.3.2 - */ - -public class SMTPSendFailedException extends SendFailedException { - protected InternetAddress addr; // address that failed - protected String cmd; // command issued to server - protected int rc; // return code from SMTP server - - private static final long serialVersionUID = 8049122628728932894L; - - /** - * Constructs an SMTPSendFailedException with the specified - * address, return code, and error string. - * - * @param cmd the command that was sent to the SMTP server - * @param rc the SMTP return code indicating the failure - * @param err the error string from the SMTP server - */ - public SMTPSendFailedException(String cmd, int rc, String err, Exception ex, - Address[] vs, Address[] vus, Address[] inv) { - super(err, ex, vs, vus, inv); - this.cmd = cmd; - this.rc = rc; - } - - /** - * Return the command that failed. - */ - public String getCommand() { - return cmd; - } - - /** - * Return the return code from the SMTP server that indicates the - * reason for the failure. See - * RFC 821 - * for interpretation of the return code. - */ - public int getReturnCode() { - return rc; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java b/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java deleted file mode 100644 index 7d3c1121..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.SendFailedException; -import javax.mail.internet.InternetAddress; - -/** - * This exception is thrown when the message cannot be sent.

    - * - * The exception includes the sender's address, which the mail server - * rejected. - * - * @since JavaMail 1.4.4 - */ - -public class SMTPSenderFailedException extends SendFailedException { - protected InternetAddress addr; // address that failed - protected String cmd; // command issued to server - protected int rc; // return code from SMTP server - - private static final long serialVersionUID = 514540454964476947L; - - /** - * Constructs an SMTPSenderFailedException with the specified - * address, return code, and error string. - * - * @param addr the address that failed - * @param cmd the command that was sent to the SMTP server - * @param rc the SMTP return code indicating the failure - * @param err the error string from the SMTP server - */ - public SMTPSenderFailedException(InternetAddress addr, String cmd, int rc, - String err) { - super(err); - this.addr = addr; - this.cmd = cmd; - this.rc = rc; - } - - /** - * Return the address that failed. - */ - public InternetAddress getAddress() { - return addr; - } - - /** - * Return the command that failed. - */ - public String getCommand() { - return cmd; - } - - - /** - * Return the return code from the SMTP server that indicates the - * reason for the failure. See - * RFC 821 - * for interpretation of the return code. - */ - public int getReturnCode() { - return rc; - } -} diff --git a/src/main/java/com/sun/mail/smtp/SMTPTransport.java b/src/main/java/com/sun/mail/smtp/SMTPTransport.java deleted file mode 100644 index 0cc75786..00000000 --- a/src/main/java/com/sun/mail/smtp/SMTPTransport.java +++ /dev/null @@ -1,2383 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.Level; -import java.lang.reflect.*; -import javax.net.ssl.SSLSocket; - -import javax.mail.*; -import javax.mail.event.*; -import javax.mail.internet.*; - -import com.sun.mail.util.*; -import com.sun.mail.auth.*; - -/** - * This class implements the Transport abstract class using SMTP for - * message submission and transport.

    - * - * See the com.sun.mail.smtp package - * documentation for further information on the SMTP protocol provider.

    - * - * This class includes many protected methods that allow a subclass to - * extend this class and add support for non-standard SMTP commands. - * The {@link #issueCommand} and {@link #sendCommand} methods can be - * used to send simple SMTP commands. Other methods such as the - * {@link #mailFrom} and {@link #data} methods can be overridden to - * insert new commands before or after the corresponding SMTP commands. - * For example, a subclass could do this to send the XACT command - * before sending the DATA command: - *

    - *	protected OutputStream data() throws MessagingException {
    - *	    if (supportsExtension("XACCOUNTING"))
    - *	        issueCommand("XACT", 25);
    - *	    return super.data();
    - *	}
    - * 
    - * - * @author Max Spivak - * @author Bill Shannon - * @author Dean Gibson (DIGEST-MD5 authentication) - * @author Lu\u00EDs Serralheiro (NTLM authentication) - * - * @see javax.mail.event.ConnectionEvent - * @see javax.mail.event.TransportEvent - */ - -public class SMTPTransport extends Transport { - - private String name = "smtp"; // Name of this protocol - private int defaultPort = 25; // default SMTP port - private boolean isSSL = false; // use SSL? - private String host; // host we're connected to - - // Following fields valid only during the sendMessage method. - private MimeMessage message; // Message to be sent - private Address[] addresses; // Addresses to which to send the msg - // Valid sent, valid unsent and invalid addresses - private Address[] validSentAddr, validUnsentAddr, invalidAddr; - // Did we send the message even though some addresses were invalid? - private boolean sendPartiallyFailed = false; - // If so, here's an exception we need to throw - private MessagingException exception; - // stream where message data is written - private SMTPOutputStream dataStream; - - // Map of SMTP service extensions supported by server, if EHLO used. - private Hashtable extMap; - - private Map authenticators = new HashMap(); - private String defaultAuthenticationMechanisms; // set in constructor - - private boolean quitWait = false; // true if we should wait - - private String saslRealm = UNKNOWN; - private String authorizationID = UNKNOWN; - private boolean enableSASL = false; // enable SASL authentication - private String[] saslMechanisms = UNKNOWN_SA; - - private String ntlmDomain = UNKNOWN; // for ntlm authentication - - private boolean reportSuccess; // throw an exception even on success - private boolean useStartTLS; // use STARTTLS command - private boolean requireStartTLS; // require STARTTLS command - private boolean useRset; // use RSET instead of NOOP - private boolean noopStrict = true; // NOOP must return 250 for success - - private MailLogger logger; // debug logger - private MailLogger traceLogger; // protocol trace logger - private String localHostName; // our own host name - private String lastServerResponse; // last SMTP response - private int lastReturnCode; // last SMTP return code - private boolean notificationDone; // only notify once per send - - private SaslAuthenticator saslAuthenticator; // if SASL is being used - - private boolean noauthdebug = true; // hide auth info in debug output - - /** Headers that should not be included when sending */ - private static final String[] ignoreList = { "Bcc", "Content-Length" }; - private static final byte[] CRLF = { (byte)'\r', (byte)'\n' }; - private static final String UNKNOWN = "UNKNOWN"; // place holder - private static final String[] UNKNOWN_SA = new String[0]; // place holder - - /** - * Constructor that takes a Session object and a URLName - * that represents a specific SMTP server. - */ - public SMTPTransport(Session session, URLName urlname) { - this(session, urlname, "smtp", false); - } - - /** - * Constructor used by this class and by SMTPSSLTransport subclass. - */ - protected SMTPTransport(Session session, URLName urlname, - String name, boolean isSSL) { - super(session, urlname); - logger = new MailLogger(this.getClass(), "DEBUG SMTP", session); - traceLogger = logger.getSubLogger("protocol", null); - noauthdebug = !PropUtil.getBooleanSessionProperty(session, - "mail.debug.auth", false); - if (urlname != null) - name = urlname.getProtocol(); - this.name = name; - if (!isSSL) - isSSL = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".ssl.enable", false); - if (isSSL) - this.defaultPort = 465; - else - this.defaultPort = 25; - this.isSSL = isSSL; - - // setting mail.smtp.quitwait to false causes us to not wait for the - // response from the QUIT command - quitWait = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".quitwait", true); - - // mail.smtp.reportsuccess causes us to throw an exception on success - reportSuccess = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".reportsuccess", false); - - // mail.smtp.starttls.enable enables use of STARTTLS command - useStartTLS = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".starttls.enable", false); - - // mail.smtp.starttls.required requires use of STARTTLS command - requireStartTLS = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".starttls.required", false); - - // mail.smtp.userset causes us to use RSET instead of NOOP - // for isConnected - useRset = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".userset", false); - - // mail.smtp.noop.strict requires 250 response to indicate success - noopStrict = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".noop.strict", true); - - // check if SASL is enabled - enableSASL = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".sasl.enable", false); - if (enableSASL) - logger.config("enable SASL"); - - // created here, because they're inner classes that reference "this" - Authenticator[] a = new Authenticator[] { - new LoginAuthenticator(), - new PlainAuthenticator(), - new DigestMD5Authenticator(), - new NtlmAuthenticator() - }; - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < a.length; i++) { - authenticators.put(a[i].getMechanism(), a[i]); - sb.append(a[i].getMechanism()).append(' '); - } - defaultAuthenticationMechanisms = sb.toString(); - } - - /** - * Get the name of the local host, for use in the EHLO and HELO commands. - * The property mail.smtp.localhost overrides mail.smtp.localaddress, - * which overrides what InetAddress would tell us. - */ - public synchronized String getLocalHost() { - // get our hostname and cache it for future use - if (localHostName == null || localHostName.length() <= 0) - localHostName = - session.getProperty("mail." + name + ".localhost"); - if (localHostName == null || localHostName.length() <= 0) - localHostName = - session.getProperty("mail." + name + ".localaddress"); - try { - if (localHostName == null || localHostName.length() <= 0) { - InetAddress localHost = InetAddress.getLocalHost(); - localHostName = localHost.getCanonicalHostName(); - // if we can't get our name, use local address literal - if (localHostName == null) - // XXX - not correct for IPv6 - localHostName = "[" + localHost.getHostAddress() + "]"; - } - } catch (UnknownHostException uhex) { - } - - // last chance, try to get our address from our socket - if (localHostName == null || localHostName.length() <= 0) { - if (serverSocket != null && serverSocket.isBound()) { - InetAddress localHost = serverSocket.getLocalAddress(); - localHostName = localHost.getCanonicalHostName(); - // if we can't get our name, use local address literal - if (localHostName == null) - // XXX - not correct for IPv6 - localHostName = "[" + localHost.getHostAddress() + "]"; - } - } - return localHostName; - } - - /** - * Set the name of the local host, for use in the EHLO and HELO commands. - * - * @since JavaMail 1.3.1 - */ - public synchronized void setLocalHost(String localhost) { - localHostName = localhost; - } - - /** - * Start the SMTP protocol on the given socket, which was already - * connected by the caller. Useful for implementing the SMTP ATRN - * command (RFC 2645) where an existing connection is used when - * the server reverses roles and becomes the client. - * - * @since JavaMail 1.3.3 - */ - public synchronized void connect(Socket socket) throws MessagingException { - serverSocket = socket; - super.connect(); - } - - /** - * Gets the authorization ID to be used for authentication. - * - * @return the authorization ID to use for authentication. - * - * @since JavaMail 1.4.4 - */ - public synchronized String getAuthorizationId() { - if (authorizationID == UNKNOWN) { - authorizationID = - session.getProperty("mail." + name + ".sasl.authorizationid"); - } - return authorizationID; - } - - /** - * Sets the authorization ID to be used for authentication. - * - * @param authzid the authorization ID to use for - * authentication. - * - * @since JavaMail 1.4.4 - */ - public synchronized void setAuthorizationID(String authzid) { - this.authorizationID = authzid; - } - - /** - * Is SASL authentication enabled? - * - * @return true if SASL authentication is enabled - * - * @since JavaMail 1.4.4 - */ - public synchronized boolean getSASLEnabled() { - return enableSASL; - } - - /** - * Set whether SASL authentication is enabled. - * - * @param enableSASL should we enable SASL authentication? - * - * @since JavaMail 1.4.4 - */ - public synchronized void setSASLEnabled(boolean enableSASL) { - this.enableSASL = enableSASL; - } - - /** - * Gets the SASL realm to be used for DIGEST-MD5 authentication. - * - * @return the name of the realm to use for SASL authentication. - * - * @since JavaMail 1.3.1 - */ - public synchronized String getSASLRealm() { - if (saslRealm == UNKNOWN) { - saslRealm = session.getProperty("mail." + name + ".sasl.realm"); - if (saslRealm == null) // try old name - saslRealm = session.getProperty("mail." + name + ".saslrealm"); - } - return saslRealm; - } - - /** - * Sets the SASL realm to be used for DIGEST-MD5 authentication. - * - * @param saslRealm the name of the realm to use for - * SASL authentication. - * - * @since JavaMail 1.3.1 - */ - public synchronized void setSASLRealm(String saslRealm) { - this.saslRealm = saslRealm; - } - - /** - * Get the list of SASL mechanisms to consider if SASL authentication - * is enabled. If the list is empty or null, all available SASL mechanisms - * are considered. - * - * @return the array of SASL mechanisms to consider - * - * @since JavaMail 1.4.4 - */ - public synchronized String[] getSASLMechanisms() { - if (saslMechanisms == UNKNOWN_SA) { - List v = new ArrayList(5); - String s = session.getProperty("mail." + name + ".sasl.mechanisms"); - if (s != null && s.length() > 0) { - if (logger.isLoggable(Level.FINE)) - logger.fine("SASL mechanisms allowed: " + s); - StringTokenizer st = new StringTokenizer(s, " ,"); - while (st.hasMoreTokens()) { - String m = st.nextToken(); - if (m.length() > 0) - v.add(m); - } - } - saslMechanisms = new String[v.size()]; - v.toArray(saslMechanisms); - } - if (saslMechanisms == null) - return null; - return (String[])saslMechanisms.clone(); - } - - /** - * Set the list of SASL mechanisms to consider if SASL authentication - * is enabled. If the list is empty or null, all available SASL mechanisms - * are considered. - * - * @param mechanisms the array of SASL mechanisms to consider - * - * @since JavaMail 1.4.4 - */ - public synchronized void setSASLMechanisms(String[] mechanisms) { - if (mechanisms != null) - mechanisms = (String[])mechanisms.clone(); - this.saslMechanisms = mechanisms; - } - - /** - * Gets the NTLM domain to be used for NTLM authentication. - * - * @return the name of the domain to use for NTLM authentication. - * - * @since JavaMail 1.4.3 - */ - public synchronized String getNTLMDomain() { - if (ntlmDomain == UNKNOWN) { - ntlmDomain = - session.getProperty("mail." + name + ".auth.ntlm.domain"); - } - return ntlmDomain; - } - - /** - * Sets the NTLM domain to be used for NTLM authentication. - * - * @param ntlmDomain the name of the domain to use for - * NTLM authentication. - * - * @since JavaMail 1.4.3 - */ - public synchronized void setNTLMDomain(String ntlmDomain) { - this.ntlmDomain = ntlmDomain; - } - - /** - * Should we report even successful sends by throwing an exception? - * If so, a SendFailedException will always be thrown and - * an {@link com.sun.mail.smtp.SMTPAddressSucceededException - * SMTPAddressSucceededException} will be included in the exception - * chain for each successful address, along with the usual - * {@link com.sun.mail.smtp.SMTPAddressFailedException - * SMTPAddressFailedException} for each unsuccessful address. - * - * @return true if an exception will be thrown on successful sends. - * - * @since JavaMail 1.3.2 - */ - public synchronized boolean getReportSuccess() { - return reportSuccess; - } - - /** - * Set whether successful sends should be reported by throwing - * an exception. - * - * @param reportSuccess should we throw an exception on success? - * - * @since JavaMail 1.3.2 - */ - public synchronized void setReportSuccess(boolean reportSuccess) { - this.reportSuccess = reportSuccess; - } - - /** - * Should we use the STARTTLS command to secure the connection - * if the server supports it? - * - * @return true if the STARTTLS command will be used - * - * @since JavaMail 1.3.2 - */ - public synchronized boolean getStartTLS() { - return useStartTLS; - } - - /** - * Set whether the STARTTLS command should be used. - * - * @param useStartTLS should we use the STARTTLS command? - * - * @since JavaMail 1.3.2 - */ - public synchronized void setStartTLS(boolean useStartTLS) { - this.useStartTLS = useStartTLS; - } - - /** - * Should we require the STARTTLS command to secure the connection? - * - * @return true if the STARTTLS command will be required - * - * @since JavaMail 1.4.2 - */ - public synchronized boolean getRequireStartTLS() { - return requireStartTLS; - } - - /** - * Set whether the STARTTLS command should be required. - * - * @param requireStartTLS should we require the STARTTLS command? - * - * @since JavaMail 1.4.2 - */ - public synchronized void setRequireStartTLS(boolean requireStartTLS) { - this.requireStartTLS = requireStartTLS; - } - - /** - * Is this Transport using SSL to connect to the server? - * - * @return true if using SSL - * @since JavaMail 1.4.6 - */ - public boolean isSSL() { - return serverSocket instanceof SSLSocket; - } - - /** - * Should we use the RSET command instead of the NOOP command - * in the @{link #isConnected isConnected} method? - * - * @return true if RSET will be used - * - * @since JavaMail 1.4 - */ - public synchronized boolean getUseRset() { - return useRset; - } - - /** - * Set whether the RSET command should be used instead of the - * NOOP command in the @{link #isConnected isConnected} method. - * - * @param useRset should we use the RSET command? - * - * @since JavaMail 1.4 - */ - public synchronized void setUseRset(boolean useRset) { - this.useRset = useRset; - } - - /** - * Is the NOOP command required to return a response code - * of 250 to indicate success? - * - * @return true if NOOP must return 250 - * - * @since JavaMail 1.4.3 - */ - public synchronized boolean getNoopStrict() { - return noopStrict; - } - - /** - * Set whether the NOOP command is required to return a response code - * of 250 to indicate success. - * - * @param noopStrict is NOOP required to return 250? - * - * @since JavaMail 1.4.3 - */ - public synchronized void setNoopStrict(boolean noopStrict) { - this.noopStrict = noopStrict; - } - - /** - * Return the last response we got from the server. - * A failed send is often followed by an RSET command, - * but the response from the RSET command is not saved. - * Instead, this returns the response from the command - * before the RSET command. - * - * @return last response from server - * - * @since JavaMail 1.3.2 - */ - public synchronized String getLastServerResponse() { - return lastServerResponse; - } - - /** - * Return the return code from the last response we got from the server. - * - * @return return code from last response from server - * - * @since JavaMail 1.4.1 - */ - public synchronized int getLastReturnCode() { - return lastReturnCode; - } - - /** - * Performs the actual protocol-specific connection attempt. - * Will attempt to connect to "localhost" if the host was null.

    - * - * Unless mail.smtp.ehlo is set to false, we'll try to identify - * ourselves using the ESMTP command EHLO. - * - * If mail.smtp.auth is set to true, we insist on having a username - * and password, and will try to authenticate ourselves if the server - * supports the AUTH extension (RFC 2554). - * - * @param host the name of the host to connect to - * @param port the port to use (-1 means use default port) - * @param user the name of the user to login as - * @param passwd the user's password - * @return true if connection successful, false if authentication failed - * @exception MessagingException for non-authentication failures - */ - protected synchronized boolean protocolConnect(String host, int port, - String user, String passwd) throws MessagingException { - // setting mail.smtp.ehlo to false disables attempts to use EHLO - boolean useEhlo = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".ehlo", true); - // setting mail.smtp.auth to true enables attempts to use AUTH - boolean useAuth = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".auth", false); - - if (logger.isLoggable(Level.FINE)) - logger.fine("useEhlo " + useEhlo + ", useAuth " + useAuth); - - /* - * If mail.smtp.auth is set, make sure we have a valid username - * and password, even if we might not end up using it (e.g., - * because the server doesn't support ESMTP or doesn't support - * the AUTH extension). - */ - if (useAuth && (user == null || passwd == null)) - return false; - - /* - * If port is not specified, set it to value of mail.smtp.port - * property if it exists, otherwise default to 25. - */ - if (port == -1) - port = PropUtil.getIntSessionProperty(session, - "mail." + name + ".port", -1); - if (port == -1) - port = defaultPort; - - if (host == null || host.length() == 0) - host = "localhost"; - - /* - * If anything goes wrong, we need to be sure - * to close the connection. - */ - boolean connected = false; - try { - - if (serverSocket != null) - openServer(); // only happens from connect(socket) - else - openServer(host, port); - - boolean succeed = false; - if (useEhlo) - succeed = ehlo(getLocalHost()); - if (!succeed) - helo(getLocalHost()); - - if (useStartTLS || requireStartTLS) { - if (serverSocket instanceof SSLSocket) { - logger.fine("STARTTLS requested but already using SSL"); - } else if (supportsExtension("STARTTLS")) { - startTLS(); - /* - * Have to issue another EHLO to update list of extensions - * supported, especially authentication mechanisms. - * Don't know if this could ever fail, but we ignore - * failure. - */ - ehlo(getLocalHost()); - } else if (requireStartTLS) { - logger.fine("STARTTLS required but not supported"); - throw new MessagingException( - "STARTTLS is required but " + - "host does not support STARTTLS"); - } - } - - if ((useAuth || (user != null && passwd != null)) && - (supportsExtension("AUTH") || - supportsExtension("AUTH=LOGIN"))) { - connected = authenticate(user, passwd); - return connected; - } - - // we connected correctly - connected = true; - return true; - - } finally { - // if we didn't connect successfully, - // make sure the connection is closed - if (!connected) { - try { - closeConnection(); - } catch (MessagingException mex) { } // ignore it - } - } - } - - /** - * Authenticate to the server. - */ - private boolean authenticate(String user, String passwd) - throws MessagingException { - // setting mail.smtp.auth.mechanisms controls which mechanisms will - // be used, and in what order they'll be considered. only the first - // match is used. - String mechs = session.getProperty("mail." + name + ".auth.mechanisms"); - if (mechs == null) - mechs = defaultAuthenticationMechanisms; - - String authzid = getAuthorizationId(); - if (authzid == null) - authzid = user; - if (enableSASL) { - logger.fine("Authenticate with SASL"); - if (sasllogin(getSASLMechanisms(), getSASLRealm(), authzid, - user, passwd)) - return true; // success - logger.fine("SASL authentication failed"); - } - - if (logger.isLoggable(Level.FINE)) - logger.fine("Attempt to authenticate using mechanisms: " + mechs); - - /* - * Loop through the list of mechanisms supplied by the user - * (or defaulted) and try each in turn. If the server supports - * the mechanism and we have an authenticator for the mechanism, - * and it hasn't been disabled, use it. - */ - StringTokenizer st = new StringTokenizer(mechs); - while (st.hasMoreTokens()) { - String m = st.nextToken(); - String dprop = "mail." + name + ".auth." + - m.toLowerCase(Locale.ENGLISH) + ".disable"; - boolean disabled = - PropUtil.getBooleanSessionProperty(session, dprop, false); - if (disabled) { - if (logger.isLoggable(Level.FINE)) - logger.fine( - "mechanism " + m + " disabled by property: " + dprop); - continue; - } - m = m.toUpperCase(Locale.ENGLISH); - if (!supportsAuthentication(m)) { - logger.log(Level.FINE, "mechanism {0} not supported by server", - m); - continue; - } - Authenticator a = (Authenticator)authenticators.get(m); - if (a == null) { - logger.log(Level.FINE, "no authenticator for mechanism {0}", m); - continue; - } - // only first supported mechanism is used - return a.authenticate(host, authzid, user, passwd); - } - - // if no authentication mechanism found, fail - throw new AuthenticationFailedException( - "No authentication mechansims supported by both server and client"); - } - - /** - * Abstract base class for SMTP authentication mechanism implementations. - */ - private abstract class Authenticator { - protected int resp; // the response code, used by subclasses - private String mech; // the mechanism name, set in the constructor - - Authenticator(String mech) { - this.mech = mech.toUpperCase(Locale.ENGLISH); - } - - String getMechanism() { - return mech; - } - - /** - * Start the authentication handshake by issuing the AUTH command. - * Delegate to the doAuth method to do the mechanism-specific - * part of the handshake. - */ - boolean authenticate(String host, String authzid, - String user, String passwd) throws MessagingException { - try { - // use "initial response" capability, if supported - String ir = getInitialResponse(host, authzid, user, passwd); - if (noauthdebug && isTracing()) { - logger.fine("AUTH " + mech + " command trace suppressed"); - suspendTracing(); - } - if (ir != null) - resp = simpleCommand("AUTH " + mech + " " + - (ir.length() == 0 ? "=" : ir)); - else - resp = simpleCommand("AUTH " + mech); - - /* - * A 530 response indicates that the server wants us to - * issue a STARTTLS command first. Do that and try again. - */ - if (resp == 530) { - startTLS(); - if (ir != null) - resp = simpleCommand("AUTH " + mech + " " + ir); - else - resp = simpleCommand("AUTH " + mech); - } - if (resp == 334) - doAuth(host, authzid, user, passwd); - } catch (IOException ex) { // should never happen, ignore - logger.log(Level.FINE, "AUTH " + mech + " failed", ex); - } finally { - if (noauthdebug && isTracing()) - logger.fine("AUTH " + mech + " " + - (resp == 235 ? "succeeded" : "failed")); - resumeTracing(); - if (resp != 235) { - closeConnection(); - throw new AuthenticationFailedException( - getLastServerResponse()); - } - } - return true; - } - - /** - * Provide the initial response to use in the AUTH command, - * or null if not supported. Subclasses that support the - * initial response capability will override this method. - */ - String getInitialResponse(String host, String authzid, String user, - String passwd) throws MessagingException, IOException { - return null; - } - - abstract void doAuth(String host, String authzid, String user, - String passwd) throws MessagingException, IOException; - } - - /** - * Perform the authentication handshake for LOGIN authentication. - */ - private class LoginAuthenticator extends Authenticator { - LoginAuthenticator() { - super("LOGIN"); - } - - void doAuth(String host, String authzid, String user, String passwd) - throws MessagingException, IOException { - // send username - resp = simpleCommand( - BASE64EncoderStream.encode(ASCIIUtility.getBytes(user))); - if (resp == 334) { - // send passwd - resp = simpleCommand( - BASE64EncoderStream.encode(ASCIIUtility.getBytes(passwd))); - } - } - } - - /** - * Perform the authentication handshake for PLAIN authentication. - */ - private class PlainAuthenticator extends Authenticator { - PlainAuthenticator() { - super("PLAIN"); - } - - String getInitialResponse(String host, String authzid, String user, - String passwd) throws MessagingException, IOException { - // return "authziduserpasswd" - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - OutputStream b64os = - new BASE64EncoderStream(bos, Integer.MAX_VALUE); - if (authzid != null) - b64os.write(ASCIIUtility.getBytes(authzid)); - b64os.write(0); - b64os.write(ASCIIUtility.getBytes(user)); - b64os.write(0); - b64os.write(ASCIIUtility.getBytes(passwd)); - b64os.flush(); // complete the encoding - - return ASCIIUtility.toString(bos.toByteArray()); - } - - void doAuth(String host, String authzid, String user, String passwd) - throws MessagingException, IOException { - // should never get here - throw new AuthenticationFailedException("PLAIN asked for more"); - } - } - - /** - * Perform the authentication handshake for DIGEST-MD5 authentication. - */ - private class DigestMD5Authenticator extends Authenticator { - private DigestMD5 md5support; // only create if needed - - DigestMD5Authenticator() { - super("DIGEST-MD5"); - } - - private synchronized DigestMD5 getMD5() { - if (md5support == null) - md5support = new DigestMD5(logger); - return md5support; - } - - void doAuth(String host, String authzid, String user, String passwd) - throws MessagingException, IOException { - DigestMD5 md5 = getMD5(); - if (md5 == null) { - resp = -1; - return; // XXX - should never happen - } - - byte[] b = md5.authClient(host, user, passwd, getSASLRealm(), - getLastServerResponse()); - resp = simpleCommand(b); - if (resp == 334) { // client authenticated by server - if (!md5.authServer(getLastServerResponse())) { - // server NOT authenticated by client !!! - resp = -1; - } else { - // send null response - resp = simpleCommand(new byte[0]); - } - } - } - } - - /** - * Perform the authentication handshake for NTLM authentication. - */ - private class NtlmAuthenticator extends Authenticator { - private Ntlm ntlm; - private int flags; - - NtlmAuthenticator() { - super("NTLM"); - } - - String getInitialResponse(String host, String authzid, String user, - String passwd) throws MessagingException, IOException { - ntlm = new Ntlm(getNTLMDomain(), getLocalHost(), - user, passwd, logger); - - flags = PropUtil.getIntProperty( - session.getProperties(), - "mail." + name + ".auth.ntlm.flags", 0); - - String type1 = ntlm.generateType1Msg(flags); - return type1; - } - - void doAuth(String host, String authzid, String user, String passwd) - throws MessagingException, IOException { - String type3 = ntlm.generateType3Msg( - getLastServerResponse().substring(4).trim()); - - resp = simpleCommand(type3); - } - } - - /** - * SASL-based login. - */ - public boolean sasllogin(String[] allowed, String realm, String authzid, - String u, String p) throws MessagingException { - if (saslAuthenticator == null) { - try { - Class sac = Class.forName( - "com.sun.mail.smtp.SMTPSaslAuthenticator"); - Constructor c = sac.getConstructor(new Class[] { - SMTPTransport.class, - String.class, - Properties.class, - MailLogger.class, - String.class - }); - saslAuthenticator = (SaslAuthenticator)c.newInstance( - new Object[] { - this, - name, - session.getProperties(), - logger, - host - }); - } catch (Exception ex) { - logger.log(Level.FINE, "Can't load SASL authenticator", ex); - // probably because we're running on a system without SASL - return false; // not authenticated, try without SASL - } - } - - // were any allowed mechanisms specified? - List v; - if (allowed != null && allowed.length > 0) { - // remove anything not supported by the server - v = new ArrayList(allowed.length); - for (int i = 0; i < allowed.length; i++) - if (supportsAuthentication(allowed[i])) // XXX - case must match - v.add(allowed[i]); - } else { - // everything is allowed - v = new ArrayList(); - if (extMap != null) { - String a = (String)extMap.get("AUTH"); - if (a != null) { - StringTokenizer st = new StringTokenizer(a); - while (st.hasMoreTokens()) - v.add(st.nextToken()); - } - } - } - String[] mechs = (String[])v.toArray(new String[v.size()]); - try { - if (noauthdebug && isTracing()) { - logger.fine("SASL AUTH command trace suppressed"); - suspendTracing(); - } - return saslAuthenticator.authenticate(mechs, realm, authzid, u, p); - } finally { - resumeTracing(); - } - } - - /** - * Send the Message to the specified list of addresses.

    - * - * If all the addresses succeed the SMTP check - * using the RCPT TO: command, we attempt to send the message. - * A TransportEvent of type MESSAGE_DELIVERED is fired indicating the - * successful submission of a message to the SMTP host.

    - * - * If some of the addresses fail the SMTP check, - * and the mail.stmp.sendpartial property is not set, - * sending is aborted. The TransportEvent of type MESSAGE_NOT_DELIVERED - * is fired containing the valid and invalid addresses. The - * SendFailedException is also thrown.

    - * - * If some of the addresses fail the SMTP check, - * and the mail.stmp.sendpartial property is set to true, - * the message is sent. The TransportEvent of type - * MESSAGE_PARTIALLY_DELIVERED - * is fired containing the valid and invalid addresses. The - * SMTPSendFailedException is also thrown.

    - * - * MessagingException is thrown if the message can't write out - * an RFC822-compliant stream using its writeTo method.

    - * - * @param message The MimeMessage to be sent - * @param addresses List of addresses to send this message to - * @see javax.mail.event.TransportEvent - * @exception SMTPSendFailedException if the send failed because of - * an SMTP command error - * @exception SendFailedException if the send failed because of - * invalid addresses. - * @exception MessagingException if the connection is dead - * or not in the connected state or if the message is - * not a MimeMessage. - */ - public synchronized void sendMessage(Message message, Address[] addresses) - throws MessagingException, SendFailedException { - - sendMessageStart(message != null ? message.getSubject() : ""); - checkConnected(); - - // check if the message is a valid MIME/RFC822 message and that - // it has all valid InternetAddresses; fail if not - if (!(message instanceof MimeMessage)) { - logger.fine("Can only send RFC822 msgs"); - throw new MessagingException("SMTP can only send RFC822 messages"); - } - for (int i = 0; i < addresses.length; i++) { - if (!(addresses[i] instanceof InternetAddress)) { - throw new MessagingException(addresses[i] + - " is not an InternetAddress"); - } - } - if (addresses.length == 0) - throw new SendFailedException("No recipient addresses"); - - this.message = (MimeMessage)message; - this.addresses = addresses; - validUnsentAddr = addresses; // until we know better - expandGroups(); - - boolean use8bit = false; - if (message instanceof SMTPMessage) - use8bit = ((SMTPMessage)message).getAllow8bitMIME(); - if (!use8bit) - use8bit = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".allow8bitmime", false); - if (logger.isLoggable(Level.FINE)) - logger.fine("use8bit " + use8bit); - if (use8bit && supportsExtension("8BITMIME")) { - if (convertTo8Bit(this.message)) { - // in case we made any changes, save those changes - // XXX - this will change the Message-ID - try { - this.message.saveChanges(); - } catch (MessagingException mex) { - // ignore it - } - } - } - - try { - mailFrom(); - rcptTo(); - this.message.writeTo(data(), ignoreList); - finishData(); - if (sendPartiallyFailed) { - // throw the exception, - // fire TransportEvent.MESSAGE_PARTIALLY_DELIVERED event - logger.fine("Sending partially failed " + - "because of invalid destination addresses"); - notifyTransportListeners( - TransportEvent.MESSAGE_PARTIALLY_DELIVERED, - validSentAddr, validUnsentAddr, invalidAddr, - this.message); - - throw new SMTPSendFailedException(".", lastReturnCode, - lastServerResponse, exception, - validSentAddr, validUnsentAddr, invalidAddr); - } - notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, - validSentAddr, validUnsentAddr, - invalidAddr, this.message); - } catch (MessagingException mex) { - logger.log(Level.FINE, "MessagingException while sending", mex); - // the MessagingException might be wrapping an IOException - if (mex.getNextException() instanceof IOException) { - // if we catch an IOException, it means that we want - // to drop the connection so that the message isn't sent - logger.fine("nested IOException, closing"); - try { - closeConnection(); - } catch (MessagingException cex) { /* ignore it */ } - } - addressesFailed(); - notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, - validSentAddr, validUnsentAddr, - invalidAddr, this.message); - - throw mex; - } catch (IOException ex) { - logger.log(Level.FINE, "IOException while sending, closing", ex); - // if we catch an IOException, it means that we want - // to drop the connection so that the message isn't sent - try { - closeConnection(); - } catch (MessagingException mex) { /* ignore it */ } - addressesFailed(); - notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, - validSentAddr, validUnsentAddr, - invalidAddr, this.message); - - throw new MessagingException("IOException while sending message", - ex); - } finally { - // no reason to keep this data around - validSentAddr = validUnsentAddr = invalidAddr = null; - this.addresses = null; - this.message = null; - this.exception = null; - sendPartiallyFailed = false; - notificationDone = false; // reset for next send - } - sendMessageEnd(); - } - - /** - * The send failed, fix the address arrays to report the failure correctly. - */ - private void addressesFailed() { - if (validSentAddr != null) { - if (validUnsentAddr != null) { - Address newa[] = - new Address[validSentAddr.length + validUnsentAddr.length]; - System.arraycopy(validSentAddr, 0, - newa, 0, validSentAddr.length); - System.arraycopy(validUnsentAddr, 0, - newa, validSentAddr.length, validUnsentAddr.length); - validSentAddr = null; - validUnsentAddr = newa; - } else { - validUnsentAddr = validSentAddr; - validSentAddr = null; - } - } - } - - /** - * Close the Transport and terminate the connection to the server. - */ - public synchronized void close() throws MessagingException { - if (!super.isConnected()) // Already closed. - return; - try { - if (serverSocket != null) { - sendCommand("QUIT"); - if (quitWait) { - int resp = readServerResponse(); - if (resp != 221 && resp != -1 && - logger.isLoggable(Level.FINE)) - logger.fine("QUIT failed with " + resp); - } - } - } finally { - closeConnection(); - } - } - - private void closeConnection() throws MessagingException { - try { - if (serverSocket != null) - serverSocket.close(); - } catch (IOException ioex) { // shouldn't happen - throw new MessagingException("Server Close Failed", ioex); - } finally { - serverSocket = null; - serverOutput = null; - serverInput = null; - lineInputStream = null; - if (super.isConnected()) // only notify if already connected - super.close(); - } - } - - /** - * Check whether the transport is connected. Override superclass - * method, to actually ping our server connection. - */ - public synchronized boolean isConnected() { - if (!super.isConnected()) - // if we haven't been connected at all, don't bother with NOOP - return false; - - try { - // sendmail may respond slowly to NOOP after many requests - // so if mail.smtp.userset is set we use RSET instead of NOOP. - if (useRset) - sendCommand("RSET"); - else - sendCommand("NOOP"); - int resp = readServerResponse(); - - /* - * NOOP should return 250 on success, however, SIMS 3.2 returns - * 200, so we work around it. - * - * Hotmail didn't used to implement the NOOP command at all so - * assume any kind of response means we're still connected. - * That is, any response except 421, which means the server - * is shutting down the connection. - * - * Some versions of Exchange return 451 instead of 421 when - * timing out a connection. - * - * Argh! - * - * If mail.smtp.noop.strict is set to false, be tolerant of - * servers that return the wrong response code for success. - */ - if (resp >= 0 && (noopStrict ? resp == 250 : resp != 421)) { - return true; - } else { - try { - closeConnection(); - } catch (MessagingException mex) { } // ignore it - return false; - } - } catch (Exception ex) { - try { - closeConnection(); - } catch (MessagingException mex) { } // ignore it - return false; - } - } - - /** - * Notify all TransportListeners. Keep track of whether notification - * has been done so as to only notify once per send. - * - * @since JavaMail 1.4.2 - */ - protected void notifyTransportListeners(int type, Address[] validSent, - Address[] validUnsent, - Address[] invalid, Message msg) { - - if (!notificationDone) { - super.notifyTransportListeners(type, validSent, validUnsent, - invalid, msg); - notificationDone = true; - } - } - - /** - * Expand any group addresses. - */ - private void expandGroups() { - Vector groups = null; - for (int i = 0; i < addresses.length; i++) { - InternetAddress a = (InternetAddress)addresses[i]; - if (a.isGroup()) { - if (groups == null) { - // first group, catch up with where we are - groups = new Vector(); - for (int k = 0; k < i; k++) - groups.addElement(addresses[k]); - } - // parse it and add each individual address - try { - InternetAddress[] ia = a.getGroup(true); - if (ia != null) { - for (int j = 0; j < ia.length; j++) - groups.addElement(ia[j]); - } else - groups.addElement(a); - } catch (ParseException pex) { - // parse failed, add the whole thing - groups.addElement(a); - } - } else { - // if we've started accumulating a list, add this to it - if (groups != null) - groups.addElement(a); - } - } - - // if we have a new list, convert it back to an array - if (groups != null) { - InternetAddress[] newa = new InternetAddress[groups.size()]; - groups.copyInto(newa); - addresses = newa; - } - } - - /** - * If the Part is a text part and has a Content-Transfer-Encoding - * of "quoted-printable" or "base64", and it obeys the rules for - * "8bit" encoding, change the encoding to "8bit". If the part is - * a multipart, recursively process all its parts. - * - * @return true if any changes were made - * - * XXX - This is really quite a hack. - */ - private boolean convertTo8Bit(MimePart part) { - boolean changed = false; - try { - if (part.isMimeType("text/*")) { - String enc = part.getEncoding(); - if (enc != null && (enc.equalsIgnoreCase("quoted-printable") || - enc.equalsIgnoreCase("base64"))) { - InputStream is = null; - try { - is = part.getInputStream(); - if (is8Bit(is)) { - /* - * If the message was created using an InputStream - * then we have to extract the content as an object - * and set it back as an object so that the content - * will be re-encoded. - * - * If the message was not created using an - * InputStream, the following should have no effect. - */ - part.setContent(part.getContent(), - part.getContentType()); - part.setHeader("Content-Transfer-Encoding", "8bit"); - changed = true; - } - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException ex2) { - // ignore it - } - } - } - } - } else if (part.isMimeType("multipart/*")) { - MimeMultipart mp = (MimeMultipart)part.getContent(); - int count = mp.getCount(); - for (int i = 0; i < count; i++) { - if (convertTo8Bit((MimePart)mp.getBodyPart(i))) - changed = true; - } - } - } catch (IOException ioex) { - // any exception causes us to give up - } catch (MessagingException mex) { - // any exception causes us to give up - } - return changed; - } - - /** - * Check whether the data in the given InputStream follows the - * rules for 8bit text. Lines have to be 998 characters or less - * and no NULs are allowed. CR and LF must occur in pairs but we - * don't check that because we assume this is text and we convert - * all CR/LF combinations into canonical CRLF later. - */ - private boolean is8Bit(InputStream is) { - int b; - int linelen = 0; - boolean need8bit = false; - try { - while ((b = is.read()) >= 0) { - b &= 0xff; - if (b == '\r' || b == '\n') - linelen = 0; - else if (b == 0) - return false; - else { - linelen++; - if (linelen > 998) // 1000 - CRLF - return false; - } - if (b > 0x7f) - need8bit = true; - } - } catch (IOException ex) { - return false; - } - if (need8bit) - logger.fine("found an 8bit part"); - return need8bit; - } - - protected void finalize() throws Throwable { - super.finalize(); - try { - closeConnection(); - } catch (MessagingException mex) { } // ignore it - } - - ///////////////////// smtp stuff /////////////////////// - private BufferedInputStream serverInput; - private LineInputStream lineInputStream; - private OutputStream serverOutput; - private Socket serverSocket; - private TraceInputStream traceInput; - private TraceOutputStream traceOutput; - - /////// smtp protocol ////// - - /** - * Issue the HELO command. - * - * @param domain our domain - * - * @since JavaMail 1.4.1 - */ - protected void helo(String domain) throws MessagingException { - if (domain != null) - issueCommand("HELO " + domain, 250); - else - issueCommand("HELO", 250); - } - - /** - * Issue the EHLO command. - * Collect the returned list of service extensions. - * - * @param domain our domain - * @return true if command succeeds - * - * @since JavaMail 1.4.1 - */ - protected boolean ehlo(String domain) throws MessagingException { - String cmd; - if (domain != null) - cmd = "EHLO " + domain; - else - cmd = "EHLO"; - sendCommand(cmd); - int resp = readServerResponse(); - if (resp == 250) { - // extract the supported service extensions - BufferedReader rd = - new BufferedReader(new StringReader(lastServerResponse)); - String line; - extMap = new Hashtable(); - try { - boolean first = true; - while ((line = rd.readLine()) != null) { - if (first) { // skip first line which is the greeting - first = false; - continue; - } - if (line.length() < 5) - continue; // shouldn't happen - line = line.substring(4); // skip response code - int i = line.indexOf(' '); - String arg = ""; - if (i > 0) { - arg = line.substring(i + 1); - line = line.substring(0, i); - } - if (logger.isLoggable(Level.FINE)) - logger.fine("Found extension \"" + - line + "\", arg \"" + arg + "\""); - extMap.put(line.toUpperCase(Locale.ENGLISH), arg); - } - } catch (IOException ex) { } // can't happen - } - return resp == 250; - } - - /** - * Issue the MAIL FROM: command to start sending a message.

    - * - * Gets the sender's address in the following order: - *

      - *
    1. SMTPMessage.getEnvelopeFrom()
    2. - *
    3. mail.smtp.from property
    4. - *
    5. From: header in the message
    6. - *
    7. System username using the - * InternetAddress.getLocalAddress() method
    8. - *
    - * - * @since JavaMail 1.4.1 - */ - protected void mailFrom() throws MessagingException { - String from = null; - if (message instanceof SMTPMessage) - from = ((SMTPMessage)message).getEnvelopeFrom(); - if (from == null || from.length() <= 0) - from = session.getProperty("mail." + name + ".from"); - if (from == null || from.length() <= 0) { - Address[] fa; - Address me; - if (message != null && (fa = message.getFrom()) != null && - fa.length > 0) - me = fa[0]; - else - me = InternetAddress.getLocalAddress(session); - - if (me != null) - from = ((InternetAddress)me).getAddress(); - else - throw new MessagingException( - "can't determine local email address"); - } - - String cmd = "MAIL FROM:" + normalizeAddress(from); - - // request delivery status notification? - if (supportsExtension("DSN")) { - String ret = null; - if (message instanceof SMTPMessage) - ret = ((SMTPMessage)message).getDSNRet(); - if (ret == null) - ret = session.getProperty("mail." + name + ".dsn.ret"); - // XXX - check for legal syntax? - if (ret != null) - cmd += " RET=" + ret; - } - - /* - * If an RFC 2554 submitter has been specified, and the server - * supports the AUTH extension, include the AUTH= element on - * the MAIL FROM command. - */ - if (supportsExtension("AUTH")) { - String submitter = null; - if (message instanceof SMTPMessage) - submitter = ((SMTPMessage)message).getSubmitter(); - if (submitter == null) - submitter = session.getProperty("mail." + name + ".submitter"); - // XXX - check for legal syntax? - if (submitter != null) { - try { - String s = xtext(submitter); - cmd += " AUTH=" + s; - } catch (IllegalArgumentException ex) { - if (logger.isLoggable(Level.FINE)) - logger.log(Level.FINE, "ignoring invalid submitter: " + - submitter, ex); - } - } - } - - /* - * Have any extensions to the MAIL command been specified? - */ - String ext = null; - if (message instanceof SMTPMessage) - ext = ((SMTPMessage)message).getMailExtension(); - if (ext == null) - ext = session.getProperty("mail." + name + ".mailextension"); - if (ext != null && ext.length() > 0) - cmd += " " + ext; - - try { - issueSendCommand(cmd, 250); - } catch (SMTPSendFailedException ex) { - int retCode = ex.getReturnCode(); - switch (retCode) { - case 550: case 553: case 503: case 551: case 501: - // given address is invalid - try { - ex.setNextException(new SMTPSenderFailedException( - new InternetAddress(from), cmd, - retCode, ex.getMessage())); - } catch (AddressException aex) { - // oh well... - } - break; - } - throw ex; - } - } - - /** - * Sends each address to the SMTP host using the RCPT TO: - * command and copies the address either into - * the validSentAddr or invalidAddr arrays. - * Sets the sendFailed - * flag to true if any addresses failed. - * - * @since JavaMail 1.4.1 - */ - /* - * success/failure/error possibilities from the RCPT command - * from rfc821, section 4.3 - * S: 250, 251 - * F: 550, 551, 552, 553, 450, 451, 452 - * E: 500, 501, 503, 421 - * - * and how we map the above error/failure conditions to valid/invalid - * address vectors that are reported in the thrown exception: - * invalid addr: 550, 501, 503, 551, 553 - * valid addr: 552 (quota), 450, 451, 452 (quota), 421 (srvr abort) - */ - protected void rcptTo() throws MessagingException { - Vector valid = new Vector(); - Vector validUnsent = new Vector(); - Vector invalid = new Vector(); - int retCode = -1; - MessagingException mex = null; - boolean sendFailed = false; - MessagingException sfex = null; - validSentAddr = validUnsentAddr = invalidAddr = null; - boolean sendPartial = false; - if (message instanceof SMTPMessage) - sendPartial = ((SMTPMessage)message).getSendPartial(); - if (!sendPartial) - sendPartial = PropUtil.getBooleanSessionProperty(session, - "mail." + name + ".sendpartial", false); - if (sendPartial) - logger.fine("sendPartial set"); - - boolean dsn = false; - String notify = null; - if (supportsExtension("DSN")) { - if (message instanceof SMTPMessage) - notify = ((SMTPMessage)message).getDSNNotify(); - if (notify == null) - notify = session.getProperty("mail." + name + ".dsn.notify"); - // XXX - check for legal syntax? - if (notify != null) - dsn = true; - } - - // try the addresses one at a time - for (int i = 0; i < addresses.length; i++) { - - sfex = null; - InternetAddress ia = (InternetAddress)addresses[i]; - String cmd = "RCPT TO:" + normalizeAddress(ia.getAddress()); - if (dsn) - cmd += " NOTIFY=" + notify; - // send the addresses to the SMTP server - sendCommand(cmd); - // check the server's response for address validity - retCode = readServerResponse(); - switch (retCode) { - case 250: case 251: - valid.addElement(ia); - if (!reportSuccess) - break; - - // user wants exception even when successful, including - // details of the return code - - // create and chain the exception - sfex = new SMTPAddressSucceededException(ia, cmd, retCode, - lastServerResponse); - if (mex == null) - mex = sfex; - else - mex.setNextException(sfex); - break; - - case 550: case 553: case 503: case 551: case 501: - // given address is invalid - if (!sendPartial) - sendFailed = true; - invalid.addElement(ia); - // create and chain the exception - sfex = new SMTPAddressFailedException(ia, cmd, retCode, - lastServerResponse); - if (mex == null) - mex = sfex; - else - mex.setNextException(sfex); - break; - - case 552: case 450: case 451: case 452: - // given address is valid - if (!sendPartial) - sendFailed = true; - validUnsent.addElement(ia); - // create and chain the exception - sfex = new SMTPAddressFailedException(ia, cmd, retCode, - lastServerResponse); - if (mex == null) - mex = sfex; - else - mex.setNextException(sfex); - break; - - default: - // handle remaining 4xy & 5xy codes - if (retCode >= 400 && retCode <= 499) { - // assume address is valid, although we don't really know - validUnsent.addElement(ia); - } else if (retCode >= 500 && retCode <= 599) { - // assume address is invalid, although we don't really know - invalid.addElement(ia); - } else { - // completely unexpected response, just give up - if (logger.isLoggable(Level.FINE)) - logger.fine("got response code " + retCode + - ", with response: " + lastServerResponse); - String _lsr = lastServerResponse; // else rset will nuke it - int _lrc = lastReturnCode; - if (serverSocket != null) // hasn't already been closed - issueCommand("RSET", -1); - lastServerResponse = _lsr; // restore, for get - lastReturnCode = _lrc; - throw new SMTPAddressFailedException(ia, cmd, retCode, - _lsr); - } - if (!sendPartial) - sendFailed = true; - // create and chain the exception - sfex = new SMTPAddressFailedException(ia, cmd, retCode, - lastServerResponse); - if (mex == null) - mex = sfex; - else - mex.setNextException(sfex); - break; - } - } - - // if we're willing to send to a partial list, and we found no - // valid addresses, that's complete failure - if (sendPartial && valid.size() == 0) - sendFailed = true; - - // copy the vectors into appropriate arrays - if (sendFailed) { - // copy invalid addrs - invalidAddr = new Address[invalid.size()]; - invalid.copyInto(invalidAddr); - - // copy all valid addresses to validUnsent, since something failed - validUnsentAddr = new Address[valid.size() + validUnsent.size()]; - int i = 0; - for (int j = 0; j < valid.size(); j++) - validUnsentAddr[i++] = (Address)valid.elementAt(j); - for (int j = 0; j < validUnsent.size(); j++) - validUnsentAddr[i++] = (Address)validUnsent.elementAt(j); - } else if (reportSuccess || (sendPartial && - (invalid.size() > 0 || validUnsent.size() > 0))) { - // we'll go on to send the message, but after sending we'll - // throw an exception with this exception nested - sendPartiallyFailed = true; - exception = mex; - - // copy invalid addrs - invalidAddr = new Address[invalid.size()]; - invalid.copyInto(invalidAddr); - - // copy valid unsent addresses to validUnsent - validUnsentAddr = new Address[validUnsent.size()]; - validUnsent.copyInto(validUnsentAddr); - - // copy valid addresses to validSent - validSentAddr = new Address[valid.size()]; - valid.copyInto(validSentAddr); - } else { // all addresses pass - validSentAddr = addresses; - } - - - // print out the debug info - if (logger.isLoggable(Level.FINE)) { - if (validSentAddr != null && validSentAddr.length > 0) { - logger.fine("Verified Addresses"); - for (int l = 0; l < validSentAddr.length; l++) { - logger.fine(" " + validSentAddr[l]); - } - } - if (validUnsentAddr != null && validUnsentAddr.length > 0) { - logger.fine("Valid Unsent Addresses"); - for (int j = 0; j < validUnsentAddr.length; j++) { - logger.fine(" " + validUnsentAddr[j]); - } - } - if (invalidAddr != null && invalidAddr.length > 0) { - logger.fine("Invalid Addresses"); - for (int k = 0; k < invalidAddr.length; k++) { - logger.fine(" " + invalidAddr[k]); - } - } - } - - // throw the exception, fire TransportEvent.MESSAGE_NOT_DELIVERED event - if (sendFailed) { - logger.fine( - "Sending failed because of invalid destination addresses"); - notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, - validSentAddr, validUnsentAddr, - invalidAddr, this.message); - - // reset the connection so more sends are allowed - String lsr = lastServerResponse; // save, for get - int lrc = lastReturnCode; - try { - if (serverSocket != null) - issueCommand("RSET", -1); - } catch (MessagingException ex) { - // if can't reset, best to close the connection - try { - close(); - } catch (MessagingException ex2) { - // thrown by close()--ignore, will close() later anyway - logger.log(Level.FINE, "close failed", ex2); - } - } finally { - lastServerResponse = lsr; // restore - lastReturnCode = lrc; - } - - throw new SendFailedException("Invalid Addresses", mex, - validSentAddr, - validUnsentAddr, invalidAddr); - } - } - - /** - * Send the DATA command to the SMTP host and return - * an OutputStream to which the data is to be written. - * - * @since JavaMail 1.4.1 - */ - protected OutputStream data() throws MessagingException { - assert Thread.holdsLock(this); - issueSendCommand("DATA", 354); - dataStream = new SMTPOutputStream(serverOutput); - return dataStream; - } - - /** - * Terminate the sent data. - * - * @since JavaMail 1.4.1 - */ - protected void finishData() throws IOException, MessagingException { - assert Thread.holdsLock(this); - dataStream.ensureAtBOL(); - issueSendCommand(".", 250); - } - - /** - * Issue the STARTTLS command and switch the socket to - * TLS mode if it succeeds. - * - * @since JavaMail 1.4.1 - */ - protected void startTLS() throws MessagingException { - issueCommand("STARTTLS", 220); - // it worked, now switch the socket into TLS mode - try { - serverSocket = SocketFetcher.startTLS(serverSocket, host, - session.getProperties(), "mail." + name); - initStreams(); - } catch (IOException ioex) { - closeConnection(); - throw new MessagingException("Could not convert socket to TLS", - ioex); - } - } - - /////// primitives /////// - - /** - * Connect to host on port and start the SMTP protocol. - */ - private void openServer(String host, int port) - throws MessagingException { - - if (logger.isLoggable(Level.FINE)) - logger.fine("trying to connect to host \"" + host + - "\", port " + port + ", isSSL " + isSSL); - - try { - Properties props = session.getProperties(); - - serverSocket = SocketFetcher.getSocket(host, port, - props, "mail." + name, isSSL); - - // socket factory may've chosen a different port, - // update it for the debug messages that follow - port = serverSocket.getPort(); - // save host name for startTLS - this.host = host; - - initStreams(); - - int r = -1; - if ((r = readServerResponse()) != 220) { - serverSocket.close(); - serverSocket = null; - serverOutput = null; - serverInput = null; - lineInputStream = null; - if (logger.isLoggable(Level.FINE)) - logger.fine("could not connect to host \"" + - host + "\", port: " + port + - ", response: " + r + "\n"); - throw new MessagingException( - "Could not connect to SMTP host: " + host + - ", port: " + port + - ", response: " + r); - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("connected to host \"" + - host + "\", port: " + port + "\n"); - } - } catch (UnknownHostException uhex) { - throw new MessagingException("Unknown SMTP host: " + host, uhex); - } catch (SocketConnectException scex) { - throw new MailConnectException(scex); - } catch (IOException ioe) { - throw new MessagingException("Could not connect to SMTP host: " + - host + ", port: " + port, ioe); - } - } - - /** - * Start the protocol to the server on serverSocket, - * assumed to be provided and connected by the caller. - */ - private void openServer() throws MessagingException { - int port = -1; - host = "UNKNOWN"; - try { - port = serverSocket.getPort(); - host = serverSocket.getInetAddress().getHostName(); - if (logger.isLoggable(Level.FINE)) - logger.fine("starting protocol to host \"" + - host + "\", port " + port); - - initStreams(); - - int r = -1; - if ((r = readServerResponse()) != 220) { - serverSocket.close(); - serverSocket = null; - serverOutput = null; - serverInput = null; - lineInputStream = null; - if (logger.isLoggable(Level.FINE)) - logger.fine("got bad greeting from host \"" + - host + "\", port: " + port + - ", response: " + r + "\n"); - throw new MessagingException( - "Got bad greeting from SMTP host: " + host + - ", port: " + port + - ", response: " + r); - } else { - if (logger.isLoggable(Level.FINE)) - logger.fine("protocol started to host \"" + - host + "\", port: " + port + "\n"); - } - } catch (IOException ioe) { - throw new MessagingException( - "Could not start protocol to SMTP host: " + - host + ", port: " + port, ioe); - } - } - - - private void initStreams() throws IOException { - boolean quote = PropUtil.getBooleanSessionProperty(session, - "mail.debug.quote", false); - - traceInput = - new TraceInputStream(serverSocket.getInputStream(), traceLogger); - traceInput.setQuote(quote); - - traceOutput = - new TraceOutputStream(serverSocket.getOutputStream(), traceLogger); - traceOutput.setQuote(quote); - - serverOutput = - new BufferedOutputStream(traceOutput); - serverInput = - new BufferedInputStream(traceInput); - lineInputStream = new LineInputStream(serverInput); - } - - /** - * Is protocol tracing enabled? - */ - private boolean isTracing() { - return traceLogger.isLoggable(Level.FINEST); - } - - /** - * Temporarily turn off protocol tracing, e.g., to prevent - * tracing the authentication sequence, including the password. - */ - private void suspendTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(false); - traceOutput.setTrace(false); - } - } - - /** - * Resume protocol tracing, if it was enabled to begin with. - */ - private void resumeTracing() { - if (traceLogger.isLoggable(Level.FINEST)) { - traceInput.setTrace(true); - traceOutput.setTrace(true); - } - } - - /** - * Send the command to the server. If the expected response code - * is not received, throw a MessagingException. - * - * @param cmd the command to send - * @param expect the expected response code (-1 means don't care) - * - * @since JavaMail 1.4.1 - */ - public synchronized void issueCommand(String cmd, int expect) - throws MessagingException { - sendCommand(cmd); - - // if server responded with an unexpected return code, - // throw the exception, notifying the client of the response - int resp = readServerResponse(); - if (expect != -1 && resp != expect) - throw new MessagingException(lastServerResponse); - } - - /** - * Issue a command that's part of sending a message. - */ - private void issueSendCommand(String cmd, int expect) - throws MessagingException { - sendCommand(cmd); - - // if server responded with an unexpected return code, - // throw the exception, notifying the client of the response - int ret; - if ((ret = readServerResponse()) != expect) { - // assume message was not sent to anyone, - // combine valid sent & unsent addresses - int vsl = validSentAddr == null ? 0 : validSentAddr.length; - int vul = validUnsentAddr == null ? 0 : validUnsentAddr.length; - Address[] valid = new Address[vsl + vul]; - if (vsl > 0) - System.arraycopy(validSentAddr, 0, valid, 0, vsl); - if (vul > 0) - System.arraycopy(validUnsentAddr, 0, valid, vsl, vul); - validSentAddr = null; - validUnsentAddr = valid; - if (logger.isLoggable(Level.FINE)) - logger.fine("got response code " + ret + - ", with response: " + lastServerResponse); - String _lsr = lastServerResponse; // else rset will nuke it - int _lrc = lastReturnCode; - if (serverSocket != null) // hasn't already been closed - issueCommand("RSET", -1); - lastServerResponse = _lsr; // restore, for get - lastReturnCode = _lrc; - throw new SMTPSendFailedException(cmd, ret, lastServerResponse, - exception, validSentAddr, validUnsentAddr, invalidAddr); - } - } - - /** - * Send the command to the server and return the response code - * from the server. - * - * @since JavaMail 1.4.1 - */ - public synchronized int simpleCommand(String cmd) - throws MessagingException { - sendCommand(cmd); - return readServerResponse(); - } - - /** - * Send the command to the server and return the response code - * from the server. - * - * @since JavaMail 1.4.1 - */ - protected int simpleCommand(byte[] cmd) throws MessagingException { - assert Thread.holdsLock(this); - sendCommand(cmd); - return readServerResponse(); - } - - /** - * Sends command cmd to the server terminating - * it with CRLF. - * - * @since JavaMail 1.4.1 - */ - protected void sendCommand(String cmd) throws MessagingException { - sendCommand(ASCIIUtility.getBytes(cmd)); - } - - private void sendCommand(byte[] cmdBytes) throws MessagingException { - assert Thread.holdsLock(this); - //if (logger.isLoggable(Level.FINE)) - //logger.fine("SENT: " + new String(cmdBytes, 0)); - - try { - serverOutput.write(cmdBytes); - serverOutput.write(CRLF); - serverOutput.flush(); - } catch (IOException ex) { - throw new MessagingException("Can't send command to SMTP host", ex); - } - } - - /** - * Reads server reponse returning the returnCode - * as the number. Returns -1 on failure. Sets - * lastServerResponse and lastReturnCode. - * - * @return server response code - * - * @since JavaMail 1.4.1 - */ - protected int readServerResponse() throws MessagingException { - assert Thread.holdsLock(this); - String serverResponse = ""; - int returnCode = 0; - StringBuffer buf = new StringBuffer(100); - - // read the server response line(s) and add them to the buffer - // that stores the response - try { - String line = null; - - do { - line = lineInputStream.readLine(); - if (line == null) { - serverResponse = buf.toString(); - if (serverResponse.length() == 0) - serverResponse = "[EOF]"; - lastServerResponse = serverResponse; - lastReturnCode = -1; - logger.log(Level.FINE, "EOF: {0}", serverResponse); - return -1; - } - buf.append(line); - buf.append("\n"); - } while (isNotLastLine(line)); - - serverResponse = buf.toString(); - } catch (IOException ioex) { - logger.log(Level.FINE, "exception reading response", ioex); - //ioex.printStackTrace(out); - lastServerResponse = ""; - lastReturnCode = 0; - throw new MessagingException("Exception reading response", ioex); - //returnCode = -1; - } - - // print debug info - //if (logger.isLoggable(Level.FINE)) - //logger.fine("RCVD: " + serverResponse); - - // parse out the return code - if (serverResponse.length() >= 3) { - try { - returnCode = Integer.parseInt(serverResponse.substring(0, 3)); - } catch (NumberFormatException nfe) { - try { - close(); - } catch (MessagingException mex) { - // thrown by close()--ignore, will close() later anyway - logger.log(Level.FINE, "close failed", mex); - } - returnCode = -1; - } catch (StringIndexOutOfBoundsException ex) { - try { - close(); - } catch (MessagingException mex) { - // thrown by close()--ignore, will close() later anyway - logger.log(Level.FINE, "close failed", mex); - } - returnCode = -1; - } - } else { - returnCode = -1; - } - if (returnCode == -1) - logger.log(Level.FINE, "bad server response: {0}", serverResponse); - - lastServerResponse = serverResponse; - lastReturnCode = returnCode; - return returnCode; - } - - /** - * Check if we're in the connected state. Don't bother checking - * whether the server is still alive, that will be detected later. - * - * @exception IllegalStateException if not connected - * - * @since JavaMail 1.4.1 - */ - protected void checkConnected() { - if (!super.isConnected()) - throw new IllegalStateException("Not connected"); - } - - // tests if the line is an intermediate line according to SMTP - private boolean isNotLastLine(String line) { - return line != null && line.length() >= 4 && line.charAt(3) == '-'; - } - - // wraps an address in "<>"'s if necessary - private String normalizeAddress(String addr) { - if ((!addr.startsWith("<")) && (!addr.endsWith(">"))) - return "<" + addr + ">"; - else - return addr; - } - - /** - * Return true if the SMTP server supports the specified service - * extension. Extensions are reported as results of the EHLO - * command when connecting to the server. See - * RFC 1869 - * and other RFCs that define specific extensions. - * - * @param ext the service extension name - * @return true if the extension is supported - * - * @since JavaMail 1.3.2 - */ - public boolean supportsExtension(String ext) { - return extMap != null && - extMap.get(ext.toUpperCase(Locale.ENGLISH)) != null; - } - - /** - * Return the parameter the server provided for the specified - * service extension, or null if the extension isn't supported. - * - * @param ext the service extension name - * @return the extension parameter - * - * @since JavaMail 1.3.2 - */ - public String getExtensionParameter(String ext) { - return extMap == null ? null : - (String)extMap.get(ext.toUpperCase(Locale.ENGLISH)); - } - - /** - * Does the server we're connected to support the specified - * authentication mechanism? Uses the extension information - * returned by the server from the EHLO command. - * - * @param auth the authentication mechanism - * @return true if the authentication mechanism is supported - * - * @since JavaMail 1.4.1 - */ - protected boolean supportsAuthentication(String auth) { - assert Thread.holdsLock(this); - if (extMap == null) - return false; - String a = (String)extMap.get("AUTH"); - if (a == null) - return false; - StringTokenizer st = new StringTokenizer(a); - while (st.hasMoreTokens()) { - String tok = st.nextToken(); - if (tok.equalsIgnoreCase(auth)) - return true; - } - // hack for buggy servers that advertise capability incorrectly - if (auth.equalsIgnoreCase("LOGIN") && supportsExtension("AUTH=LOGIN")) { - logger.fine("use AUTH=LOGIN hack"); - return true; - } - return false; - } - - private static char[] hexchar = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - - /** - * Convert a string to RFC 1891 xtext format. - * - *

    -     *     xtext = *( xchar / hexchar )
    -     *
    -     *     xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive,
    -     *          except for "+" and "=".
    -     *
    -     * ; "hexchar"s are intended to encode octets that cannot appear
    -     * ; as ASCII characters within an esmtp-value.
    -     *
    -     *     hexchar = ASCII "+" immediately followed by two upper case
    -     *          hexadecimal digits
    -     * 

    - * - * @since JavaMail 1.4.1 - */ - protected static String xtext(String s) { - StringBuffer sb = null; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c >= 128) // not ASCII - throw new IllegalArgumentException( - "Non-ASCII character in SMTP submitter: " + s); - if (c < '!' || c > '~' || c == '+' || c == '=') { - if (sb == null) { - sb = new StringBuffer(s.length() + 4); - sb.append(s.substring(0, i)); - } - sb.append('+'); - sb.append(hexchar[(((int)c)& 0xf0) >> 4]); - sb.append(hexchar[((int)c)& 0x0f]); - } else { - if (sb != null) - sb.append(c); - } - } - return sb != null ? sb.toString() : s; - } - - /* - * Probe points for GlassFish monitoring. - */ - private void sendMessageStart(String subject) { } - private void sendMessageEnd() { } -} diff --git a/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java b/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java deleted file mode 100644 index 33008315..00000000 --- a/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.smtp; - -import javax.mail.MessagingException; - -/** - * Interface to make it easier to call SMTPSaslAuthenticator. - */ - -public interface SaslAuthenticator { - public boolean authenticate(String[] mechs, String realm, String authzid, - String u, String p) throws MessagingException; - -} diff --git a/src/main/java/com/sun/mail/smtp/package.html b/src/main/java/com/sun/mail/smtp/package.html deleted file mode 100644 index bae38d57..00000000 --- a/src/main/java/com/sun/mail/smtp/package.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - -An SMTP protocol provider for the JavaMail API -that provides access to an SMTP server. -Refer to RFC 821 -for more information. -

    -When sending a message, detailed information on each address that -fails is available in an -{@link com.sun.mail.smtp.SMTPAddressFailedException SMTPAddressFailedException} -chained off the top level -{@link javax.mail.SendFailedException SendFailedException} -that is thrown. -In addition, if the mail.smtp.reportsuccess property -is set, an -{@link com.sun.mail.smtp.SMTPAddressSucceededException -SMTPAddressSucceededException} -will be included in the list for each address that is successful. -Note that this will cause a top level -{@link javax.mail.SendFailedException SendFailedException} -to be thrown even though the send was successful. -

    -The SMTP provider also supports ESMTP -(RFC 1651). -It can optionally use SMTP Authentication -(RFC 2554) -using the LOGIN, PLAIN, DIGEST-MD5, and NTLM mechanisms -(RFC 2595 -and RFC 2831). -

    -To use SMTP authentication you'll need to set the mail.smtp.auth -property (see below) or provide the SMTP Transport -with a username and password when connecting to the SMTP server. You -can do this using one of the following approaches: -

    -

      -
    • -Provide an Authenticator object when creating your mail Session -and provide the username and password information during the -Authenticator callback. -

      -Note that the mail.smtp.user property can be set to provide a -default username for the callback, but the password will still need to be -supplied explicitly. -

      -This approach allows you to use the static Transport send method -to send messages. -

    • -

      -

    • -Call the Transport connect method explicitly with username and -password arguments. -

      -This approach requires you to explicitly manage a Transport object -and use the Transport sendMessage method to send the message. -The transport.java demo program demonstrates how to manage a Transport -object. The following is roughly equivalent to the static -Transport send method, but supplies the needed username and -password: -

      -

      -Transport tr = session.getTransport("smtp");
      -tr.connect(smtphost, username, password);
      -msg.saveChanges();	// don't forget this
      -tr.sendMessage(msg, msg.getAllRecipients());
      -tr.close();
      -
      -
    • -
    -

    -When using DIGEST-MD5 authentication, -you'll also need to supply an appropriate realm; -your mail server administrator can supply this information. -You can set this using the mail.smtp.sasl.realm property, -or the setSASLRealm method on SMTPTransport. -

    -The SMTP protocol provider can use SASL -(RFC 2222) -authentication mechanisms on systems that support the -javax.security.sasl APIs, such as J2SE 5.0. -In addition to the SASL mechanisms that are built into -the SASL implementation, users can also provide additional -SASL mechanisms of their own design to support custom authentication -schemes. See the - -Java SASL API Programming and Deployment Guide for details. -Note that the current implementation doesn't support SASL mechanisms -that provide their own integrity or confidentiality layer. -

    -SMTP can also optionally request Delivery Status Notifications -(RFC 1891). -The delivery status will typically be reported using -a "multipart/report" -(RFC 1892) -message type with a "message/delivery-status" -(RFC 1894) -part. -You can use the classes in the {@link com.sun.mail.dsn} package to -handle these MIME types. -Note that you'll need to include dsn.jar in your CLASSPATH -as this support is not included in mail.jar. -

    -See below for the properties to enable these features. -

    -Note also that THERE IS NOT SUFFICIENT DOCUMENTATION HERE TO USE THESE -FEATURES!!! You will need to read the appropriate RFCs mentioned above -to understand what these features do and how to use them. Don't just -start setting properties and then complain to us when it doesn't work -like you expect it to work. READ THE RFCs FIRST!!! -

    -The SMTP protocol provider supports the following properties, -which may be set in the JavaMail Session object. -The properties are always set as strings; the Type column describes -how the string is interpreted. For example, use -

    -	props.put("mail.smtp.port", "888");
    -
    -to set the mail.smtp.port property, which is of type int. -

    -Note that if you're using the "smtps" protocol to access SMTP over SSL, -all the properties would be named "mail.smtps.*". -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    mail.smtp.userStringDefault user name for SMTP.
    mail.smtp.hostStringThe SMTP server to connect to.
    mail.smtp.portintThe SMTP server port to connect to, if the connect() method doesn't -explicitly specify one. Defaults to 25.
    mail.smtp.connectiontimeoutintSocket connection timeout value in milliseconds. -Default is infinite timeout.
    mail.smtp.timeoutintSocket I/O timeout value in milliseconds. Default is infinite timeout.
    mail.smtp.fromString -Email address to use for SMTP MAIL command. This sets the envelope -return address. Defaults to msg.getFrom() or -InternetAddress.getLocalAddress(). NOTE: mail.smtp.user was previously -used for this. -
    mail.smtp.localhostString -Local host name used in the SMTP HELO or EHLO command. -Defaults to InetAddress.getLocalHost().getHostName(). -Should not normally need to -be set if your JDK and your name service are configured properly. -
    mail.smtp.localaddressString -Local address (host name) to bind to when creating the SMTP socket. -Defaults to the address picked by the Socket class. -Should not normally need to be set, but useful with multi-homed hosts -where it's important to pick a particular local address to bind to. -
    mail.smtp.localportint -Local port number to bind to when creating the SMTP socket. -Defaults to the port number picked by the Socket class. -
    mail.smtp.ehloboolean -If false, do not attempt to sign on with the EHLO command. Defaults to -true. Normally failure of the EHLO command will fallback to the HELO -command; this property exists only for servers that don't fail EHLO -properly or don't implement EHLO properly. -
    mail.smtp.authbooleanIf true, attempt to authenticate the user using the AUTH command. -Defaults to false.
    mail.smtp.auth.mechanismsString -If set, lists the authentication mechanisms to consider, and the order -in which to consider them. Only mechanisms supported by the server and -supported by the current implementation will be used. -The default is "LOGIN PLAIN DIGEST-MD5 NTLM", which includes all -the authentication mechanisms supported by the current implementation. -
    mail.smtp.auth.login.disablebooleanIf true, prevents use of the AUTH LOGIN command. -Default is false.
    mail.smtp.auth.plain.disablebooleanIf true, prevents use of the AUTH PLAIN command. -Default is false.
    mail.smtp.auth.digest-md5.disablebooleanIf true, prevents use of the AUTH DIGEST-MD5 command. -Default is false.
    mail.smtp.auth.ntlm.disablebooleanIf true, prevents use of the AUTH NTLM command. -Default is false.
    mail.smtp.auth.ntlm.domainString -The NTLM authentication domain. -
    mail.smtp.auth.ntlm.flagsint -NTLM protocol-specific flags. -See -http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details. -
    mail.smtp.submitterStringThe submitter to use in the AUTH tag in the MAIL FROM command. -Typically used by a mail relay to pass along information about the -original submitter of the message. -See also the {@link com.sun.mail.smtp.SMTPMessage#setSubmitter setSubmitter} -method of {@link com.sun.mail.smtp.SMTPMessage SMTPMessage}. -Mail clients typically do not use this. -
    mail.smtp.dsn.notifyStringThe NOTIFY option to the RCPT command. Either NEVER, or some -combination of SUCCESS, FAILURE, and DELAY (separated by commas).
    mail.smtp.dsn.retStringThe RET option to the MAIL command. Either FULL or HDRS.
    mail.smtp.allow8bitmimeboolean -If set to true, and the server supports the 8BITMIME extension, text -parts of messages that use the "quoted-printable" or "base64" encodings -are converted to use "8bit" encoding if they follow the RFC2045 rules -for 8bit text. -
    mail.smtp.sendpartialboolean -If set to true, and a message has some valid and some invalid -addresses, send the message anyway, reporting the partial failure with -a SendFailedException. If set to false (the default), the message is -not sent to any of the recipients if there is an invalid recipient -address. -
    mail.smtp.sasl.enableboolean -If set to true, attempt to use the javax.security.sasl package to -choose an authentication mechanism for login. -Defaults to false. -
    mail.smtp.sasl.mechanismsString -A space or comma separated list of SASL mechanism names to try -to use. -
    mail.smtp.sasl.authorizationidString -The authorization ID to use in the SASL authentication. -If not set, the authentication ID (user name) is used. -
    mail.smtp.sasl.realmStringThe realm to use with DIGEST-MD5 authentication.
    mail.smtp.quitwaitboolean -If set to false, the QUIT command is sent -and the connection is immediately closed. -If set to true (the default), causes the transport to wait -for the response to the QUIT command. -
    mail.smtp.reportsuccessboolean -If set to true, causes the transport to include an -{@link com.sun.mail.smtp.SMTPAddressSucceededException -SMTPAddressSucceededException} -for each address that is successful. -Note also that this will cause a -{@link javax.mail.SendFailedException SendFailedException} -to be thrown from the -{@link com.sun.mail.smtp.SMTPTransport#sendMessage sendMessage} -method of -{@link com.sun.mail.smtp.SMTPTransport SMTPTransport} -even if all addresses were correct and the message was sent -successfully. -
    mail.smtp.socketFactorySocketFactory -If set to a class that implements the -javax.net.SocketFactory interface, this class -will be used to create SMTP sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.smtp.socketFactory.classString -If set, specifies the name of a class that implements the -javax.net.SocketFactory interface. This class -will be used to create SMTP sockets. -
    mail.smtp.socketFactory.fallbackboolean -If set to true, failure to create a socket using the specified -socket factory class will cause the socket to be created using -the java.net.Socket class. -Defaults to true. -
    mail.smtp.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.smtp.ssl.enableboolean -If set to true, use SSL to connect and use the SSL port by default. -Defaults to false for the "smtp" protocol and true for the "smtps" protocol. -
    mail.smtp.ssl.checkserveridentityboolean -If set to true, check the server identity as specified by -RFC 2595. -These additional checks based on the content of the server's certificate -are intended to prevent man-in-the-middle attacks. -Defaults to false. -
    mail.smtp.ssl.trustString -If set, and a socket factory hasn't been specified, enables use of a -{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. -If set to "*", all hosts are trusted. -If set to a whitespace separated list of hosts, those hosts are trusted. -Otherwise, trust depends on the certificate the server presents. -
    mail.smtp.ssl.socketFactorySSLSocketFactory -If set to a class that extends the -javax.net.ssl.SSLSocketFactory class, this class -will be used to create SMTP SSL sockets. Note that this is an -instance of a class, not a name, and must be set using the -put method, not the setProperty method. -
    mail.smtp.ssl.socketFactory.classString -If set, specifies the name of a class that extends the -javax.net.ssl.SSLSocketFactory class. This class -will be used to create SMTP SSL sockets. -
    mail.smtp.ssl.socketFactory.portint -Specifies the port to connect to when using the specified socket -factory. -If not set, the default port will be used. -
    mail.smtp.ssl.protocolsstring -Specifies the SSL protocols that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledProtocols method. -
    mail.smtp.ssl.ciphersuitesstring -Specifies the SSL cipher suites that will be enabled for SSL connections. -The property value is a whitespace separated list of tokens acceptable -to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. -
    mail.smtp.starttls.enableboolean -If true, enables the use of the STARTTLS command (if -supported by the server) to switch the connection to a TLS-protected -connection before issuing any login commands. Note that an appropriate -trust store must configured so that the client will trust the server's -certificate. -Defaults to false. -
    mail.smtp.starttls.requiredboolean -If true, requires the use of the STARTTLS command. -If the server doesn't support the STARTTLS command, or the command -fails, the connect method will fail. -Defaults to false. -
    mail.smtp.socks.hoststring -Specifies the host name of a SOCKS5 proxy server that will be used for -connections to the mail server. -(Note that this only works on JDK 1.5 or newer.) -
    mail.smtp.socks.portstring -Specifies the port number for the SOCKS5 proxy server. -This should only need to be used if the proxy server is not using -the standard port number of 1080. -
    mail.smtp.mailextensionString -Extension string to append to the MAIL command. -The extension string can be used to specify standard SMTP -service extensions as well as vendor-specific extensions. -Typically the application should use the -{@link com.sun.mail.smtp.SMTPTransport SMTPTransport} -method {@link com.sun.mail.smtp.SMTPTransport#supportsExtension -supportsExtension} -to verify that the server supports the desired service extension. -See RFC 1869 -and other RFCs that define specific extensions. -
    mail.smtp.usersetboolean -If set to true, use the RSET command instead of the NOOP command -in the {@link javax.mail.Transport#isConnected isConnected} method. -In some cases sendmail will respond slowly after many NOOP commands; -use of RSET avoids this sendmail issue. -Defaults to false. -
    mail.smtp.noop.strictboolean -If set to true (the default), insist on a 250 response code from the NOOP -command to indicate success. The NOOP command is used by the -{@link javax.mail.Transport#isConnected isConnected} method to determine -if the connection is still alive. -Some older servers return the wrong response code on success, some -servers don't implement the NOOP command at all and so always return -a failure code. Set this property to false to handle servers -that are broken in this way. -Normally, when a server times out a connection, it will send a 421 -response code, which the client will see as the response to the next -command it issues. -Some servers send the wrong failure response code when timing out a -connection. -Do not set this property to false when dealing with servers that are -broken in this way. -
    -

    -In general, applications should not need to use the classes in this -package directly. Instead, they should use the APIs defined by -javax.mail package (and subpackages). Applications should -never construct instances of SMTPTransport directly. -Instead, they should use the -Session method getTransport to acquire an -appropriate Transport object. -

    -In addition to printing debugging output as controlled by the -{@link javax.mail.Session Session} configuration, -the com.sun.mail.smtp provider logs the same information using -{@link java.util.logging.Logger} as described in the following table: -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    Logger NameLogging LevelPurpose
    com.sun.mail.smtpCONFIGConfiguration of the SMTPTransport
    com.sun.mail.smtpFINEGeneral debugging output
    com.sun.mail.smtp.protocolFINESTComplete protocol trace
    - -

    -WARNING: The APIs unique to this package should be -considered EXPERIMENTAL. They may be changed in the -future in ways that are incompatible with applications using the -current APIs. - - - diff --git a/src/main/java/com/sun/mail/util/ASCIIUtility.java b/src/main/java/com/sun/mail/util/ASCIIUtility.java deleted file mode 100644 index 2b3446a5..00000000 --- a/src/main/java/com/sun/mail/util/ASCIIUtility.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.IOException; - -public class ASCIIUtility { - - // Private constructor so that this class is not instantiated - private ASCIIUtility() { } - - /** - * Convert the bytes within the specified range of the given byte - * array into a signed integer in the given radix . The range extends - * from start till, but not including end.

    - * - * Based on java.lang.Integer.parseInt() - */ - public static int parseInt(byte[] b, int start, int end, int radix) - throws NumberFormatException { - if (b == null) - throw new NumberFormatException("null"); - - int result = 0; - boolean negative = false; - int i = start; - int limit; - int multmin; - int digit; - - if (end > start) { - if (b[i] == '-') { - negative = true; - limit = Integer.MIN_VALUE; - i++; - } else { - limit = -Integer.MAX_VALUE; - } - multmin = limit / radix; - if (i < end) { - digit = Character.digit((char)b[i++], radix); - if (digit < 0) { - throw new NumberFormatException( - "illegal number: " + toString(b, start, end) - ); - } else { - result = -digit; - } - } - while (i < end) { - // Accumulating negatively avoids surprises near MAX_VALUE - digit = Character.digit((char)b[i++], radix); - if (digit < 0) { - throw new NumberFormatException("illegal number"); - } - if (result < multmin) { - throw new NumberFormatException("illegal number"); - } - result *= radix; - if (result < limit + digit) { - throw new NumberFormatException("illegal number"); - } - result -= digit; - } - } else { - throw new NumberFormatException("illegal number"); - } - if (negative) { - if (i > start + 1) { - return result; - } else { /* Only got "-" */ - throw new NumberFormatException("illegal number"); - } - } else { - return -result; - } - } - - /** - * Convert the bytes within the specified range of the given byte - * array into a signed integer . The range extends from - * start till, but not including end.

    - */ - public static int parseInt(byte[] b, int start, int end) - throws NumberFormatException { - return parseInt(b, start, end, 10); - } - - /** - * Convert the bytes within the specified range of the given byte - * array into a signed long in the given radix . The range extends - * from start till, but not including end.

    - * - * Based on java.lang.Long.parseLong() - */ - public static long parseLong(byte[] b, int start, int end, int radix) - throws NumberFormatException { - if (b == null) - throw new NumberFormatException("null"); - - long result = 0; - boolean negative = false; - int i = start; - long limit; - long multmin; - int digit; - - if (end > start) { - if (b[i] == '-') { - negative = true; - limit = Long.MIN_VALUE; - i++; - } else { - limit = -Long.MAX_VALUE; - } - multmin = limit / radix; - if (i < end) { - digit = Character.digit((char)b[i++], radix); - if (digit < 0) { - throw new NumberFormatException( - "illegal number: " + toString(b, start, end) - ); - } else { - result = -digit; - } - } - while (i < end) { - // Accumulating negatively avoids surprises near MAX_VALUE - digit = Character.digit((char)b[i++], radix); - if (digit < 0) { - throw new NumberFormatException("illegal number"); - } - if (result < multmin) { - throw new NumberFormatException("illegal number"); - } - result *= radix; - if (result < limit + digit) { - throw new NumberFormatException("illegal number"); - } - result -= digit; - } - } else { - throw new NumberFormatException("illegal number"); - } - if (negative) { - if (i > start + 1) { - return result; - } else { /* Only got "-" */ - throw new NumberFormatException("illegal number"); - } - } else { - return -result; - } - } - - /** - * Convert the bytes within the specified range of the given byte - * array into a signed long . The range extends from - * start till, but not including end.

    - */ - public static long parseLong(byte[] b, int start, int end) - throws NumberFormatException { - return parseLong(b, start, end, 10); - } - - /** - * Convert the bytes within the specified range of the given byte - * array into a String. The range extends from start - * till, but not including end.

    - */ - public static String toString(byte[] b, int start, int end) { - int size = end - start; - char[] theChars = new char[size]; - - for (int i = 0, j = start; i < size; ) - theChars[i++] = (char)(b[j++]&0xff); - - return new String(theChars); - } - - /** - * Convert the bytes into a String. - * - * @since JavaMail 1.4.4 - */ - public static String toString(byte[] b) { - return toString(b, 0, b.length); - } - - public static String toString(ByteArrayInputStream is) { - int size = is.available(); - char[] theChars = new char[size]; - byte[] bytes = new byte[size]; - - is.read(bytes, 0, size); - for (int i = 0; i < size;) - theChars[i] = (char)(bytes[i++]&0xff); - - return new String(theChars); - } - - - public static byte[] getBytes(String s) { - char [] chars= s.toCharArray(); - int size = chars.length; - byte[] bytes = new byte[size]; - - for (int i = 0; i < size;) - bytes[i] = (byte) chars[i++]; - return bytes; - } - - public static byte[] getBytes(InputStream is) throws IOException { - - int len; - int size = 1024; - byte [] buf; - - - if (is instanceof ByteArrayInputStream) { - size = is.available(); - buf = new byte[size]; - len = is.read(buf, 0, size); - } - else { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - buf = new byte[size]; - while ((len = is.read(buf, 0, size)) != -1) - bos.write(buf, 0, len); - buf = bos.toByteArray(); - } - return buf; - } -} diff --git a/src/main/java/com/sun/mail/util/BASE64DecoderStream.java b/src/main/java/com/sun/mail/util/BASE64DecoderStream.java deleted file mode 100644 index 526e04b5..00000000 --- a/src/main/java/com/sun/mail/util/BASE64DecoderStream.java +++ /dev/null @@ -1,470 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a BASE64 Decoder. It is implemented as - * a FilterInputStream, so one can just wrap this class around - * any input stream and read bytes from this filter. The decoding - * is done as the bytes are read out. - * - * @author John Mani - * @author Bill Shannon - */ - -public class BASE64DecoderStream extends FilterInputStream { - // buffer of decoded bytes for single byte reads - private byte[] buffer = new byte[3]; - private int bufsize = 0; // size of the cache - private int index = 0; // index into the cache - - // buffer for almost 8K of typical 76 chars + CRLF lines, - // used by getByte method. this buffer contains encoded bytes. - private byte[] input_buffer = new byte[78*105]; - private int input_pos = 0; - private int input_len = 0;; - - private boolean ignoreErrors = false; - - /** - * Create a BASE64 decoder that decodes the specified input stream. - * The System property mail.mime.base64.ignoreerrors - * controls whether errors in the encoded data cause an exception - * or are ignored. The default is false (errors cause exception). - * - * @param in the input stream - */ - public BASE64DecoderStream(InputStream in) { - super(in); - // default to false - ignoreErrors = PropUtil.getBooleanSystemProperty( - "mail.mime.base64.ignoreerrors", false); - } - - /** - * Create a BASE64 decoder that decodes the specified input stream. - * - * @param in the input stream - * @param ignoreErrors ignore errors in encoded data? - */ - public BASE64DecoderStream(InputStream in, boolean ignoreErrors) { - super(in); - this.ignoreErrors = ignoreErrors; - } - - /** - * Read the next decoded byte from this input stream. The byte - * is returned as an int in the range 0 - * to 255. If no byte is available because the end of - * the stream has been reached, the value -1 is returned. - * This method blocks until input data is available, the end of the - * stream is detected, or an exception is thrown. - * - * @return next byte of data, or -1 if the end of the - * stream is reached. - * @exception IOException if an I/O error occurs. - * @see java.io.FilterInputStream#in - */ - public int read() throws IOException { - if (index >= bufsize) { - bufsize = decode(buffer, 0, buffer.length); - if (bufsize <= 0) // buffer is empty - return -1; - index = 0; // reset index into buffer - } - return buffer[index++] & 0xff; // Zero off the MSB - } - - /** - * Reads up to len decoded bytes of data from this input stream - * into an array of bytes. This method blocks until some input is - * available. - *

    - * - * @param buf the buffer into which the data is read. - * @param off the start offset of the data. - * @param len the maximum number of bytes read. - * @return the total number of bytes read into the buffer, or - * -1 if there is no more data because the end of - * the stream has been reached. - * @exception IOException if an I/O error occurs. - */ - public int read(byte[] buf, int off, int len) throws IOException { - // empty out single byte read buffer - int off0 = off; - while (index < bufsize && len > 0) { - buf[off++] = buffer[index++]; - len--; - } - if (index >= bufsize) - bufsize = index = 0; - - int bsize = (len / 3) * 3; // round down to multiple of 3 bytes - if (bsize > 0) { - int size = decode(buf, off, bsize); - off += size; - len -= size; - - if (size != bsize) { // hit EOF? - if (off == off0) // haven't returned any data - return -1; - else // returned some data before hitting EOF - return off - off0; - } - } - - // finish up with a partial read if necessary - for (; len > 0; len--) { - int c = read(); - if (c == -1) // EOF - break; - buf[off++] = (byte)c; - } - - if (off == off0) // haven't returned any data - return -1; - else // returned some data before hitting EOF - return off - off0; - } - - /** - * Skips over and discards n bytes of data from this stream. - */ - public long skip(long n) throws IOException { - long skipped = 0; - while (n-- > 0 && read() >= 0) - skipped++; - return skipped; - } - - /** - * Tests if this input stream supports marks. Currently this class - * does not support marks - */ - public boolean markSupported() { - return false; // Maybe later .. - } - - /** - * Returns the number of bytes that can be read from this input - * stream without blocking. However, this figure is only - * a close approximation in case the original encoded stream - * contains embedded CRLFs; since the CRLFs are discarded, not decoded - */ - public int available() throws IOException { - // This is only an estimate, since in.available() - // might include CRLFs too .. - return ((in.available() * 3)/4 + (bufsize-index)); - } - - /** - * This character array provides the character to value map - * based on RFC1521. - */ - private final static char pem_array[] = { - 'A','B','C','D','E','F','G','H', // 0 - 'I','J','K','L','M','N','O','P', // 1 - 'Q','R','S','T','U','V','W','X', // 2 - 'Y','Z','a','b','c','d','e','f', // 3 - 'g','h','i','j','k','l','m','n', // 4 - 'o','p','q','r','s','t','u','v', // 5 - 'w','x','y','z','0','1','2','3', // 6 - '4','5','6','7','8','9','+','/' // 7 - }; - - private final static byte pem_convert_array[] = new byte[256]; - - static { - for (int i = 0; i < 255; i++) - pem_convert_array[i] = -1; - for (int i = 0; i < pem_array.length; i++) - pem_convert_array[pem_array[i]] = (byte)i; - } - - /** - * The decoder algorithm. Most of the complexity here is dealing - * with error cases. Returns the number of bytes decoded, which - * may be zero. Decoding is done by filling an int with 4 6-bit - * values by shifting them in from the bottom and then extracting - * 3 8-bit bytes from the int by shifting them out from the bottom. - * - * @param outbuf the buffer into which to put the decoded bytes - * @param pos position in the buffer to start filling - * @param len the number of bytes to fill - * @return the number of bytes filled, always a multiple - * of three, and may be zero - * @exception IOException if the data is incorrectly formatted - */ - private int decode(byte[] outbuf, int pos, int len) throws IOException { - int pos0 = pos; - while (len >= 3) { - /* - * We need 4 valid base64 characters before we start decoding. - * We skip anything that's not a valid base64 character (usually - * just CRLF). - */ - int got = 0; - int val = 0; - while (got < 4) { - int i = getByte(); - if (i == -1 || i == -2) { - boolean atEOF; - if (i == -1) { - if (got == 0) - return pos - pos0; - if (!ignoreErrors) - throw new DecodingException( - "BASE64Decoder: Error in encoded stream: " + - "needed 4 valid base64 characters " + - "but only got " + got + " before EOF" + - recentChars()); - atEOF = true; // don't read any more - } else { // i == -2 - // found a padding character, we're at EOF - // XXX - should do something to make EOF "sticky" - if (got < 2 && !ignoreErrors) - throw new DecodingException( - "BASE64Decoder: Error in encoded stream: " + - "needed at least 2 valid base64 characters," + - " but only got " + got + - " before padding character (=)" + - recentChars()); - - // didn't get any characters before padding character? - if (got == 0) - return pos - pos0; - atEOF = false; // need to keep reading - } - - // pad partial result with zeroes - - // how many bytes will we produce on output? - // (got always < 4, so size always < 3) - int size = got - 1; - if (size == 0) - size = 1; - - // handle the one padding character we've seen - got++; - val <<= 6; - - while (got < 4) { - if (!atEOF) { - // consume the rest of the padding characters, - // filling with zeroes - i = getByte(); - if (i == -1) { - if (!ignoreErrors) - throw new DecodingException( - "BASE64Decoder: Error in encoded " + - "stream: hit EOF while looking for " + - "padding characters (=)" + - recentChars()); - } else if (i != -2) { - if (!ignoreErrors) - throw new DecodingException( - "BASE64Decoder: Error in encoded " + - "stream: found valid base64 " + - "character after a padding character " + - "(=)" + recentChars()); - } - } - val <<= 6; - got++; - } - - // now pull out however many valid bytes we got - val >>= 8; // always skip first one - if (size == 2) - outbuf[pos + 1] = (byte)(val & 0xff); - val >>= 8; - outbuf[pos] = (byte)(val & 0xff); - // len -= size; // not needed, return below - pos += size; - return pos - pos0; - } else { - // got a valid byte - val <<= 6; - got++; - val |= i; - } - } - - // read 4 valid characters, now extract 3 bytes - outbuf[pos + 2] = (byte)(val & 0xff); - val >>= 8; - outbuf[pos + 1] = (byte)(val & 0xff); - val >>= 8; - outbuf[pos] = (byte)(val & 0xff); - len -= 3; - pos += 3; - } - return pos - pos0; - } - - /** - * Read the next valid byte from the input stream. - * Buffer lots of data from underlying stream in input_buffer, - * for efficiency. - * - * @return the next byte, -1 on EOF, or -2 if next byte is '=' - * (padding at end of encoded data) - */ - private int getByte() throws IOException { - int c; - do { - if (input_pos >= input_len) { - try { - input_len = in.read(input_buffer); - } catch (EOFException ex) { - return -1; - } - if (input_len <= 0) - return -1; - input_pos = 0; - } - // get the next byte in the buffer - c = input_buffer[input_pos++] & 0xff; - // is it a padding byte? - if (c == '=') - return -2; - // no, convert it - c = pem_convert_array[c]; - // loop until we get a legitimate byte - } while (c == -1); - return c; - } - - /** - * Return the most recent characters, for use in an error message. - */ - private String recentChars() { - // reach into the input buffer and extract up to 10 - // recent characters, to help in debugging. - String errstr = ""; - int nc = input_pos > 10 ? 10 : input_pos; - if (nc > 0) { - errstr += ", the " + nc + - " most recent characters were: \""; - for (int k = input_pos - nc; k < input_pos; k++) { - char c = (char)(input_buffer[k] & 0xff); - switch (c) { - case '\r': errstr += "\\r"; break; - case '\n': errstr += "\\n"; break; - case '\t': errstr += "\\t"; break; - default: - if (c >= ' ' && c < 0177) - errstr += c; - else - errstr += ("\\" + (int)c); - } - } - errstr += "\""; - } - return errstr; - } - - /** - * Base64 decode a byte array. No line breaks are allowed. - * This method is suitable for short strings, such as those - * in the IMAP AUTHENTICATE protocol, but not to decode the - * entire content of a MIME part. - * - * NOTE: inbuf may only contain valid base64 characters. - * Whitespace is not ignored. - */ - public static byte[] decode(byte[] inbuf) { - int size = (inbuf.length / 4) * 3; - if (size == 0) - return inbuf; - - if (inbuf[inbuf.length - 1] == '=') { - size--; - if (inbuf[inbuf.length - 2] == '=') - size--; - } - byte[] outbuf = new byte[size]; - - int inpos = 0, outpos = 0; - size = inbuf.length; - while (size > 0) { - int val; - int osize = 3; - val = pem_convert_array[inbuf[inpos++] & 0xff]; - val <<= 6; - val |= pem_convert_array[inbuf[inpos++] & 0xff]; - val <<= 6; - if (inbuf[inpos] != '=') // End of this BASE64 encoding - val |= pem_convert_array[inbuf[inpos++] & 0xff]; - else - osize--; - val <<= 6; - if (inbuf[inpos] != '=') // End of this BASE64 encoding - val |= pem_convert_array[inbuf[inpos++] & 0xff]; - else - osize--; - if (osize > 2) - outbuf[outpos + 2] = (byte)(val & 0xff); - val >>= 8; - if (osize > 1) - outbuf[outpos + 1] = (byte)(val & 0xff); - val >>= 8; - outbuf[outpos] = (byte)(val & 0xff); - outpos += osize; - size -= 4; - } - return outbuf; - } - - /*** begin TEST program *** - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - BASE64DecoderStream decoder = new BASE64DecoderStream(infile); - int c; - - while ((c = decoder.read()) != -1) - System.out.print((char)c); - System.out.flush(); - } - *** end TEST program ***/ -} diff --git a/src/main/java/com/sun/mail/util/BASE64EncoderStream.java b/src/main/java/com/sun/mail/util/BASE64EncoderStream.java deleted file mode 100644 index aafb4437..00000000 --- a/src/main/java/com/sun/mail/util/BASE64EncoderStream.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a BASE64 encoder. It is implemented as - * a FilterOutputStream, so one can just wrap this class around - * any output stream and write bytes into this filter. The encoding - * is done as the bytes are written out. - * - * @author John Mani - * @author Bill Shannon - */ - -public class BASE64EncoderStream extends FilterOutputStream { - private byte[] buffer; // cache of bytes that are yet to be encoded - private int bufsize = 0; // size of the cache - private byte[] outbuf; // line size output buffer - private int count = 0; // number of bytes that have been output - private int bytesPerLine; // number of bytes per line - private int lineLimit; // number of input bytes to output bytesPerLine - private boolean noCRLF = false; - - private static byte[] newline = new byte[] { '\r', '\n' }; - - /** - * Create a BASE64 encoder that encodes the specified output stream. - * - * @param out the output stream - * @param bytesPerLine number of bytes per line. The encoder inserts - * a CRLF sequence after the specified number of bytes, - * unless bytesPerLine is Integer.MAX_VALUE, in which - * case no CRLF is inserted. bytesPerLine is rounded - * down to a multiple of 4. - */ - public BASE64EncoderStream(OutputStream out, int bytesPerLine) { - super(out); - buffer = new byte[3]; - if (bytesPerLine == Integer.MAX_VALUE || bytesPerLine < 4) { - noCRLF = true; - bytesPerLine = 76; - } - bytesPerLine = (bytesPerLine / 4) * 4; // Rounded down to 4n - this.bytesPerLine = bytesPerLine; // save it - lineLimit = bytesPerLine / 4 * 3; - - if (noCRLF) { - outbuf = new byte[bytesPerLine]; - } else { - outbuf = new byte[bytesPerLine + 2]; - outbuf[bytesPerLine] = (byte)'\r'; - outbuf[bytesPerLine + 1] = (byte)'\n'; - } - } - - /** - * Create a BASE64 encoder that encodes the specified input stream. - * Inserts the CRLF sequence after outputting 76 bytes. - * - * @param out the output stream - */ - public BASE64EncoderStream(OutputStream out) { - this(out, 76); - } - - /** - * Encodes len bytes from the specified - * byte array starting at offset off to - * this output stream. - * - * @param b the data. - * @param off the start offset in the data. - * @param len the number of bytes to write. - * @exception IOException if an I/O error occurs. - */ - public synchronized void write(byte[] b, int off, int len) - throws IOException { - int end = off + len; - - // finish off incomplete coding unit - while (bufsize != 0 && off < end) - write(b[off++]); - - // finish off line - int blen = ((bytesPerLine - count) / 4) * 3; - if (off + blen <= end) { - // number of bytes that will be produced in outbuf - int outlen = encodedSize(blen); - if (!noCRLF) { - outbuf[outlen++] = (byte)'\r'; - outbuf[outlen++] = (byte)'\n'; - } - out.write(encode(b, off, blen, outbuf), 0, outlen); - off += blen; - count = 0; - } - - // do bulk encoding a line at a time. - for (; off + lineLimit <= end; off += lineLimit) - out.write(encode(b, off, lineLimit, outbuf)); - - // handle remaining partial line - if (off + 3 <= end) { - blen = end - off; - blen = (blen / 3) * 3; // round down - // number of bytes that will be produced in outbuf - int outlen = encodedSize(blen); - out.write(encode(b, off, blen, outbuf), 0, outlen); - off += blen; - count += outlen; - } - - // start next coding unit - for (; off < end; off++) - write(b[off]); - } - - /** - * Encodes b.length bytes to this output stream. - * - * @param b the data to be written. - * @exception IOException if an I/O error occurs. - */ - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - /** - * Encodes the specified byte to this output stream. - * - * @param c the byte. - * @exception IOException if an I/O error occurs. - */ - public synchronized void write(int c) throws IOException { - buffer[bufsize++] = (byte)c; - if (bufsize == 3) { // Encoding unit = 3 bytes - encode(); - bufsize = 0; - } - } - - /** - * Flushes this output stream and forces any buffered output bytes - * to be encoded out to the stream. - * - * @exception IOException if an I/O error occurs. - */ - public synchronized void flush() throws IOException { - if (bufsize > 0) { // If there's unencoded characters in the buffer .. - encode(); // .. encode them - bufsize = 0; - } - out.flush(); - } - - /** - * Forces any buffered output bytes to be encoded out to the stream - * and closes this output stream - */ - public synchronized void close() throws IOException { - flush(); - if (count > 0 && !noCRLF) { - out.write(newline); - out.flush(); - } - out.close(); - } - - /** This array maps the characters to their 6 bit values */ - private final static char pem_array[] = { - 'A','B','C','D','E','F','G','H', // 0 - 'I','J','K','L','M','N','O','P', // 1 - 'Q','R','S','T','U','V','W','X', // 2 - 'Y','Z','a','b','c','d','e','f', // 3 - 'g','h','i','j','k','l','m','n', // 4 - 'o','p','q','r','s','t','u','v', // 5 - 'w','x','y','z','0','1','2','3', // 6 - '4','5','6','7','8','9','+','/' // 7 - }; - - /** - * Encode the data stored in buffer. - * Uses outbuf to store the encoded - * data before writing. - * - * @exception IOException if an I/O error occurs. - */ - private void encode() throws IOException { - int osize = encodedSize(bufsize); - out.write(encode(buffer, 0, bufsize, outbuf), 0, osize); - // increment count - count += osize; - // If writing out this encoded unit caused overflow, - // start a new line. - if (count >= bytesPerLine) { - if (!noCRLF) - out.write(newline); - count = 0; - } - } - - /** - * Base64 encode a byte array. No line breaks are inserted. - * This method is suitable for short strings, such as those - * in the IMAP AUTHENTICATE protocol, but not to encode the - * entire content of a MIME part. - */ - public static byte[] encode(byte[] inbuf) { - if (inbuf.length == 0) - return inbuf; - return encode(inbuf, 0, inbuf.length, null); - } - - /** - * Internal use only version of encode. Allow specifying which - * part of the input buffer to encode. If outbuf is non-null, - * it's used as is. Otherwise, a new output buffer is allocated. - */ - private static byte[] encode(byte[] inbuf, int off, int size, - byte[] outbuf) { - if (outbuf == null) - outbuf = new byte[encodedSize(size)]; - int inpos, outpos; - int val; - for (inpos = off, outpos = 0; size >= 3; size -= 3, outpos += 4) { - val = inbuf[inpos++] & 0xff; - val <<= 8; - val |= inbuf[inpos++] & 0xff; - val <<= 8; - val |= inbuf[inpos++] & 0xff; - outbuf[outpos+3] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+2] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; - } - // done with groups of three, finish up any odd bytes left - if (size == 1) { - val = inbuf[inpos++] & 0xff; - val <<= 4; - outbuf[outpos+3] = (byte)'='; // pad character; - outbuf[outpos+2] = (byte)'='; // pad character; - outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; - } else if (size == 2) { - val = inbuf[inpos++] & 0xff; - val <<= 8; - val |= inbuf[inpos++] & 0xff; - val <<= 2; - outbuf[outpos+3] = (byte)'='; // pad character; - outbuf[outpos+2] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; - val >>= 6; - outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; - } - return outbuf; - } - - /** - * Return the corresponding encoded size for the given number - * of bytes, not including any CRLF. - */ - private static int encodedSize(int size) { - return ((size + 2) / 3) * 4; - } - - /*** begin TEST program - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - BASE64EncoderStream encoder = new BASE64EncoderStream(System.out); - int c; - - while ((c = infile.read()) != -1) - encoder.write(c); - encoder.close(); - } - *** end TEST program **/ -} diff --git a/src/main/java/com/sun/mail/util/BEncoderStream.java b/src/main/java/com/sun/mail/util/BEncoderStream.java deleted file mode 100644 index a659afee..00000000 --- a/src/main/java/com/sun/mail/util/BEncoderStream.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a 'B' Encoder as defined by RFC2047 for - * encoding MIME headers. It subclasses the BASE64EncoderStream - * class. - * - * @author John Mani - */ - -public class BEncoderStream extends BASE64EncoderStream { - - /** - * Create a 'B' encoder that encodes the specified input stream. - * @param out the output stream - */ - public BEncoderStream(OutputStream out) { - super(out, Integer.MAX_VALUE); // MAX_VALUE is 2^31, should - // suffice (!) to indicate that - // CRLFs should not be inserted - } - - /** - * Returns the length of the encoded version of this byte array. - */ - public static int encodedLength(byte[] b) { - return ((b.length + 2)/3) * 4; - } -} diff --git a/src/main/java/com/sun/mail/util/CRLFOutputStream.java b/src/main/java/com/sun/mail/util/CRLFOutputStream.java deleted file mode 100644 index f685b18b..00000000 --- a/src/main/java/com/sun/mail/util/CRLFOutputStream.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - - -/** - * Convert lines into the canonical format, that is, terminate lines with the - * CRLF sequence. - * - * @author John Mani - */ -public class CRLFOutputStream extends FilterOutputStream { - protected int lastb = -1; - protected boolean atBOL = true; // at beginning of line? - private static final byte[] newline = { (byte)'\r', (byte)'\n' }; - - public CRLFOutputStream(OutputStream os) { - super(os); - } - - public void write(int b) throws IOException { - if (b == '\r') { - writeln(); - } else if (b == '\n') { - if (lastb != '\r') - writeln(); - } else { - out.write(b); - atBOL = false; - } - lastb = b; - } - - public void write(byte b[]) throws IOException { - write(b, 0, b.length); - } - - public void write(byte b[], int off, int len) throws IOException { - int start = off; - - len += off; - for (int i = start; i < len ; i++) { - if (b[i] == '\r') { - out.write(b, start, i - start); - writeln(); - start = i + 1; - } else if (b[i] == '\n') { - if (lastb != '\r') { - out.write(b, start, i - start); - writeln(); - } - start = i + 1; - } - lastb = b[i]; - } - if ((len - start) > 0) { - out.write(b, start, len - start); - atBOL = false; - } - } - - /* - * Just write out a new line, something similar to out.println() - */ - public void writeln() throws IOException { - out.write(newline); - atBOL = true; - } -} diff --git a/src/main/java/com/sun/mail/util/DecodingException.java b/src/main/java/com/sun/mail/util/DecodingException.java deleted file mode 100644 index 3593292d..00000000 --- a/src/main/java/com/sun/mail/util/DecodingException.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.IOException; - -/** - * A special IOException that indicates a failure to decode data due - * to an error in the formatting of the data. This allows applications - * to distinguish decoding errors from other I/O errors. - * - * @author Bill Shannon - */ - -public class DecodingException extends IOException { - - private static final long serialVersionUID = -6913647794421459390L; - - /** - * Constructor - */ - public DecodingException(String s) { - super(s); - } -} diff --git a/src/main/java/com/sun/mail/util/FolderClosedIOException.java b/src/main/java/com/sun/mail/util/FolderClosedIOException.java deleted file mode 100644 index 313a648a..00000000 --- a/src/main/java/com/sun/mail/util/FolderClosedIOException.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.IOException; -import javax.mail.Folder; - -/** - * A variant of FolderClosedException that can be thrown from methods - * that only throw IOException. The getContent method will catch this - * exception and translate it back to FolderClosedException. - * - * @author Bill Shannon - */ - -public class FolderClosedIOException extends IOException { - transient private Folder folder; - - private static final long serialVersionUID = 4281122580365555735L; - - /** - * Constructor - * @param folder the Folder - */ - public FolderClosedIOException(Folder folder) { - this(folder, null); - } - - /** - * Constructor - * @param folder the Folder - * @param message the detailed error message - */ - public FolderClosedIOException(Folder folder, String message) { - super(message); - this.folder = folder; - } - - /** - * Returns the dead Folder object - */ - public Folder getFolder() { - return folder; - } -} diff --git a/src/main/java/com/sun/mail/util/LineInputStream.java b/src/main/java/com/sun/mail/util/LineInputStream.java deleted file mode 100644 index 6787fac3..00000000 --- a/src/main/java/com/sun/mail/util/LineInputStream.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class is to support reading CRLF terminated lines that - * contain only US-ASCII characters from an input stream. Provides - * functionality that is similar to the deprecated - * DataInputStream.readLine(). Expected use is to read - * lines as String objects from a RFC822 stream. - * - * It is implemented as a FilterInputStream, so one can just wrap - * this class around any input stream and read bytes from this filter. - * - * @author John Mani - */ - -public class LineInputStream extends FilterInputStream { - - private char[] lineBuffer = null; // reusable byte buffer - private static int MAX_INCR = 1024*1024; // 1MB - - public LineInputStream(InputStream in) { - super(in); - } - - /** - * Read a line containing only ASCII characters from the input - * stream. A line is terminated by a CR or NL or CR-NL sequence. - * A common error is a CR-CR-NL sequence, which will also terminate - * a line. - * The line terminator is not returned as part of the returned - * String. Returns null if no data is available.

    - * - * This class is similar to the deprecated - * DataInputStream.readLine() - */ - public String readLine() throws IOException { - //InputStream in = this.in; - char[] buf = lineBuffer; - - if (buf == null) - buf = lineBuffer = new char[128]; - - int c1; - int room = buf.length; - int offset = 0; - - while ((c1 = in.read()) != -1) { - if (c1 == '\n') // Got NL, outa here. - break; - else if (c1 == '\r') { - // Got CR, is the next char NL ? - boolean twoCRs = false; - if (in.markSupported()) - in.mark(2); - int c2 = in.read(); - if (c2 == '\r') { // discard extraneous CR - twoCRs = true; - c2 = in.read(); - } - if (c2 != '\n') { - /* - * If the stream supports it (which we hope will always - * be the case), reset to after the first CR. Otherwise, - * we wrap a PushbackInputStream around the stream so we - * can unread the characters we don't need. The only - * problem with that is that the caller might stop - * reading from this LineInputStream, throw it away, - * and then start reading from the underlying stream. - * If that happens, the pushed back characters will be - * lost forever. - */ - if (in.markSupported()) - in.reset(); - else { - if (!(in instanceof PushbackInputStream)) - in /*= this.in*/ = new PushbackInputStream(in, 2); - if (c2 != -1) - ((PushbackInputStream)in).unread(c2); - if (twoCRs) - ((PushbackInputStream)in).unread('\r'); - } - } - break; // outa here. - } - - // Not CR, NL or CR-NL ... - // .. Insert the byte into our byte buffer - if (--room < 0) { // No room, need to grow. - if (buf.length < MAX_INCR) - buf = new char[buf.length * 2]; - else - buf = new char[buf.length + MAX_INCR]; - room = buf.length - offset - 1; - System.arraycopy(lineBuffer, 0, buf, 0, offset); - lineBuffer = buf; - } - buf[offset++] = (char)c1; - } - - if ((c1 == -1) && (offset == 0)) - return null; - - return String.copyValueOf(buf, 0, offset); - } -} diff --git a/src/main/java/com/sun/mail/util/LineOutputStream.java b/src/main/java/com/sun/mail/util/LineOutputStream.java deleted file mode 100644 index 6586f682..00000000 --- a/src/main/java/com/sun/mail/util/LineOutputStream.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class is to support writing out Strings as a sequence of bytes - * terminated by a CRLF sequence. The String must contain only US-ASCII - * characters.

    - * - * The expected use is to write out RFC822 style headers to an output - * stream.

    - * - * @author John Mani - */ - -public class LineOutputStream extends FilterOutputStream { - private static byte[] newline; - - static { - newline = new byte[2]; - newline[0] = (byte)'\r'; - newline[1] = (byte)'\n'; - } - - public LineOutputStream(OutputStream out) { - super(out); - } - - public void writeln(String s) throws IOException { - byte[] bytes = ASCIIUtility.getBytes(s); - out.write(bytes); - out.write(newline); - } - - public void writeln() throws IOException { - out.write(newline); - } -} diff --git a/src/main/java/com/sun/mail/util/LogOutputStream.java b/src/main/java/com/sun/mail/util/LogOutputStream.java deleted file mode 100644 index dc372e2c..00000000 --- a/src/main/java/com/sun/mail/util/LogOutputStream.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2008-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.logging.Level; - -/** - * Capture output lines and send them to the mail logger. - */ -public class LogOutputStream extends OutputStream { - protected MailLogger logger; - protected Level level; - - private int lastb = -1; - private byte[] buf = new byte[80]; - private int pos = 0; - - /** - * Log to the specified logger. - */ - public LogOutputStream(MailLogger logger) { - this.logger = logger; - this.level = Level.FINEST; - } - - public void write(int b) throws IOException { - if (!logger.isLoggable(level)) - return; - - if (b == '\r') { - logBuf(); - } else if (b == '\n') { - if (lastb != '\r') - logBuf(); - } else { - expandCapacity(1); - buf[pos++] = (byte)b; - } - lastb = b; - } - - public void write(byte b[]) throws IOException { - write(b, 0, b.length); - } - - public void write(byte b[], int off, int len) throws IOException { - int start = off; - - if (!logger.isLoggable(level)) - return; - len += off; - for (int i = start; i < len ; i++) { - if (b[i] == '\r') { - expandCapacity(i - start); - System.arraycopy(b, start, buf, pos, i - start); - pos += i - start; - logBuf(); - start = i + 1; - } else if (b[i] == '\n') { - if (lastb != '\r') { - expandCapacity(i - start); - System.arraycopy(b, start, buf, pos, i - start); - pos += i - start; - logBuf(); - } - start = i + 1; - } - lastb = b[i]; - } - if ((len - start) > 0) { - expandCapacity(len - start); - System.arraycopy(b, start, buf, pos, len - start); - pos += len - start; - } - } - - /** - * Log the specified message. - * Can be overridden by subclass to do different logging. - */ - protected void log(String msg) { - logger.log(level, msg); - } - - /** - * Convert the buffer to a string and log it. - */ - private void logBuf() { - String msg = new String(buf, 0, pos); - pos = 0; - log(msg); - } - - /** - * Ensure that the buffer can hold at least len bytes - * beyond the current position. - */ - private void expandCapacity(int len) { - while (pos + len > buf.length) { - byte[] nb = new byte[buf.length * 2]; - System.arraycopy(buf, 0, nb, 0, pos); - buf = nb; - } - } -} diff --git a/src/main/java/com/sun/mail/util/MailConnectException.java b/src/main/java/com/sun/mail/util/MailConnectException.java deleted file mode 100644 index 4b83293f..00000000 --- a/src/main/java/com/sun/mail/util/MailConnectException.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import javax.mail.MessagingException; - -/** - * A MessagingException that indicates a socket connection attempt failed. - * Unlike java.net.ConnectException, it includes details of what we - * were trying to connect to. The underlying exception is available - * as the "cause" of this exception. - * - * @see java.net.ConnectException - * @author Bill Shannon - * @since JavaMail 1.5.0 - */ - -public class MailConnectException extends MessagingException { - private String host; - private int port; - private int cto; - - private static final long serialVersionUID = -3818807731125317729L; - - /** - * Constructs a MailConnectException. - * - * @param cex the SocketConnectException with the details - */ - public MailConnectException(SocketConnectException cex) { - super( - "Couldn't connect to host, port: " + - cex.getHost() + ", " + cex.getPort() + - "; timeout " + cex.getConnectionTimeout() + - (cex.getMessage() != null ? ("; " + cex.getMessage()) : "")); - // extract the details and save them here - this.host = cex.getHost(); - this.port = cex.getPort(); - this.cto = cex.getConnectionTimeout(); - setNextException(cex.getException()); - } - - /** - * The host we were trying to connect to. - * - * @return the host - */ - public String getHost() { - return host; - } - - /** - * The port we were trying to connect to. - * - * @return the port - */ - public int getPort() { - return port; - } - - /** - * The timeout used for the connection attempt. - * - * @return the connection timeout - */ - public int getConnectionTimeout() { - return cto; - } -} diff --git a/src/main/java/com/sun/mail/util/MailLogger.java b/src/main/java/com/sun/mail/util/MailLogger.java deleted file mode 100644 index 2354d280..00000000 --- a/src/main/java/com/sun/mail/util/MailLogger.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.PrintStream; -import java.text.MessageFormat; -import java.util.logging.Logger; -import java.util.logging.Level; - -import javax.mail.Session; - -/** - * A simplified logger used by JavaMail to handle logging to a - * PrintStream and logging through a java.util.logging.Logger. - * If debug is set, messages are written to the PrintStream and - * prefixed by the specified prefix (which is not included in - * Logger messages). - * Messages are logged by the Logger based on the configuration - * of the logging system. - */ - -/* - * It would be so much simpler to just subclass Logger and override - * the log(LogRecord) method, as the javadocs suggest, but that doesn't - * work because Logger makes the decision about whether to log the message - * or not before calling the log(LogRecord) method. Instead, we just - * provide the few log methods we need here. - */ - -public final class MailLogger { - private final Logger logger; // for log messages - private final String prefix; // for debug output - private final boolean debug; // produce debug output? - private final PrintStream out; // stream for debug output - - /** - * Construct a new MailLogger using the specified Logger name, - * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. - * - * @param name the Logger name - * @param prefix the prefix for debug output, or null for none - * @param debug if true, write to PrintStream - * @param out the PrintStream to write to - */ - public MailLogger(String name, String prefix, boolean debug, - PrintStream out) { - logger = Logger.getLogger(name); - this.prefix = prefix; - this.debug = debug; - this.out = out != null ? out : System.out; - } - - /** - * Construct a new MailLogger using the specified class' package - * name as the Logger name, - * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. - * - * @param clazz the Logger name is the package name of this class - * @param prefix the prefix for debug output, or null for none - * @param debug if true, write to PrintStream - * @param out the PrintStream to write to - */ - public MailLogger(Class clazz, String prefix, boolean debug, - PrintStream out) { - String name = packageOf(clazz); - logger = Logger.getLogger(name); - this.prefix = prefix; - this.debug = debug; - this.out = out != null ? out : System.out; - } - - /** - * Construct a new MailLogger using the specified class' package - * name combined with the specified subname as the Logger name, - * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. - * - * @param clazz the Logger name is the package name of this class - * @param subname the Logger name relative to this Logger name - * @param prefix the prefix for debug output, or null for none - * @param debug if true, write to PrintStream - * @param out the PrintStream to write to - */ - public MailLogger(Class clazz, String subname, String prefix, boolean debug, - PrintStream out) { - String name = packageOf(clazz) + "." + subname; - logger = Logger.getLogger(name); - this.prefix = prefix; - this.debug = debug; - this.out = out != null ? out : System.out; - } - - /** - * Construct a new MailLogger using the specified Logger name and - * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream - * from the Session. - * - * @param name the Logger name - * @param prefix the prefix for debug output, or null for none - * @param session where to get the debug flag and PrintStream - */ - public MailLogger(String name, String prefix, Session session) { - this(name, prefix, session.getDebug(), session.getDebugOut()); - } - - /** - * Construct a new MailLogger using the specified class' package - * name as the Logger name and the specified - * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream - * from the Session. - * - * @param clazz the Logger name is the package name of this class - * @param prefix the prefix for debug output, or null for none - * @param session where to get the debug flag and PrintStream - */ - public MailLogger(Class clazz, String prefix, Session session) { - this(clazz, prefix, session.getDebug(), session.getDebugOut()); - } - - /** - * Create a MailLogger that uses a Logger with the specified name - * and prefix. The new MailLogger uses the same debug flag and - * PrintStream as this MailLogger. - * - * @param name the Logger name - * @param prefix the prefix for debug output, or null for none - */ - public MailLogger getLogger(String name, String prefix) { - return new MailLogger(name, prefix, debug, out); - } - - /** - * Create a MailLogger using the specified class' package - * name as the Logger name and the specified prefix. - * The new MailLogger uses the same debug flag and - * PrintStream as this MailLogger. - * - * @param clazz the Logger name is the package name of this class - * @param prefix the prefix for debug output, or null for none - */ - public MailLogger getLogger(Class clazz, String prefix) { - return new MailLogger(clazz, prefix, debug, out); - } - - /** - * Create a MailLogger that uses a Logger whose name is composed - * of this MailLogger's name plus the specified sub-name, separated - * by a dot. The new MailLogger uses the new prefix for debug output. - * This is used primarily by the protocol trace code that wants a - * different prefix (none). - * - * @param subname the Logger name relative to this Logger name - * @param prefix the prefix for debug output, or null for none - */ - public MailLogger getSubLogger(String subname, String prefix) { - return new MailLogger(logger.getName() + "." + subname, prefix, - debug, out); - } - - /** - * Create a MailLogger that uses a Logger whose name is composed - * of this MailLogger's name plus the specified sub-name, separated - * by a dot. The new MailLogger uses the new prefix for debug output. - * This is used primarily by the protocol trace code that wants a - * different prefix (none). - * - * @param subname the Logger name relative to this Logger name - * @param prefix the prefix for debug output, or null for none - * @param debug the debug flag for the sub-logger - */ - public MailLogger getSubLogger(String subname, String prefix, - boolean debug) { - return new MailLogger(logger.getName() + "." + subname, prefix, - debug, out); - } - - /** - * Log the message at the specified level. - */ - public void log(Level level, String msg) { - ifDebugOut(msg); - if (logger.isLoggable(level)) { - final String[] frame = inferCaller(); - logger.logp(level, frame[0], frame[1], msg); - } - } - - /** - * Log the message at the specified level. - */ - public void log(Level level, String msg, Object param1) { - if (debug) { - msg = MessageFormat.format(msg, new Object[] { param1 }); - debugOut(msg); - } - - if (logger.isLoggable(level)) { - final String[] frame = inferCaller(); - logger.logp(level, frame[0], frame[1], msg, param1); - } - } - - /** - * Log the message at the specified level. - */ - public void log(Level level, String msg, Object params[]) { - if (debug) { - msg = MessageFormat.format(msg, params); - debugOut(msg); - } - - if (logger.isLoggable(level)) { - final String[] frame = inferCaller(); - logger.logp(level, frame[0], frame[1], msg, params); - } - } - - /* - * Maybe for JavaMail 1.5... - * - public void logf(Level level, String msg, Object... params) { - msg = String.format(msg, params); - ifDebugOut(msg); - logger.log(level, msg); - } - */ - - /** - * Log the message at the specified level. - */ - public void log(Level level, String msg, Throwable thrown) { - if (debug) { - if (thrown != null) { - debugOut(msg + ", THROW: "); - thrown.printStackTrace(out); - } else { - debugOut(msg); - } - } - - if (logger.isLoggable(level)) { - final String[] frame = inferCaller(); - logger.logp(level, frame[0], frame[1], msg, thrown); - } - } - - /** - * Log a message at the CONFIG level. - */ - public void config(String msg) { - log(Level.CONFIG, msg); - } - - /** - * Log a message at the FINE level. - */ - public void fine(String msg) { - log(Level.FINE, msg); - } - - /** - * Log a message at the FINER level. - */ - public void finer(String msg) { - log(Level.FINER, msg); - } - - /** - * Log a message at the FINEST level. - */ - public void finest(String msg) { - log(Level.FINEST, msg); - } - - /** - * If "debug" is set, or our embedded Logger is loggable at the - * given level, return true. - */ - public boolean isLoggable(Level level) { - return debug || logger.isLoggable(level); - } - - private final void ifDebugOut(String msg) { - if (debug) - debugOut(msg); - } - - private final void debugOut(String msg) { - if (prefix != null) - out.println(prefix + ": " + msg); - else - out.println(msg); - } - - /** - * Return the package name of the class. - * Sometimes there will be no Package object for the class, - * e.g., if the class loader hasn't created one (see Class.getPackage()). - */ - private String packageOf(Class clazz) { - Package p = clazz.getPackage(); - if (p != null) - return p.getName(); // hopefully the common case - String cname = clazz.getName(); - int i = cname.lastIndexOf('.'); - if (i > 0) - return cname.substring(0, i); - // no package name, now what? - return ""; - } - - /* - * A disadvantage of not being able to use Logger directly in JavaMail - * code is that the "source class" information that Logger guesses will - * always refer to this class instead of our caller. This method - * duplicates what Logger does to try to find *our* caller, so that - * Logger doesn't have to do it (and get the wrong answer), and because - * our caller is what's wanted. - */ - private String[] inferCaller() { - // Get the stack trace. - StackTraceElement stack[] = (new Throwable()).getStackTrace(); - // First, search back to a method in the Logger class. - int ix = 0; - while (ix < stack.length) { - StackTraceElement frame = stack[ix]; - String cname = frame.getClassName(); - if (isLoggerImplFrame(cname)) { - break; - } - ix++; - } - // Now search for the first frame before the "Logger" class. - while (ix < stack.length) { - StackTraceElement frame = stack[ix]; - String cname = frame.getClassName(); - if (!isLoggerImplFrame(cname)) { - // We've found the relevant frame. - return new String[]{cname, frame.getMethodName()}; - } - ix++; - } - // We haven't found a suitable frame, so just punt. This is - // OK as we are only committed to making a "best effort" here. - return new String[]{null, null}; - } - - private boolean isLoggerImplFrame(String cname) { - return MailLogger.class.getName().equals(cname); - } -} diff --git a/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java b/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java deleted file mode 100644 index 0ec24a46..00000000 --- a/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; -import java.net.*; -import java.security.*; -import java.security.cert.*; -import java.util.*; - -import javax.net.ssl.*; - -/** - * An SSL socket factory that makes it easier to specify trust. - * This socket factory can be configured to trust all hosts or - * trust a specific set of hosts, in which case the server's - * certificate isn't verified. Alternatively, a custom TrustManager - * can be supplied.

    - * - * An instance of this factory can be set as the value of the - * mail.<protocol>.ssl.socketFactory property. - * - * @since JavaMail 1.4.2 - * @author Stephan Sann - * @author Bill Shannon - */ -public class MailSSLSocketFactory extends SSLSocketFactory { - - /** Should all hosts be trusted? */ - private boolean trustAllHosts; - - /** String-array of trusted hosts */ - private String[] trustedHosts = null; - - /** Holds a SSLContext to get SSLSocketFactories from */ - private SSLContext sslcontext; - - /** Holds the KeyManager array to use */ - private KeyManager[] keyManagers; - - /** Holds the TrustManager array to use */ - private TrustManager[] trustManagers; - - /** Holds the SecureRandom to use */ - private SecureRandom secureRandom; - - /** Holds a SSLSocketFactory to pass all API-method-calls to */ - private SSLSocketFactory adapteeFactory = null; - - /** - * Initializes a new MailSSLSocketFactory. - * - * @throws GeneralSecurityException - */ - public MailSSLSocketFactory() throws GeneralSecurityException { - this("TLS"); - } - - /** - * Initializes a new MailSSLSocketFactory with a given protocol. - * Normally the protocol will be specified as "TLS". - * - * @param protocol The protocol to use - * @throws NoSuchAlgorithmException if given protocol is not supported - */ - public MailSSLSocketFactory(String protocol) - throws GeneralSecurityException { - - // By default we do NOT trust all hosts. - trustAllHosts = false; - - // Get an instance of an SSLContext. - sslcontext = SSLContext.getInstance(protocol); - - // Default properties to init the SSLContext - keyManagers = null; - trustManagers = new TrustManager[] { new MailTrustManager() }; - secureRandom = null; - - // Assemble a default SSLSocketFactory to delegate all API-calls to. - newAdapteeFactory(); - } - - - /** - * Gets an SSLSocketFactory based on the given (or default) - * KeyManager array, TrustManager array and SecureRandom and - * sets it to the instance var adapteeFactory. - */ - private synchronized void newAdapteeFactory() - throws KeyManagementException { - sslcontext.init(keyManagers, trustManagers, secureRandom); - - // Get SocketFactory and save it in our instance var - adapteeFactory = (SSLSocketFactory)sslcontext.getSocketFactory(); - } - - /** - * @return the keyManagers - */ - public synchronized KeyManager[] getKeyManagers() { - return (KeyManager[])keyManagers.clone(); - } - - /** - * @param keyManagers the keyManagers to set - */ - public synchronized void setKeyManagers(KeyManager[] keyManagers) - throws GeneralSecurityException { - this.keyManagers = (KeyManager[])keyManagers.clone(); - newAdapteeFactory(); - } - - /** - * @return the secureRandom - */ - public synchronized SecureRandom getSecureRandom() { - return secureRandom; - } - - /** - * @param secureRandom the secureRandom to set - */ - public synchronized void setSecureRandom(SecureRandom secureRandom) - throws GeneralSecurityException { - this.secureRandom = secureRandom; - newAdapteeFactory(); - } - - /** - * @return the trustManagers - */ - public synchronized TrustManager[] getTrustManagers() { - return trustManagers; - } - - /** - * @param trustManagers the trustManagers to set - */ - public synchronized void setTrustManagers(TrustManager[] trustManagers) - throws GeneralSecurityException { - this.trustManagers = trustManagers; - newAdapteeFactory(); - } - - /** - * @return true if all hosts should be trusted - */ - public synchronized boolean isTrustAllHosts() { - return trustAllHosts; - } - - /** - * @param trustAllHosts should all hosts be trusted? - */ - public synchronized void setTrustAllHosts(boolean trustAllHosts) { - this.trustAllHosts = trustAllHosts; - } - - /** - * @return the trusted hosts - */ - public synchronized String[] getTrustedHosts() { - return (String[])trustedHosts.clone(); - } - - /** - * @param trustedHosts the hosts to trust - */ - public synchronized void setTrustedHosts(String[] trustedHosts) { - this.trustedHosts = (String[])trustedHosts.clone(); - } - - /** - * After a successful conection to the server, this method is - * called to ensure that the server should be trusted. - * - * @param server name of the server we connected to - * @param sslSocket SSLSocket connected to the server - * @return true if "trustAllHosts" is set to true OR the server - * is contained in the "trustedHosts" array; - */ - public synchronized boolean isServerTrusted(String server, - SSLSocket sslSocket) { - - //System.out.println("DEBUG: isServerTrusted host " + server); - - // If "trustAllHosts" is set to true, we return true - if (trustAllHosts) - return true; - - // If the socket host is contained in the "trustedHosts" array, - // we return true - if (trustedHosts != null) - return Arrays.asList(trustedHosts).contains(server); // ignore case? - - // If we get here, trust of the server was verified by the trust manager - return true; - } - - - // SocketFactory methods - - /* (non-Javadoc) - * @see javax.net.ssl.SSLSocketFactory#createSocket(java.net.Socket, - * java.lang.String, int, boolean) - */ - //@Override - public synchronized Socket createSocket(Socket socket, String s, int i, - boolean flag) throws IOException { - return adapteeFactory.createSocket(socket, s, i, flag); - } - - /* (non-Javadoc) - * @see javax.net.ssl.SSLSocketFactory#getDefaultCipherSuites() - */ - //@Override - public synchronized String[] getDefaultCipherSuites() { - return adapteeFactory.getDefaultCipherSuites(); - } - - /* (non-Javadoc) - * @see javax.net.ssl.SSLSocketFactory#getSupportedCipherSuites() - */ - //@Override - public synchronized String[] getSupportedCipherSuites() { - return adapteeFactory.getSupportedCipherSuites(); - } - - /* (non-Javadoc) - * @see javax.net.SocketFactory#createSocket() - */ - //@Override - public synchronized Socket createSocket() throws IOException { - return adapteeFactory.createSocket(); - } - - /* (non-Javadoc) - * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int, - * java.net.InetAddress, int) - */ - //@Override - public synchronized Socket createSocket(InetAddress inetaddress, int i, - InetAddress inetaddress1, int j) throws IOException { - return adapteeFactory.createSocket(inetaddress, i, inetaddress1, j); - } - - /* (non-Javadoc) - * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int) - */ - //@Override - public synchronized Socket createSocket(InetAddress inetaddress, int i) - throws IOException { - return adapteeFactory.createSocket(inetaddress, i); - } - - /* (non-Javadoc) - * @see javax.net.SocketFactory#createSocket(java.lang.String, int, - * java.net.InetAddress, int) - */ - //@Override - public synchronized Socket createSocket(String s, int i, - InetAddress inetaddress, int j) - throws IOException, UnknownHostException { - return adapteeFactory.createSocket(s, i, inetaddress, j); - } - - /* (non-Javadoc) - * @see javax.net.SocketFactory#createSocket(java.lang.String, int) - */ - //@Override - public synchronized Socket createSocket(String s, int i) - throws IOException, UnknownHostException { - return adapteeFactory.createSocket(s, i); - } - - - // inner classes - - /** - * A default Trustmanager. - * - * @author Stephan Sann - */ - private class MailTrustManager implements X509TrustManager { - - /** A TrustManager to pass method calls to */ - private X509TrustManager adapteeTrustManager = null; - - /** - * Initializes a new TrustManager instance. - */ - private MailTrustManager() throws GeneralSecurityException { - TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); - tmf.init((KeyStore)null); - adapteeTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; - } - - /* (non-Javadoc) - * @see javax.net.ssl.X509TrustManager#checkClientTrusted( - * java.security.cert.X509Certificate[], java.lang.String) - */ - public void checkClientTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - if (!(isTrustAllHosts() || getTrustedHosts() != null)) - adapteeTrustManager.checkClientTrusted(certs, authType); - } - - /* (non-Javadoc) - * @see javax.net.ssl.X509TrustManager#checkServerTrusted( - * java.security.cert.X509Certificate[], java.lang.String) - */ - public void checkServerTrusted(X509Certificate[] certs, String authType) - throws CertificateException { - - if (!(isTrustAllHosts() || getTrustedHosts() != null)) - adapteeTrustManager.checkServerTrusted(certs, authType); - } - - /* (non-Javadoc) - * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() - */ - public X509Certificate[] getAcceptedIssuers() { - return adapteeTrustManager.getAcceptedIssuers(); - } - } -} diff --git a/src/main/java/com/sun/mail/util/MessageRemovedIOException.java b/src/main/java/com/sun/mail/util/MessageRemovedIOException.java deleted file mode 100644 index c4e65199..00000000 --- a/src/main/java/com/sun/mail/util/MessageRemovedIOException.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.IOException; - -/** - * A variant of MessageRemovedException that can be thrown from methods - * that only throw IOException. The getContent method will catch this - * exception and translate it back to MessageRemovedException. - * - * @see javax.mail.Message#isExpunged() - * @see javax.mail.Message#getMessageNumber() - * @author Bill Shannon - */ - -public class MessageRemovedIOException extends IOException { - - private static final long serialVersionUID = 4280468026581616424L; - - /** - * Constructs a MessageRemovedIOException with no detail message. - */ - public MessageRemovedIOException() { - super(); - } - - /** - * Constructs a MessageRemovedIOException with the specified detail message. - * @param s the detail message - */ - public MessageRemovedIOException(String s) { - super(s); - } -} diff --git a/src/main/java/com/sun/mail/util/MimeUtil.java b/src/main/java/com/sun/mail/util/MimeUtil.java deleted file mode 100644 index f3252210..00000000 --- a/src/main/java/com/sun/mail/util/MimeUtil.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.lang.reflect.*; -import java.security.*; - -import javax.mail.internet.MimePart; - -/** - * General MIME-related utility methods. - * - * @author Bill Shannon - * @since JavaMail 1.4.4 - */ -public class MimeUtil { - - private static final Method cleanContentType; - - static { - Method meth = null; - try { - String cth = System.getProperty("mail.mime.contenttypehandler"); - if (cth != null) { - ClassLoader cl = getContextClassLoader(); - Class clsHandler = null; - if (cl != null) { - try { - clsHandler = Class.forName(cth, false, cl); - } catch (ClassNotFoundException cex) { } - } - if (clsHandler == null) - clsHandler = Class.forName(cth); - meth = clsHandler.getMethod("cleanContentType", - new Class[] { MimePart.class, String.class }); - } - } catch (ClassNotFoundException ex) { - // ignore it - } catch (NoSuchMethodException ex) { - // ignore it - } catch (RuntimeException ex) { - // ignore it - } finally { - cleanContentType = meth; - } - } - - // No one should instantiate this class. - private MimeUtil() { - } - - /** - * If a Content-Type handler has been specified, - * call it to clean up the Content-Type value. - */ - public static String cleanContentType(MimePart mp, String contentType) { - if (cleanContentType != null) { - try { - return (String)cleanContentType.invoke(null, - new Object[] { mp, contentType }); - } catch (Exception ex) { - return contentType; - } - } else - return contentType; - } - - /** - * Convenience method to get our context class loader. - * Assert any privileges we might have and then call the - * Thread.getContextClassLoader method. - */ - private static ClassLoader getContextClassLoader() { - return (ClassLoader) - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } catch (SecurityException ex) { } - return cl; - } - }); - } -} diff --git a/src/main/java/com/sun/mail/util/PropUtil.java b/src/main/java/com/sun/mail/util/PropUtil.java deleted file mode 100644 index ec1e1c4d..00000000 --- a/src/main/java/com/sun/mail/util/PropUtil.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.util.*; -import javax.mail.Session; - -/** - * Utilities to make it easier to get property values. - * Properties can be strings or type-specific value objects. - * - * @author Bill Shannon - */ -public class PropUtil { - - // No one should instantiate this class. - private PropUtil() { - } - - /** - * Get an integer valued property. - */ - public static int getIntProperty(Properties props, String name, int def) { - return getInt(getProp(props, name), def); - } - - /** - * Get a boolean valued property. - */ - public static boolean getBooleanProperty(Properties props, - String name, boolean def) { - return getBoolean(getProp(props, name), def); - } - - /** - * Get an integer valued property. - */ - public static int getIntSessionProperty(Session session, - String name, int def) { - return getInt(getProp(session.getProperties(), name), def); - } - - /** - * Get a boolean valued property. - */ - public static boolean getBooleanSessionProperty(Session session, - String name, boolean def) { - return getBoolean(getProp(session.getProperties(), name), def); - } - - /** - * Get a boolean valued System property. - */ - public static boolean getBooleanSystemProperty(String name, boolean def) { - try { - return getBoolean(getProp(System.getProperties(), name), def); - } catch (SecurityException sex) { - // fall through... - } - - /* - * If we can't get the entire System Properties object because - * of a SecurityException, just ask for the specific property. - */ - try { - String value = System.getProperty(name); - if (value == null) - return def; - if (def) - return !value.equalsIgnoreCase("false"); - else - return value.equalsIgnoreCase("true"); - } catch (SecurityException sex) { - return def; - } - } - - /** - * Get the value of the specified property. - * If the "get" method returns null, use the getProperty method, - * which might cascade to a default Properties object. - */ - private static Object getProp(Properties props, String name) { - Object val = props.get(name); - if (val != null) - return val; - else - return props.getProperty(name); - } - - /** - * Interpret the value object as an integer, - * returning def if unable. - */ - private static int getInt(Object value, int def) { - if (value == null) - return def; - if (value instanceof String) { - try { - return Integer.parseInt((String)value); - } catch (NumberFormatException nfex) { } - } - if (value instanceof Integer) - return ((Integer)value).intValue(); - return def; - } - - /** - * Interpret the value object as a boolean, - * returning def if unable. - */ - private static boolean getBoolean(Object value, boolean def) { - if (value == null) - return def; - if (value instanceof String) { - /* - * If the default is true, only "false" turns it off. - * If the default is false, only "true" turns it on. - */ - if (def) - return !((String)value).equalsIgnoreCase("false"); - else - return ((String)value).equalsIgnoreCase("true"); - } - if (value instanceof Boolean) - return ((Boolean)value).booleanValue(); - return def; - } -} diff --git a/src/main/java/com/sun/mail/util/QDecoderStream.java b/src/main/java/com/sun/mail/util/QDecoderStream.java deleted file mode 100644 index a0654d95..00000000 --- a/src/main/java/com/sun/mail/util/QDecoderStream.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a Q Decoder as defined in RFC 2047 - * for decoding MIME headers. It subclasses the QPDecoderStream class. - * - * @author John Mani - */ - -public class QDecoderStream extends QPDecoderStream { - - /** - * Create a Q-decoder that decodes the specified input stream. - * @param in the input stream - */ - public QDecoderStream(InputStream in) { - super(in); - } - - /** - * Read the next decoded byte from this input stream. The byte - * is returned as an int in the range 0 - * to 255. If no byte is available because the end of - * the stream has been reached, the value -1 is returned. - * This method blocks until input data is available, the end of the - * stream is detected, or an exception is thrown. - * - * @return the next byte of data, or -1 if the end of the - * stream is reached. - * @exception IOException if an I/O error occurs. - */ - public int read() throws IOException { - int c = in.read(); - - if (c == '_') // Return '_' as ' ' - return ' '; - else if (c == '=') { - // QP Encoded atom. Get the next two bytes .. - ba[0] = (byte)in.read(); - ba[1] = (byte)in.read(); - // .. and decode them - try { - return ASCIIUtility.parseInt(ba, 0, 2, 16); - } catch (NumberFormatException nex) { - throw new DecodingException( - "QDecoder: Error in QP stream " + nex.getMessage()); - } - } else - return c; - } -} diff --git a/src/main/java/com/sun/mail/util/QEncoderStream.java b/src/main/java/com/sun/mail/util/QEncoderStream.java deleted file mode 100644 index ec5744f4..00000000 --- a/src/main/java/com/sun/mail/util/QEncoderStream.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a Q Encoder as defined by RFC 2047 for - * encoding MIME headers. It subclasses the QPEncoderStream class. - * - * @author John Mani - */ - -public class QEncoderStream extends QPEncoderStream { - - private String specials; - private static String WORD_SPECIALS = "=_?\"#$%&'(),.:;<>@[\\]^`{|}~"; - private static String TEXT_SPECIALS = "=_?"; - - /** - * Create a Q encoder that encodes the specified input stream - * @param out the output stream - * @param encodingWord true if we are Q-encoding a word within a - * phrase. - */ - public QEncoderStream(OutputStream out, boolean encodingWord) { - super(out, Integer.MAX_VALUE); // MAX_VALUE is 2^31, should - // suffice (!) to indicate that - // CRLFs should not be inserted - // when encoding rfc822 headers - - // a RFC822 "word" token has more restrictions than a - // RFC822 "text" token. - specials = encodingWord ? WORD_SPECIALS : TEXT_SPECIALS; - } - - /** - * Encodes the specified byte to this output stream. - * @param c the byte. - * @exception IOException if an I/O error occurs. - */ - public void write(int c) throws IOException { - c = c & 0xff; // Turn off the MSB. - if (c == ' ') - output('_', false); - else if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0) - // Encoding required. - output(c, true); - else // No encoding required - output(c, false); - } - - /** - * Returns the length of the encoded version of this byte array. - */ - public static int encodedLength(byte[] b, boolean encodingWord) { - int len = 0; - String specials = encodingWord ? WORD_SPECIALS: TEXT_SPECIALS; - for (int i = 0; i < b.length; i++) { - int c = b[i] & 0xff; // Mask off MSB - if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0) - // needs encoding - len += 3; // Q-encoding is 1 -> 3 conversion - else - len++; - } - return len; - } - - /**** begin TEST program *** - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - QEncoderStream encoder = new QEncoderStream(System.out); - int c; - - while ((c = infile.read()) != -1) - encoder.write(c); - encoder.close(); - } - *** end TEST program ***/ -} diff --git a/src/main/java/com/sun/mail/util/QPDecoderStream.java b/src/main/java/com/sun/mail/util/QPDecoderStream.java deleted file mode 100644 index 8760cd67..00000000 --- a/src/main/java/com/sun/mail/util/QPDecoderStream.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a QP Decoder. It is implemented as - * a FilterInputStream, so one can just wrap this class around - * any input stream and read bytes from this filter. The decoding - * is done as the bytes are read out. - * - * @author John Mani - */ - -public class QPDecoderStream extends FilterInputStream { - protected byte[] ba = new byte[2]; - protected int spaces = 0; - - /** - * Create a Quoted Printable decoder that decodes the specified - * input stream. - * @param in the input stream - */ - public QPDecoderStream(InputStream in) { - super(new PushbackInputStream(in, 2)); // pushback of size=2 - } - - /** - * Read the next decoded byte from this input stream. The byte - * is returned as an int in the range 0 - * to 255. If no byte is available because the end of - * the stream has been reached, the value -1 is returned. - * This method blocks until input data is available, the end of the - * stream is detected, or an exception is thrown. - * - * @return the next byte of data, or -1 if the end of the - * stream is reached. - * @exception IOException if an I/O error occurs. - */ - public int read() throws IOException { - if (spaces > 0) { - // We have cached space characters, return one - spaces--; - return ' '; - } - - int c = in.read(); - - if (c == ' ') { - // Got space, keep reading till we get a non-space char - while ((c = in.read()) == ' ') - spaces++; - - if (c == '\r' || c == '\n' || c == -1) - // If the non-space char is CR/LF/EOF, the spaces we got - // so far is junk introduced during transport. Junk 'em. - spaces = 0; - else { - // The non-space char is NOT CR/LF, the spaces are valid. - ((PushbackInputStream)in).unread(c); - c = ' '; - } - return c; // return either or - } - else if (c == '=') { - // QP Encoded atom. Decode the next two bytes - int a = in.read(); - - if (a == '\n') { - /* Hmm ... not really confirming QP encoding, but lets - * allow this as a LF terminated encoded line .. and - * consider this a soft linebreak and recurse to fetch - * the next char. - */ - return read(); - } else if (a == '\r') { - // Expecting LF. This forms a soft linebreak to be ignored. - int b = in.read(); - if (b != '\n') - /* Not really confirming QP encoding, but - * lets allow this as well. - */ - ((PushbackInputStream)in).unread(b); - return read(); - } else if (a == -1) { - // Not valid QP encoding, but we be nice and tolerant here ! - return -1; - } else { - ba[0] = (byte)a; - ba[1] = (byte)in.read(); - try { - return ASCIIUtility.parseInt(ba, 0, 2, 16); - } catch (NumberFormatException nex) { - /* - System.err.println( - "Illegal characters in QP encoded stream: " + - ASCIIUtility.toString(ba, 0, 2) - ); - */ - - ((PushbackInputStream)in).unread(ba); - return c; - } - } - } - return c; - } - - /** - * Reads up to len decoded bytes of data from this input stream - * into an array of bytes. This method blocks until some input is - * available. - *

    - * - * @param buf the buffer into which the data is read. - * @param off the start offset of the data. - * @param len the maximum number of bytes read. - * @return the total number of bytes read into the buffer, or - * -1 if there is no more data because the end of - * the stream has been reached. - * @exception IOException if an I/O error occurs. - */ - public int read(byte[] buf, int off, int len) throws IOException { - int i, c; - for (i = 0; i < len; i++) { - if ((c = read()) == -1) { - if (i == 0) // At end of stream, so we should - i = -1; // return -1 , NOT 0. - break; - } - buf[off+i] = (byte)c; - } - return i; - } - - /** - * Skips over and discards n bytes of data from this stream. - */ - public long skip(long n) throws IOException { - long skipped = 0; - while (n-- > 0 && read() >= 0) - skipped++; - return skipped; - } - - /** - * Tests if this input stream supports marks. Currently this class - * does not support marks - */ - public boolean markSupported() { - return false; - } - - /** - * Returns the number of bytes that can be read from this input - * stream without blocking. The QP algorithm does not permit - * a priori knowledge of the number of bytes after decoding, so - * this method just invokes the available method - * of the original input stream. - */ - public int available() throws IOException { - // This is bogus ! We don't really know how much - // bytes are available *after* decoding - return in.available(); - } - - /**** begin TEST program - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - QPDecoderStream decoder = new QPDecoderStream(infile); - int c; - - while ((c = decoder.read()) != -1) - System.out.print((char)c); - System.out.println(); - } - *** end TEST program ****/ -} diff --git a/src/main/java/com/sun/mail/util/QPEncoderStream.java b/src/main/java/com/sun/mail/util/QPEncoderStream.java deleted file mode 100644 index 03776f46..00000000 --- a/src/main/java/com/sun/mail/util/QPEncoderStream.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a Quoted Printable Encoder. It is implemented as - * a FilterOutputStream, so one can just wrap this class around - * any output stream and write bytes into this filter. The Encoding - * is done as the bytes are written out. - * - * @author John Mani - */ - -public class QPEncoderStream extends FilterOutputStream { - private int count = 0; // number of bytes that have been output - private int bytesPerLine; // number of bytes per line - private boolean gotSpace = false; - private boolean gotCR = false; - - /** - * Create a QP encoder that encodes the specified input stream - * @param out the output stream - * @param bytesPerLine the number of bytes per line. The encoder - * inserts a CRLF sequence after this many number - * of bytes. - */ - public QPEncoderStream(OutputStream out, int bytesPerLine) { - super(out); - // Subtract 1 to account for the '=' in the soft-return - // at the end of a line - this.bytesPerLine = bytesPerLine - 1; - } - - /** - * Create a QP encoder that encodes the specified input stream. - * Inserts the CRLF sequence after outputting 76 bytes. - * @param out the output stream - */ - public QPEncoderStream(OutputStream out) { - this(out, 76); - } - - /** - * Encodes len bytes from the specified - * byte array starting at offset off to - * this output stream. - * - * @param b the data. - * @param off the start offset in the data. - * @param len the number of bytes to write. - * @exception IOException if an I/O error occurs. - */ - public void write(byte[] b, int off, int len) throws IOException { - for (int i = 0; i < len; i++) - write(b[off + i]); - } - - /** - * Encodes b.length bytes to this output stream. - * @param b the data to be written. - * @exception IOException if an I/O error occurs. - */ - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - /** - * Encodes the specified byte to this output stream. - * @param c the byte. - * @exception IOException if an I/O error occurs. - */ - public void write(int c) throws IOException { - c = c & 0xff; // Turn off the MSB. - if (gotSpace) { // previous character was - if (c == '\r' || c == '\n') - // if CR/LF, we need to encode the char - output(' ', true); - else // no encoding required, just output the char - output(' ', false); - gotSpace = false; - } - - if (c == '\r') { - gotCR = true; - outputCRLF(); - } else { - if (c == '\n') { - if (gotCR) - // This is a CRLF sequence, we already output the - // corresponding CRLF when we got the CR, so ignore this - ; - else - outputCRLF(); - } else if (c == ' ') { - gotSpace = true; - } else if (c < 040 || c >= 0177 || c == '=') - // Encoding required. - output(c, true); - else // No encoding required - output(c, false); - // whatever it was, it wasn't a CR - gotCR = false; - } - } - - /** - * Flushes this output stream and forces any buffered output bytes - * to be encoded out to the stream. - * @exception IOException if an I/O error occurs. - */ - public void flush() throws IOException { - out.flush(); - } - - /** - * Forces any buffered output bytes to be encoded out to the stream - * and closes this output stream - */ - public void close() throws IOException { - if (gotSpace) { - output(' ', true); - gotSpace = false; - } - out.close(); - } - - private void outputCRLF() throws IOException { - out.write('\r'); - out.write('\n'); - count = 0; - } - - // The encoding table - private final static char hex[] = { - '0','1', '2', '3', '4', '5', '6', '7', - '8','9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - - protected void output(int c, boolean encode) throws IOException { - if (encode) { - if ((count += 3) > bytesPerLine) { - out.write('='); - out.write('\r'); - out.write('\n'); - count = 3; // set the next line's length - } - out.write('='); - out.write(hex[c >> 4]); - out.write(hex[c & 0xf]); - } else { - if (++count > bytesPerLine) { - out.write('='); - out.write('\r'); - out.write('\n'); - count = 1; // set the next line's length - } - out.write(c); - } - } - - /**** begin TEST program *** - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - QPEncoderStream encoder = new QPEncoderStream(System.out); - int c; - - while ((c = infile.read()) != -1) - encoder.write(c); - encoder.close(); - } - *** end TEST program ***/ -} diff --git a/src/main/java/com/sun/mail/util/ReadableMime.java b/src/main/java/com/sun/mail/util/ReadableMime.java deleted file mode 100644 index aab3497f..00000000 --- a/src/main/java/com/sun/mail/util/ReadableMime.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.InputStream; - -import javax.mail.MessagingException; - -/** - * A Message or message Part whose data can be read as a MIME format - * stream. Note that the MIME stream will include both the headers - * and the body of the message or part. This should be the same data - * that is produced by the writeTo method, but in a readable form. - * - * @author Bill Shannon - * @since JavaMail 1.4.5 - */ -public interface ReadableMime { - /** - * Return the MIME format stream corresponding to this message part. - * - * @return the MIME format stream - */ - public InputStream getMimeStream() throws MessagingException; -} diff --git a/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java b/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java deleted file mode 100644 index 521b126e..00000000 --- a/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.InputStream; -import java.io.ByteArrayOutputStream; - -import javax.mail.util.SharedByteArrayInputStream; - -/** - * A ByteArrayOutputStream that allows us to share the byte array - * rather than copy it. Eventually could replace this with something - * that doesn't require a single contiguous byte array. - * - * @author Bill Shannon - * @since JavaMail 1.4.5 - */ -public class SharedByteArrayOutputStream extends ByteArrayOutputStream { - public SharedByteArrayOutputStream(int size) { - super(size); - } - - public InputStream toStream() { - return new SharedByteArrayInputStream(buf, 0, count); - } -} diff --git a/src/main/java/com/sun/mail/util/SocketConnectException.java b/src/main/java/com/sun/mail/util/SocketConnectException.java deleted file mode 100644 index 6546badd..00000000 --- a/src/main/java/com/sun/mail/util/SocketConnectException.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.IOException; - -/** - * An IOException that indicates a socket connection attempt failed. - * Unlike java.net.ConnectException, it includes details of what we - * were trying to connect to. - * - * @see java.net.ConnectException - * @author Bill Shannon - * @since JavaMail 1.5.0 - */ - -public class SocketConnectException extends IOException { - private String host; - private int port; - private int cto; - - private static final long serialVersionUID = 3997871560538755463L; - - /** - * Constructs a SocketConnectException. - * - * @param msg error message detail - * @param cause the underlying exception that indicates the failure - * @param host the host we were trying to connect to - * @param port the port we were trying to connect to - * @param cto the timeout for the connection attempt - */ - public SocketConnectException(String msg, Exception cause, - String host, int port, int cto) { - super(msg); - initCause(cause); - this.host = host; - this.port = port; - this.cto = cto; - } - - /** - * The exception that caused the failure. - * - * @return the exception - */ - public Exception getException() { - // the "cause" is always an Exception; see constructor above - assert getCause() instanceof Exception; - return (Exception)getCause(); - } - - /** - * The host we were trying to connect to. - * - * @return the host - */ - public String getHost() { - return host; - } - - /** - * The port we were trying to connect to. - * - * @return the port - */ - public int getPort() { - return port; - } - - /** - * The timeout used for the connection attempt. - * - * @return the connection timeout - */ - public int getConnectionTimeout() { - return cto; - } -} diff --git a/src/main/java/com/sun/mail/util/SocketFetcher.java b/src/main/java/com/sun/mail/util/SocketFetcher.java deleted file mode 100644 index 6cbfd00a..00000000 --- a/src/main/java/com/sun/mail/util/SocketFetcher.java +++ /dev/null @@ -1,722 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.security.*; -import java.net.*; -import java.io.*; -import java.lang.reflect.*; -import java.util.*; -import java.util.regex.*; -import java.util.logging.Level; -import java.security.cert.*; -import javax.net.*; -import javax.net.ssl.*; -import javax.security.auth.x500.X500Principal; - -/** - * This class is used to get Sockets. Depending on the arguments passed - * it will either return a plain java.net.Socket or dynamically load - * the SocketFactory class specified in the classname param and return - * a socket created by that SocketFactory. - * - * @author Max Spivak - * @author Bill Shannon - */ -public class SocketFetcher { - - private static MailLogger logger = new MailLogger( - SocketFetcher.class, - "socket", - "DEBUG SocketFetcher", - PropUtil.getBooleanSystemProperty("mail.socket.debug", false), - System.out); - - // No one should instantiate this class. - private SocketFetcher() { - } - - /** - * This method returns a Socket. Properties control the use of - * socket factories and other socket characteristics. The properties - * used are:

    - *

      - *
    • prefix.socketFactory - *
    • prefix.socketFactory.class - *
    • prefix.socketFactory.fallback - *
    • prefix.socketFactory.port - *
    • prefix.ssl.socketFactory - *
    • prefix.ssl.socketFactory.class - *
    • prefix.ssl.socketFactory.port - *
    • prefix.timeout - *
    • prefix.connectiontimeout - *
    • prefix.localaddress - *
    • prefix.localport - *

    - * If we're making an SSL connection, the ssl.socketFactory - * properties are used first, if set.

    - * - * If the socketFactory property is set, the value is an - * instance of a SocketFactory class, not a string. The - * instance is used directly. If the socketFactory property - * is not set, the socketFactory.class property is considered. - * (Note that the SocketFactory property must be set using the - * put method, not the setProperty - * method.)

    - * - * If the socketFactory.class property isn't set, the socket - * returned is an instance of java.net.Socket connected to the - * given host and port. If the socketFactory.class property is set, - * it is expected to contain a fully qualified classname of a - * javax.net.SocketFactory subclass. In this case, the class is - * dynamically instantiated and a socket created by that - * SocketFactory is returned.

    - * - * If the socketFactory.fallback property is set to false, don't - * fall back to using regular sockets if the socket factory fails.

    - * - * The socketFactory.port specifies a port to use when connecting - * through the socket factory. If unset, the port argument will be - * used.

    - * - * If the connectiontimeout property is set, the timeout is passed - * to the socket connect method.

    - * - * If the timeout property is set, it is used to set the socket timeout. - *

    - * - * If the localaddress property is set, it's used as the local address - * to bind to. If the localport property is also set, it's used as the - * local port number to bind to. - * - * @param host The host to connect to - * @param port The port to connect to at the host - * @param props Properties object containing socket properties - * @param prefix Property name prefix, e.g., "mail.imap" - * @param useSSL use the SSL socket factory as the default - */ - public static Socket getSocket(String host, int port, Properties props, - String prefix, boolean useSSL) - throws IOException { - - if (logger.isLoggable(Level.FINER)) - logger.finer("getSocket" + ", host " + host + ", port " + port + - ", prefix " + prefix + ", useSSL " + useSSL); - if (prefix == null) - prefix = "socket"; - if (props == null) - props = new Properties(); // empty - int cto = PropUtil.getIntProperty(props, - prefix + ".connectiontimeout", -1); - Socket socket = null; - String localaddrstr = props.getProperty(prefix + ".localaddress", null); - InetAddress localaddr = null; - if (localaddrstr != null) - localaddr = InetAddress.getByName(localaddrstr); - int localport = PropUtil.getIntProperty(props, - prefix + ".localport", 0); - - boolean fb = PropUtil.getBooleanProperty(props, - prefix + ".socketFactory.fallback", true); - - int sfPort = -1; - String sfErr = "unknown socket factory"; - int to = PropUtil.getIntProperty(props, prefix + ".timeout", -1); - try { - /* - * If using SSL, first look for SSL-specific class name or - * factory instance. - */ - SocketFactory sf = null; - String sfPortName = null; - if (useSSL) { - Object sfo = props.get(prefix + ".ssl.socketFactory"); - if (sfo instanceof SocketFactory) { - sf = (SocketFactory)sfo; - sfErr = "SSL socket factory instance " + sf; - } - if (sf == null) { - String sfClass = - props.getProperty(prefix + ".ssl.socketFactory.class"); - sf = getSocketFactory(sfClass); - sfErr = "SSL socket factory class " + sfClass; - } - sfPortName = ".ssl.socketFactory.port"; - } - - if (sf == null) { - Object sfo = props.get(prefix + ".socketFactory"); - if (sfo instanceof SocketFactory) { - sf = (SocketFactory)sfo; - sfErr = "socket factory instance " + sf; - } - if (sf == null) { - String sfClass = - props.getProperty(prefix + ".socketFactory.class"); - sf = getSocketFactory(sfClass); - sfErr = "socket factory class " + sfClass; - } - sfPortName = ".socketFactory.port"; - } - - // if we now have a socket factory, use it - if (sf != null) { - sfPort = PropUtil.getIntProperty(props, - prefix + sfPortName, -1); - - // if port passed in via property isn't valid, use param - if (sfPort == -1) - sfPort = port; - socket = createSocket(localaddr, localport, - host, sfPort, cto, to, props, prefix, sf, useSSL); - } - } catch (SocketTimeoutException sex) { - throw sex; - } catch (Exception ex) { - if (!fb) { - if (ex instanceof InvocationTargetException) { - Throwable t = - ((InvocationTargetException)ex).getTargetException(); - if (t instanceof Exception) - ex = (Exception)t; - } - if (ex instanceof IOException) - throw (IOException)ex; - throw new SocketConnectException("Using " + sfErr, ex, - host, sfPort, cto); - } - } - - if (socket == null) { - socket = createSocket(localaddr, localport, - host, port, cto, to, props, prefix, null, useSSL); - - } else { - if (to >= 0) - socket.setSoTimeout(to); - } - - return socket; - } - - public static Socket getSocket(String host, int port, Properties props, - String prefix) throws IOException { - return getSocket(host, port, props, prefix, false); - } - - /** - * Create a socket with the given local address and connected to - * the given host and port. Use the specified connection timeout - * and read timeout. - * If a socket factory is specified, use it. Otherwise, use the - * SSLSocketFactory if useSSL is true. - */ - private static Socket createSocket(InetAddress localaddr, int localport, - String host, int port, int cto, int to, - Properties props, String prefix, - SocketFactory sf, boolean useSSL) - throws IOException { - Socket socket = null; - - String socksHost = props.getProperty(prefix + ".socks.host", null); - int socksPort = 1080; - String err = null; - if (socksHost != null) { - int i = socksHost.indexOf(':'); - if (i >= 0) { - socksHost = socksHost.substring(0, i); - try { - socksPort = Integer.parseInt(socksHost.substring(i + 1)); - } catch (NumberFormatException ex) { - // ignore it - } - } - socksPort = PropUtil.getIntProperty(props, - prefix + ".socks.port", socksPort); - err = "Using SOCKS host, port: " + socksHost + ", " + socksPort; - if (logger.isLoggable(Level.FINER)) - logger.finer("socks host " + socksHost + ", port " + socksPort); - } - - if (sf != null) - socket = sf.createSocket(); - if (socket == null) { - if (socksHost != null) - socket = new Socket( - new java.net.Proxy(java.net.Proxy.Type.SOCKS, - new InetSocketAddress(socksHost, socksPort))); - else - socket = new Socket(); - } - if (to >= 0) - socket.setSoTimeout(to); - if (localaddr != null) - socket.bind(new InetSocketAddress(localaddr, localport)); - try { - if (cto >= 0) - socket.connect(new InetSocketAddress(host, port), cto); - else - socket.connect(new InetSocketAddress(host, port)); - } catch (IOException ex) { - throw new SocketConnectException(err, ex, host, port, cto); - } - - /* - * If we want an SSL connection and we didn't get an SSLSocket, - * wrap our plain Socket with an SSLSocket. - */ - if (useSSL && !(socket instanceof SSLSocket)) { - String trusted; - SSLSocketFactory ssf; - if ((trusted = props.getProperty(prefix + ".ssl.trust")) != null) { - try { - MailSSLSocketFactory msf = new MailSSLSocketFactory(); - if (trusted.equals("*")) - msf.setTrustAllHosts(true); - else - msf.setTrustedHosts(trusted.split("\\s+")); - ssf = msf; - } catch (GeneralSecurityException gex) { - IOException ioex = new IOException( - "Can't create MailSSLSocketFactory"); - ioex.initCause(gex); - throw ioex; - } - } else - ssf = (SSLSocketFactory)SSLSocketFactory.getDefault(); - socket = ssf.createSocket(socket, host, port, true); - sf = ssf; - } - - /* - * No matter how we created the socket, if it turns out to be an - * SSLSocket, configure it. - */ - configureSSLSocket(socket, host, props, prefix, sf); - - return socket; - } - - /** - * Return a socket factory of the specified class. - */ - private static SocketFactory getSocketFactory(String sfClass) - throws ClassNotFoundException, - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException { - if (sfClass == null || sfClass.length() == 0) - return null; - - // dynamically load the class - - ClassLoader cl = getContextClassLoader(); - Class clsSockFact = null; - if (cl != null) { - try { - clsSockFact = Class.forName(sfClass, false, cl); - } catch (ClassNotFoundException cex) { } - } - if (clsSockFact == null) - clsSockFact = Class.forName(sfClass); - // get & invoke the getDefault() method - Method mthGetDefault = clsSockFact.getMethod("getDefault", - new Class[]{}); - SocketFactory sf = (SocketFactory) - mthGetDefault.invoke(new Object(), new Object[]{}); - return sf; - } - - /** - * Start TLS on an existing socket. - * Supports the "STARTTLS" command in many protocols. - * This version for compatibility with possible third party code - * that might've used this API even though it shouldn't. - */ - public static Socket startTLS(Socket socket) throws IOException { - return startTLS(socket, new Properties(), "socket"); - } - - /** - * Start TLS on an existing socket. - * Supports the "STARTTLS" command in many protocols. - * This version for compatibility with possible third party code - * that might've used this API even though it shouldn't. - */ - public static Socket startTLS(Socket socket, Properties props, - String prefix) throws IOException { - InetAddress a = socket.getInetAddress(); - String host = a.getHostName(); - return startTLS(socket, host, props, prefix); - } - - /** - * Start TLS on an existing socket. - * Supports the "STARTTLS" command in many protocols. - */ - public static Socket startTLS(Socket socket, String host, Properties props, - String prefix) throws IOException { - int port = socket.getPort(); - if (logger.isLoggable(Level.FINER)) - logger.finer("startTLS host " + host + ", port " + port); - - String sfErr = "unknown socket factory"; - try { - SSLSocketFactory ssf = null; - SocketFactory sf = null; - - // first, look for an SSL socket factory - Object sfo = props.get(prefix + ".ssl.socketFactory"); - if (sfo instanceof SocketFactory) { - sf = (SocketFactory)sfo; - sfErr = "SSL socket factory instance " + sf; - } - if (sf == null) { - String sfClass = - props.getProperty(prefix + ".ssl.socketFactory.class"); - sf = getSocketFactory(sfClass); - sfErr = "SSL socket factory class " + sfClass; - } - if (sf != null && sf instanceof SSLSocketFactory) - ssf = (SSLSocketFactory)sf; - - // next, look for a regular socket factory that happens to be - // an SSL socket factory - if (ssf == null) { - sfo = props.get(prefix + ".socketFactory"); - if (sfo instanceof SocketFactory) { - sf = (SocketFactory)sfo; - sfErr = "socket factory instance " + sf; - } - if (sf == null) { - String sfClass = - props.getProperty(prefix + ".socketFactory.class"); - sf = getSocketFactory(sfClass); - sfErr = "socket factory class " + sfClass; - } - if (sf != null && sf instanceof SSLSocketFactory) - ssf = (SSLSocketFactory)sf; - } - - // finally, use the default SSL socket factory - if (ssf == null) { - String trusted; - if ((trusted = props.getProperty(prefix + ".ssl.trust")) != - null) { - try { - MailSSLSocketFactory msf = new MailSSLSocketFactory(); - if (trusted.equals("*")) - msf.setTrustAllHosts(true); - else - msf.setTrustedHosts(trusted.split("\\s+")); - ssf = msf; - sfErr = "mail SSL socket factory"; - } catch (GeneralSecurityException gex) { - IOException ioex = new IOException( - "Can't create MailSSLSocketFactory"); - ioex.initCause(gex); - throw ioex; - } - } else { - ssf = (SSLSocketFactory)SSLSocketFactory.getDefault(); - sfErr = "default SSL socket factory"; - } - } - - socket = ssf.createSocket(socket, host, port, true); - configureSSLSocket(socket, host, props, prefix, ssf); - } catch (Exception ex) { - if (ex instanceof InvocationTargetException) { - Throwable t = - ((InvocationTargetException)ex).getTargetException(); - if (t instanceof Exception) - ex = (Exception)t; - } - if (ex instanceof IOException) - throw (IOException)ex; - // wrap anything else before sending it on - IOException ioex = new IOException( - "Exception in startTLS using " + sfErr + - ": host, port: " + - host + ", " + port + - "; Exception: " + ex); - ioex.initCause(ex); - throw ioex; - } - return socket; - } - - /** - * Configure the SSL options for the socket (if it's an SSL socket), - * based on the mail..ssl.protocols and - * mail..ssl.ciphersuites properties. - * Check the identity of the server as specified by the - * mail..ssl.checkserveridentity property. - */ - private static void configureSSLSocket(Socket socket, String host, - Properties props, String prefix, SocketFactory sf) - throws IOException { - if (!(socket instanceof SSLSocket)) - return; - SSLSocket sslsocket = (SSLSocket)socket; - - String protocols = props.getProperty(prefix + ".ssl.protocols", null); - if (protocols != null) - sslsocket.setEnabledProtocols(stringArray(protocols)); - else { - /* - * At least the UW IMAP server insists on only the TLSv1 - * protocol for STARTTLS, and won't accept the old SSLv2 - * or SSLv3 protocols. Here we enable only the TLSv1 - * protocol. XXX - this should probably be parameterized. - */ - sslsocket.setEnabledProtocols(new String[] {"TLSv1"}); - } - String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null); - if (ciphers != null) - sslsocket.setEnabledCipherSuites(stringArray(ciphers)); - if (logger.isLoggable(Level.FINER)) { - logger.finer("SSL protocols after " + - Arrays.asList(sslsocket.getEnabledProtocols())); - logger.finer("SSL ciphers after " + - Arrays.asList(sslsocket.getEnabledCipherSuites())); - } - - /* - * Force the handshake to be done now so that we can report any - * errors (e.g., certificate errors) to the caller of the startTLS - * method. - */ - sslsocket.startHandshake(); - - /* - * Check server identity and trust. - */ - boolean idCheck = PropUtil.getBooleanProperty(props, - prefix + ".ssl.checkserveridentity", false); - if (idCheck) - checkServerIdentity(host, sslsocket); - if (sf instanceof MailSSLSocketFactory) { - MailSSLSocketFactory msf = (MailSSLSocketFactory)sf; - if (!msf.isServerTrusted(host, sslsocket)) { - try { - sslsocket.close(); - } finally { - throw new IOException("Server is not trusted: " + host); - } - } - } - } - - /** - * Check the server from the Socket connection against the server name(s) - * as expressed in the server certificate (RFC 2595 check). - * - * @param server name of the server expected - * @param sslSocket SSLSocket connected to the server - * @return true if the RFC 2595 check passes - */ - private static void checkServerIdentity(String server, SSLSocket sslSocket) - throws IOException { - - // Check against the server name(s) as expressed in server certificate - try { - java.security.cert.Certificate[] certChain = - sslSocket.getSession().getPeerCertificates(); - if (certChain != null && certChain.length > 0 && - certChain[0] instanceof X509Certificate && - matchCert(server, (X509Certificate)certChain[0])) - return; - } catch (SSLPeerUnverifiedException e) { - sslSocket.close(); - IOException ioex = new IOException( - "Can't verify identity of server: " + server); - ioex.initCause(e); - throw ioex; - } - - // If we get here, there is nothing to consider the server as trusted. - sslSocket.close(); - throw new IOException("Can't verify identity of server: " + server); - } - - /** - * Do any of the names in the cert match the server name? - * - * @param server name of the server expected - * @param cert X509Certificate to get the subject's name from - * @return true if it matches - */ - private static boolean matchCert(String server, X509Certificate cert) { - if (logger.isLoggable(Level.FINER)) - logger.finer("matchCert server " + - server + ", cert " + cert); - - /* - * First, try to use sun.security.util.HostnameChecker, - * which exists in Sun's JDK starting with 1.4.1. - * We use reflection to access it in case it's not available - * in the JDK we're running on. - */ - try { - Class hnc = Class.forName("sun.security.util.HostnameChecker"); - // invoke HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP) - // HostnameChecker.TYPE_LDAP == 2 - // LDAP requires the same regex handling as we need - Method getInstance = hnc.getMethod("getInstance", - new Class[] { byte.class }); - Object hostnameChecker = getInstance.invoke(new Object(), - new Object[] { new Byte((byte)2) }); - - // invoke hostnameChecker.match( server, cert) - if (logger.isLoggable(Level.FINER)) - logger.finer("using sun.security.util.HostnameChecker"); - Method match = hnc.getMethod("match", - new Class[] { String.class, X509Certificate.class }); - try { - match.invoke(hostnameChecker, new Object[] { server, cert }); - return true; - } catch (InvocationTargetException cex) { - logger.log(Level.FINER, "FAIL", cex); - return false; - } - } catch (Exception ex) { - logger.log(Level.FINER, "NO sun.security.util.HostnameChecker", ex); - // ignore failure and continue below - } - - /* - * Lacking HostnameChecker, we implement a crude version of - * the same checks ourselves. - */ - try { - /* - * Check each of the subjectAltNames. - * XXX - only checks DNS names, should also handle - * case where server name is a literal IP address - */ - Collection names = cert.getSubjectAlternativeNames(); - if (names != null) { - boolean foundName = false; - for (Iterator it = names.iterator(); it.hasNext(); ) { - List nameEnt = (List)it.next(); - Integer type = (Integer)nameEnt.get(0); - if (type.intValue() == 2) { // 2 == dNSName - foundName = true; - String name = (String)nameEnt.get(1); - if (logger.isLoggable(Level.FINER)) - logger.finer("found name: " + name); - if (matchServer(server, name)) - return true; - } - } - if (foundName) // found a name, but no match - return false; - } - } catch (CertificateParsingException ex) { - // ignore it - } - - // XXX - following is a *very* crude parse of the name and ignores - // all sorts of important issues such as quoting - Pattern p = Pattern.compile("CN=([^,]*)"); - Matcher m = p.matcher(cert.getSubjectX500Principal().getName()); - if (m.find() && matchServer(server, m.group(1).trim())) - return true; - - return false; - } - - /** - * Does the server we're expecting to connect to match the - * given name from the server's certificate? - * - * @param server name of the server expected - * @param name name from the server's certificate - */ - private static boolean matchServer(String server, String name) { - if (logger.isLoggable(Level.FINER)) - logger.finer("match server " + server + " with " + name); - if (name.startsWith("*.")) { - // match "foo.example.com" with "*.example.com" - String tail = name.substring(2); - if (tail.length() == 0) - return false; - int off = server.length() - tail.length(); - if (off < 1) - return false; - // if tail matches and is preceeded by "." - return server.charAt(off - 1) == '.' && - server.regionMatches(true, off, tail, 0, tail.length()); - } else - return server.equalsIgnoreCase(name); - } - - /** - * Parse a string into whitespace separated tokens - * and return the tokens in an array. - */ - private static String[] stringArray(String s) { - StringTokenizer st = new StringTokenizer(s); - List tokens = new ArrayList(); - while (st.hasMoreTokens()) - tokens.add(st.nextToken()); - return (String[])tokens.toArray(new String[tokens.size()]); - } - - /** - * Convenience method to get our context class loader. - * Assert any privileges we might have and then call the - * Thread.getContextClassLoader method. - */ - private static ClassLoader getContextClassLoader() { - return (ClassLoader) - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } catch (SecurityException ex) { } - return cl; - } - }); - } -} diff --git a/src/main/java/com/sun/mail/util/TraceInputStream.java b/src/main/java/com/sun/mail/util/TraceInputStream.java deleted file mode 100644 index 27c6754e..00000000 --- a/src/main/java/com/sun/mail/util/TraceInputStream.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; -import java.util.logging.Level; - -/** - * This class is a FilterInputStream that writes the bytes - * being read from the given input stream into the given output - * stream. This class is typically used to provide a trace of - * the data that is being retrieved from an input stream. - * - * @author John Mani - */ - -public class TraceInputStream extends FilterInputStream { - private boolean trace = false; - private boolean quote = false; - private OutputStream traceOut; - - /** - * Creates an input stream filter built on top of the specified - * input stream. - * - * @param in the underlying input stream. - * @param logger log trace here - */ - public TraceInputStream(InputStream in, MailLogger logger) { - super(in); - this.trace = logger.isLoggable(Level.FINEST); - this.traceOut = new LogOutputStream(logger); - } - - /** - * Creates an input stream filter built on top of the specified - * input stream. - * - * @param in the underlying input stream. - * @param traceOut the trace stream. - */ - public TraceInputStream(InputStream in, OutputStream traceOut) { - super(in); - this.traceOut = traceOut; - } - - /** - * Set trace mode. - * @param trace the trace mode - */ - public void setTrace(boolean trace) { - this.trace = trace; - } - - /** - * Set quote mode. - * @param quote the quote mode - */ - public void setQuote(boolean quote) { - this.quote = quote; - } - - /** - * Reads the next byte of data from this input stream. Returns - * -1 if no data is available. Writes out the read - * byte into the trace stream, if trace mode is true - */ - public int read() throws IOException { - int b = in.read(); - if (trace && b != -1) { - if (quote) - writeByte(b); - else - traceOut.write(b); - } - return b; - } - - /** - * Reads up to len bytes of data from this input stream - * into an array of bytes. Returns -1 if no more data - * is available. Writes out the read bytes into the trace stream, if - * trace mode is true - */ - public int read(byte b[], int off, int len) throws IOException { - int count = in.read(b, off, len); - if (trace && count != -1) { - if (quote) { - for (int i = 0; i < count; i++) - writeByte(b[off + i]); - } else - traceOut.write(b, off, count); - } - return count; - } - - /** - * Write a byte in a way that every byte value is printable ASCII. - */ - private final void writeByte(int b) throws IOException { - b &= 0xff; - if (b > 0x7f) { - traceOut.write('M'); - traceOut.write('-'); - b &= 0x7f; - } - if (b == '\r') { - traceOut.write('\\'); - traceOut.write('r'); - } else if (b == '\n') { - traceOut.write('\\'); - traceOut.write('n'); - traceOut.write('\n'); - } else if (b == '\t') { - traceOut.write('\\'); - traceOut.write('t'); - } else if (b < ' ') { - traceOut.write('^'); - traceOut.write('@' + b); - } else { - traceOut.write(b); - } - } -} diff --git a/src/main/java/com/sun/mail/util/TraceOutputStream.java b/src/main/java/com/sun/mail/util/TraceOutputStream.java deleted file mode 100644 index b325a93a..00000000 --- a/src/main/java/com/sun/mail/util/TraceOutputStream.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; -import java.util.logging.Level; - -/** - * This class is a subclass of DataOutputStream that copies the - * data being written into the DataOutputStream into another output - * stream. This class is used here to provide a debug trace of the - * stuff thats being written out into the DataOutputStream. - * - * @author John Mani - */ - -public class TraceOutputStream extends FilterOutputStream { - private boolean trace = false; - private boolean quote = false; - private OutputStream traceOut; - - /** - * Creates an output stream filter built on top of the specified - * underlying output stream. - * - * @param out the underlying output stream. - * @param logger log trace here - */ - public TraceOutputStream(OutputStream out, MailLogger logger) { - super(out); - this.trace = logger.isLoggable(Level.FINEST); - this.traceOut = new LogOutputStream(logger);; - } - - /** - * Creates an output stream filter built on top of the specified - * underlying output stream. - * - * @param out the underlying output stream. - * @param traceOut the trace stream. - */ - public TraceOutputStream(OutputStream out, OutputStream traceOut) { - super(out); - this.traceOut = traceOut; - } - - /** - * Set the trace mode. - */ - public void setTrace(boolean trace) { - this.trace = trace; - } - - /** - * Set quote mode. - * @param quote the quote mode - */ - public void setQuote(boolean quote) { - this.quote = quote; - } - - /** - * Writes the specified byte to this output stream. - * Writes out the byte into the trace stream if the trace mode - * is true - */ - public void write(int b) throws IOException { - if (trace) { - if (quote) - writeByte(b); - else - traceOut.write(b); - } - out.write(b); - } - - /** - * Writes b.length bytes to this output stream. - * Writes out the bytes into the trace stream if the trace - * mode is true - */ - public void write(byte b[], int off, int len) throws IOException { - if (trace) { - if (quote) { - for (int i = 0; i < len; i++) - writeByte(b[off + i]); - } else - traceOut.write(b, off, len); - } - out.write(b, off, len); - } - - /** - * Write a byte in a way that every byte value is printable ASCII. - */ - private final void writeByte(int b) throws IOException { - b &= 0xff; - if (b > 0x7f) { - traceOut.write('M'); - traceOut.write('-'); - b &= 0x7f; - } - if (b == '\r') { - traceOut.write('\\'); - traceOut.write('r'); - } else if (b == '\n') { - traceOut.write('\\'); - traceOut.write('n'); - traceOut.write('\n'); - } else if (b == '\t') { - traceOut.write('\\'); - traceOut.write('t'); - } else if (b < ' ') { - traceOut.write('^'); - traceOut.write('@' + b); - } else { - traceOut.write(b); - } - } -} diff --git a/src/main/java/com/sun/mail/util/UUDecoderStream.java b/src/main/java/com/sun/mail/util/UUDecoderStream.java deleted file mode 100644 index 0f25ab68..00000000 --- a/src/main/java/com/sun/mail/util/UUDecoderStream.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a UUDecoder. It is implemented as - * a FilterInputStream, so one can just wrap this class around - * any input stream and read bytes from this filter. The decoding - * is done as the bytes are read out. - * - * @author John Mani - * @author Bill Shannon - */ - -public class UUDecoderStream extends FilterInputStream { - private String name; - private int mode; - - private byte[] buffer = new byte[45]; // max decoded chars in a line = 45 - private int bufsize = 0; // size of the cache - private int index = 0; // index into the cache - private boolean gotPrefix = false; - private boolean gotEnd = false; - private LineInputStream lin; - private boolean ignoreErrors; - private boolean ignoreMissingBeginEnd; - private String readAhead; - - /** - * Create a UUdecoder that decodes the specified input stream. - * The System property mail.mime.uudecode.ignoreerrors - * controls whether errors in the encoded data cause an exception - * or are ignored. The default is false (errors cause exception). - * The System property mail.mime.uudecode.ignoremissingbeginend - * controls whether a missing begin or end line cause an exception - * or are ignored. The default is false (errors cause exception). - * @param in the input stream - */ - public UUDecoderStream(InputStream in) { - super(in); - lin = new LineInputStream(in); - // default to false - ignoreErrors = PropUtil.getBooleanSystemProperty( - "mail.mime.uudecode.ignoreerrors", false); - // default to false - ignoreMissingBeginEnd = PropUtil.getBooleanSystemProperty( - "mail.mime.uudecode.ignoremissingbeginend", false); - } - - /** - * Create a UUdecoder that decodes the specified input stream. - * @param in the input stream - * @param ignoreErrors ignore errors? - * @param ignoreMissingBeginEnd ignore missing begin or end? - */ - public UUDecoderStream(InputStream in, boolean ignoreErrors, - boolean ignoreMissingBeginEnd) { - super(in); - lin = new LineInputStream(in); - this.ignoreErrors = ignoreErrors; - this.ignoreMissingBeginEnd = ignoreMissingBeginEnd; - } - - /** - * Read the next decoded byte from this input stream. The byte - * is returned as an int in the range 0 - * to 255. If no byte is available because the end of - * the stream has been reached, the value -1 is returned. - * This method blocks until input data is available, the end of the - * stream is detected, or an exception is thrown. - * - * @return next byte of data, or -1 if the end of - * stream is reached. - * @exception IOException if an I/O error occurs. - * @see java.io.FilterInputStream#in - */ - public int read() throws IOException { - if (index >= bufsize) { - readPrefix(); - if (!decode()) - return -1; - index = 0; // reset index into buffer - } - return buffer[index++] & 0xff; // return lower byte - } - - public int read(byte[] buf, int off, int len) throws IOException { - int i, c; - for (i = 0; i < len; i++) { - if ((c = read()) == -1) { - if (i == 0) // At end of stream, so we should - i = -1; // return -1, NOT 0. - break; - } - buf[off+i] = (byte)c; - } - return i; - } - - public boolean markSupported() { - return false; - } - - public int available() throws IOException { - // This is only an estimate, since in.available() - // might include CRLFs too .. - return ((in.available() * 3)/4 + (bufsize-index)); - } - - /** - * Get the "name" field from the prefix. This is meant to - * be the pathname of the decoded file - * - * @return name of decoded file - * @exception IOException if an I/O error occurs. - */ - public String getName() throws IOException { - readPrefix(); - return name; - } - - /** - * Get the "mode" field from the prefix. This is the permission - * mode of the source file. - * - * @return permission mode of source file - * @exception IOException if an I/O error occurs. - */ - public int getMode() throws IOException { - readPrefix(); - return mode; - } - - /** - * UUencoded streams start off with the line: - * "begin " - * Search for this prefix and gobble it up. - */ - private void readPrefix() throws IOException { - if (gotPrefix) // got the prefix - return; - - mode = 0666; // defaults, overridden below - name = "encoder.buf"; // same default used by encoder - String line; - for (;;) { - // read till we get the prefix: "begin MODE FILENAME" - line = lin.readLine(); // NOTE: readLine consumes CRLF pairs too - if (line == null) { - if (!ignoreMissingBeginEnd) - throw new DecodingException("UUDecoder: Missing begin"); - // at EOF, fake it - gotPrefix = true; - gotEnd = true; - break; - } - if (line.regionMatches(false, 0, "begin", 0, 5)) { - try { - mode = Integer.parseInt(line.substring(6,9)); - } catch (NumberFormatException ex) { - if (!ignoreErrors) - throw new DecodingException( - "UUDecoder: Error in mode: " + ex.toString()); - } - if (line.length() > 10) { - name = line.substring(10); - } else { - if (!ignoreErrors) - throw new DecodingException( - "UUDecoder: Missing name: " + line); - } - gotPrefix = true; - break; - } else if (ignoreMissingBeginEnd && line.length() != 0) { - int count = line.charAt(0); - count = (count - ' ') & 0x3f; - int need = ((count * 8)+5)/6; - if (need == 0 || line.length() >= need + 1) { - /* - * Looks like a legitimate encoded line. - * Pretend we saw the "begin" line and - * save this line for later processing in - * decode(). - */ - readAhead = line; - gotPrefix = true; // fake it - break; - } - } - } - } - - private boolean decode() throws IOException { - - if (gotEnd) - return false; - bufsize = 0; - int count = 0; - String line; - for (;;) { - /* - * If we ignored a missing "begin", the first line - * will be saved in readAhead. - */ - if (readAhead != null) { - line = readAhead; - readAhead = null; - } else - line = lin.readLine(); - - /* - * Improperly encoded data sometimes omits the zero length - * line that starts with a space character, we detect the - * following "end" line here. - */ - if (line == null) { - if (!ignoreMissingBeginEnd) - throw new DecodingException( - "UUDecoder: Missing end at EOF"); - gotEnd = true; - return false; - } - if (line.equals("end")) { - gotEnd = true; - return false; - } - if (line.length() == 0) - continue; - count = line.charAt(0); - if (count < ' ') { - if (!ignoreErrors) - throw new DecodingException( - "UUDecoder: Buffer format error"); - continue; - } - - /* - * The first character in a line is the number of original (not - * the encoded atoms) characters in the line. Note that all the - * code below has to handle the character that indicates - * end of encoded stream. - */ - count = (count - ' ') & 0x3f; - - if (count == 0) { - line = lin.readLine(); - if (line == null || !line.equals("end")) { - if (!ignoreMissingBeginEnd) - throw new DecodingException( - "UUDecoder: Missing End after count 0 line"); - } - gotEnd = true; - return false; - } - - int need = ((count * 8)+5)/6; -//System.out.println("count " + count + ", need " + need + ", len " + line.length()); - if (line.length() < need + 1) { - if (!ignoreErrors) - throw new DecodingException( - "UUDecoder: Short buffer error"); - continue; - } - - // got a line we're committed to, break out and decode it - break; - } - - int i = 1; - byte a, b; - /* - * A correct uuencoder always encodes 3 characters at a time, even - * if there aren't 3 characters left. But since some people out - * there have broken uuencoders we handle the case where they - * don't include these "unnecessary" characters. - */ - while (bufsize < count) { - // continue decoding until we get 'count' decoded chars - a = (byte)((line.charAt(i++) - ' ') & 0x3f); - b = (byte)((line.charAt(i++) - ' ') & 0x3f); - buffer[bufsize++] = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)); - - if (bufsize < count) { - a = b; - b = (byte)((line.charAt(i++) - ' ') & 0x3f); - buffer[bufsize++] = - (byte)(((a << 4) & 0xf0) | ((b >>> 2) & 0xf)); - } - - if (bufsize < count) { - a = b; - b = (byte)((line.charAt(i++) - ' ') & 0x3f); - buffer[bufsize++] = (byte)(((a << 6) & 0xc0) | (b & 0x3f)); - } - } - return true; - } - - /*** begin TEST program ***** - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - UUDecoderStream decoder = new UUDecoderStream(infile); - int c; - - try { - while ((c = decoder.read()) != -1) - System.out.write(c); - System.out.flush(); - } catch (Exception e) { - e.printStackTrace(); - } - } - **** end TEST program ****/ -} diff --git a/src/main/java/com/sun/mail/util/UUEncoderStream.java b/src/main/java/com/sun/mail/util/UUEncoderStream.java deleted file mode 100644 index 79dc0ebb..00000000 --- a/src/main/java/com/sun/mail/util/UUEncoderStream.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util; - -import java.io.*; - -/** - * This class implements a UUEncoder. It is implemented as - * a FilterOutputStream, so one can just wrap this class around - * any output stream and write bytes into this filter. The Encoding - * is done as the bytes are written out. - * - * @author John Mani - */ - -public class UUEncoderStream extends FilterOutputStream { - private byte[] buffer; // cache of bytes that are yet to be encoded - private int bufsize = 0; // size of the cache - private boolean wrotePrefix = false; - - protected String name; // name of file - protected int mode; // permissions mode - - /** - * Create a UUencoder that encodes the specified input stream - * @param out the output stream - */ - public UUEncoderStream(OutputStream out) { - this(out, "encoder.buf", 644); - } - - /** - * Create a UUencoder that encodes the specified input stream - * @param out the output stream - * @param name Specifies a name for the encoded buffer - */ - public UUEncoderStream(OutputStream out, String name) { - this(out, name, 644); - } - - /** - * Create a UUencoder that encodes the specified input stream - * @param out the output stream - * @param name Specifies a name for the encoded buffer - * @param mode Specifies permission mode for the encoded buffer - */ - public UUEncoderStream(OutputStream out, String name, int mode) { - super(out); - this.name = name; - this.mode = mode; - buffer = new byte[45]; - } - - /** - * Set up the buffer name and permission mode. - * This method has any effect only if it is invoked before - * you start writing into the output stream - */ - public void setNameMode(String name, int mode) { - this.name = name; - this.mode = mode; - } - - public void write(byte[] b, int off, int len) throws IOException { - for (int i = 0; i < len; i++) - write(b[off + i]); - } - - public void write(byte[] data) throws IOException { - write(data, 0, data.length); - } - - public void write(int c) throws IOException { - /* buffer up characters till we get a line's worth, then encode - * and write them out. Max number of characters allowed per - * line is 45. - */ - buffer[bufsize++] = (byte)c; - if (bufsize == 45) { - writePrefix(); - encode(); - bufsize = 0; - } - } - - public void flush() throws IOException { - if (bufsize > 0) { // If there's unencoded characters in the buffer - writePrefix(); - encode(); // .. encode them - } - writeSuffix(); - out.flush(); - } - - public void close() throws IOException { - flush(); - out.close(); - } - - /** - * Write out the prefix: "begin " - */ - private void writePrefix() throws IOException { - if (!wrotePrefix) { - // name should be ASCII, but who knows... - PrintStream ps = new PrintStream(out, false, "utf-8"); - ps.println("begin " + mode + " " + name); - ps.flush(); - wrotePrefix = true; - } - } - - /** - * Write a single line containing space and the suffix line - * containing the single word "end" (terminated by a newline) - */ - private void writeSuffix() throws IOException { - PrintStream ps = new PrintStream(out, false, "us-ascii"); - ps.println(" \nend"); - ps.flush(); - } - - /** - * Encode a line. - * Start off with the character count, followed by the encoded atoms - * and terminate with LF. (or is it CRLF or the local line-terminator ?) - * Take three bytes and encodes them into 4 characters - * If bufsize if not a multiple of 3, the remaining bytes are filled - * with '1'. This insures that the last line won't end in spaces - * and potentiallly be truncated. - */ - private void encode() throws IOException { - byte a, b, c; - int c1, c2, c3, c4; - int i = 0; - - // Start off with the count of characters in the line - out.write((bufsize & 0x3f) + ' '); - - while (i < bufsize) { - a = buffer[i++]; - if (i < bufsize) { - b = buffer[i++]; - if (i < bufsize) - c = buffer[i++]; - else // default c to 1 - c = 1; - } - else { // default b & c to 1 - b = 1; - c = 1; - } - - c1 = (a >>> 2) & 0x3f; - c2 = ((a << 4) & 0x30) | ((b >>> 4) & 0xf); - c3 = ((b << 2) & 0x3c) | ((c >>> 6) & 0x3); - c4 = c & 0x3f; - out.write(c1 + ' '); - out.write(c2 + ' '); - out.write(c3 + ' '); - out.write(c4 + ' '); - } - // Terminate with LF. (should it be CRLF or local line-terminator ?) - out.write('\n'); - } - - /**** begin TEST program ***** - public static void main(String argv[]) throws Exception { - FileInputStream infile = new FileInputStream(argv[0]); - UUEncoderStream encoder = new UUEncoderStream(System.out); - int c; - - while ((c = infile.read()) != -1) - encoder.write(c); - encoder.close(); - } - **** end TEST program *****/ -} diff --git a/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java b/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java deleted file mode 100644 index 0aadf4ea..00000000 --- a/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java +++ /dev/null @@ -1,648 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009-2013 Jason Mehrens. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ -package com.sun.mail.util.logging; - -import java.io.ObjectStreamException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; -import java.util.logging.Formatter; -import java.util.logging.*; -import javax.mail.Authenticator; - -/** - * An adapter class to allow the Mail API to access the LogManager properties. - * The LogManager properties are treated as the root of all properties. - * First, the parent properties are searched. If no value is found, then, - * the LogManager is searched with prefix value. If not found, then, just the - * key itself is searched in the LogManager. If a value is found in the - * LogManager it is then copied to this properties object with no key prefix. - * If no value is found in the LogManager or the parent properties, then this - * properties object is searched only by passing the key value. - * - *

    - * This class also emulates the LogManager functions for creating new objects - * from string class names. This is to support initial setup of objects such as - * log filters, formatters, error managers, etc. - * - *

    - * This class should never be exposed outside of this package. Keep this - * class package private (default access). - * - * @author Jason Mehrens - * @since JavaMail 1.4.3 - */ -final class LogManagerProperties extends Properties { - - private static final long serialVersionUID = -2239983349056806252L; - /** - * Caches the LogManager so we only read the config once. - */ - private final static LogManager LOG_MANAGER = LogManager.getLogManager(); - - /** - * Gets the LogManger for the running JVM. - * @return the LogManager. - * @since JavaMail 1.4.5 - */ - static LogManager getLogManager() { - return LOG_MANAGER; - } - - /** - * Converts a locale to a language tag. - * @param locale the locale to convert. - * @return the language tag. - * @throws NullPointerException if the given locale is null. - * @since JavaMail 1.4.5 - */ - static String toLanguageTag(final Locale locale) { - final String l = locale.getLanguage(); - final String c = locale.getCountry(); - final String v = locale.getVariant(); - final char[] b = new char[l.length() + c.length() + v.length() + 2]; - int count = l.length(); - l.getChars(0, count, b, 0); - if (c.length() != 0 || (l.length() != 0 && v.length() != 0)) { - b[count] = '-'; - ++count; //be nice to the client compiler. - c.getChars(0, c.length(), b, count); - count += c.length(); - } - - if (v.length() != 0 && (l.length() != 0 || c.length() != 0)) { - b[count] = '-'; - ++count; //be nice to the client compiler. - v.getChars(0, v.length(), b, count); - count += v.length(); - } - return String.valueOf(b, 0, count); - } - - /** - * Creates a new filter from the given class name. - * @param name the fully qualified class name. - * @return a new filter. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - */ - static Filter newFilter(String name) throws Exception { - return newObjectFrom(name, Filter.class); - } - - /** - * Creates a new formatter from the given class name. - * @param name the fully qualified class name. - * @return a new formatter. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - */ - static Formatter newFormatter(String name) throws Exception { - return newObjectFrom(name, Formatter.class); - } - - /** - * Creates a new log record comparator from the given class name. - * @param name the fully qualified class name. - * @return a new comparator. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - * @see java.util.logging.LogRecord - */ - @SuppressWarnings("unchecked") - static Comparator newComparator(String name) throws Exception { - return newObjectFrom(name, Comparator.class); - } - - /** - * Returns a comparator that imposes the reverse ordering of the specified - * {@link Comparator}. If the given comparator declares a public - * reverseOrder method that method is called first and the return value is - * used. If that method is not declared or the caller does not have access - * then a comparator wrapping the given comparator is returned. - * - * @param the element type to be compared - * @param c a comparator whose ordering is to be reversed by the returned - * comparator - * @return A comparator that imposes the reverse ordering of the specified - * comparator. - * @throws NullPointerException if the given comparator is null. - * @since JavaMail 1.5.0 - */ - @SuppressWarnings("unchecked") - static Comparator reverseOrder(final Comparator c) { - Comparator reverse = null; - //Comparator in Java 1.8 has 'reverseOrder' as a default method. - //This code calls that method first to allow custom code to define what - //reverse order means. - try { - //assert Modifier.isPublic(c.getClass().getModifiers()) : - // Modifier.toString(c.getClass().getModifiers()); - final Method m = c.getClass().getMethod("reverseOrder"); - if (Comparator.class.isAssignableFrom(m.getReturnType())) { - try { - reverse = (Comparator) m.invoke(c); - } catch (final ExceptionInInitializerError eiie) { - throw wrapOrThrow(eiie); - } - } - } catch (final NoSuchMethodException ignore) { - } catch (final IllegalAccessException ignore) { - } catch (final InvocationTargetException ite) { - paramOrError(ite); //Ignore invocation bugs. - } - - if (reverse == null) { - reverse = Collections.reverseOrder(c); - } - return reverse; - } - - /** - * Creates a new error manager from the given class name. - * @param name the fully qualified class name. - * @return a new error manager. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - */ - static ErrorManager newErrorManager(String name) throws Exception { - return newObjectFrom(name, ErrorManager.class); - } - - /** - * Creates a new authenticator from the given class name. - * @param name the fully qualified class name. - * @return a new authenticator. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - */ - static Authenticator newAuthenticator(String name) throws Exception { - return newObjectFrom(name, Authenticator.class); - } - - /** - * Creates a new object from the given class name. - * @param name the fully qualified class name. - * @param type the assignable type for the given name. - * @return a new object assignable to the given type. - * @throws ClassCastException if class name does not match the type. - * @throws ClassNotFoundException if the class name was not found. - * @throws IllegalAccessException if the constructor is inaccessible. - * @throws InstantiationException if the given class name is abstract. - * @throws InvocationTargetException if the constructor throws an exception. - * @throws LinkageError if the linkage fails. - * @throws ExceptionInInitializerError if the static initializer fails. - * @throws Exception to match the error method of the ErrorManager. - * @throws NoSuchMethodException if the class name does not have a no - * argument constructor. - * @since JavaMail 1.4.5 - */ - private static T newObjectFrom(String name, Class type) throws Exception { - try { - final Class clazz = LogManagerProperties.findClass(name); - //This check avoids additional side effects when the name parameter - //is a literal name and not a class name. - if (type.isAssignableFrom(clazz)) { - try { - return type.cast(clazz.getConstructor().newInstance()); - } catch (final InvocationTargetException ITE) { - throw paramOrError(ITE); - } - } else { - throw new ClassCastException(clazz.getName() - + " cannot be cast to " + type.getName()); - } - } catch (final NoClassDefFoundError NCDFE) { - //No class def found can occur on filesystems that are - //case insensitive (BUG ID 6196068). In some cases, we allow class - //names or literal names, this code guards against the case where a - //literal name happens to match a class name in a different case. - //This is also a nice way to adap this error for the error manager. - throw new ClassNotFoundException(NCDFE.toString(), NCDFE); - } catch (final ExceptionInInitializerError EIIE) { - throw wrapOrThrow(EIIE); - } - } - - /** - * Returns the given exception or throws the escaping cause. - * @param ite any invocation target. - * @return the exception. - * @throws VirtualMachineError if present as cause. - * @throws ThreadDeath if present as cause. - * @since JavaMail 1.4.5 - */ - private static Exception paramOrError(InvocationTargetException ite) { - final Throwable cause = ite.getCause(); - if (cause != null) { - if (cause instanceof VirtualMachineError - || cause instanceof ThreadDeath) { - throw (Error) cause; - } - } - return ite; - } - - /** - * Throws the given error if the cause is an error otherwise - * the given error is wrapped. - * @param eiie the error. - * @return an InvocationTargetException. - * @since JavaMail 1.5.0 - */ - private static InvocationTargetException wrapOrThrow( - ExceptionInInitializerError eiie) { - //This linkage error will escape the constructor new instance call. - //If the cause is an error, rethrow to skip any error manager. - if (eiie.getCause() instanceof Error) { - throw eiie; - } else { - //Considered a bug in the code, wrap the error so it can be - //reported to the error manager. - return new InvocationTargetException(eiie); - } - } - - /** - * This code is modified from the LogManager, which explictly states - * searching the system class loader first, then the context class loader. - * There is resistance (compatibility) to change this behavior to simply - * searching the context class loader. - * @param name full class name - * @return the class. - * @throws LinkageError if the linkage fails. - * @throws ClassNotFoundException if the class name was not found. - * @throws ExceptionInInitializerError if static initializer fails. - */ - private static Class findClass(String name) throws ClassNotFoundException { - ClassLoader[] loaders = getClassLoaders(); - assert loaders.length == 2 : loaders.length; - Class clazz; - if (loaders[0] != null) { - try { - clazz = Class.forName(name, false, loaders[0]); - } catch (ClassNotFoundException tryContext) { - clazz = tryLoad(name, loaders[1]); - } - } else { - clazz = tryLoad(name, loaders[1]); - } - return clazz; - } - - private static Class tryLoad(String name, ClassLoader l) throws ClassNotFoundException { - if (l != null) { - return Class.forName(name, false, l); - } else { - return Class.forName(name); - } - } - - private static ClassLoader[] getClassLoaders() { - return AccessController.doPrivileged(new PrivilegedAction() { - - public ClassLoader[] run() { - final ClassLoader[] loaders = new ClassLoader[2]; - try { - loaders[0] = ClassLoader.getSystemClassLoader(); - } catch (SecurityException ignore) { - loaders[0] = null; - } - - try { - loaders[1] = Thread.currentThread().getContextClassLoader(); - } catch (SecurityException ignore) { - loaders[1] = null; - } - return loaders; - } - }); - } - /** - * The namespace prefix to search LogManager and defaults. - */ - private final String prefix; - - /** - * Creates a log manager properties object. - * @param parent the parent properties. - * @param prefix the namespace prefix. - * @throws NullPointerException if prefix or parent is - * null. - */ - LogManagerProperties(final Properties parent, final String prefix) { - super(parent); - parent.isEmpty(); //null check, happens-before - if (prefix == null) { - throw new NullPointerException(); - } - this.prefix = prefix; - - //'defaults' is not decalared final. - super.isEmpty(); //happens-before. - } - - /** - * Returns a properties object that contains a snapshot of the current - * state. This method violates the clone contract so that no instances - * of LogManagerProperties is exported for public use. - * @return the snapshot. - * @since JavaMail 1.4.4 - */ - @Override - public synchronized Object clone() { - return exportCopy(defaults); - } - - /** - * Searches defaults, then searches the log manager - * by the prefix property, and then by the key itself. - * @param key a non null key. - * @return the value for that key. - */ - @Override - public synchronized String getProperty(final String key) { - String value = defaults.getProperty(key); - if (value == null) { - final LogManager manager = getLogManager(); - if (key.length() > 0) { - value = manager.getProperty(prefix + '.' + key); - } - - if (value == null) { - value = manager.getProperty(key); - } - - /** - * Copy the log manager properties as we read them. If a value is - * no longer present in the LogManager read it from here. - * The reason this works is because LogManager.reset() closes - * all attached handlers therefore, stale values only exist in - * closed handlers. - */ - if (value != null) { - super.put(key, value); - } else { - Object v = super.get(key); //defaults are not used. - value = v instanceof String ? (String) v : null; - } - } - return value; - } - - /** - * Calls getProperty directly. If getProperty returns null the default - * value is returned. - * @param key a key to search for. - * @param def the default value to use if not found. - * @return the value for the key. - * @since JavaMail 1.4.4 - */ - @Override - public String getProperty(final String key, final String def) { - final String value = this.getProperty(key); - return value == null ? def : value; - } - - /** - * Required to work with PropUtil. Calls getProperty directly if the - * given key is a string. Otherwise, performs a normal get operation. - * @param key any key. - * @return the value for the key or null. - * @since JavaMail 1.4.5 - */ - @Override - public Object get(final Object key) { - if (key instanceof String) { - return this.getProperty((String) key); - } else { - return super.get(key); - } - } - - /** - * Required to work with PropUtil. An updated copy of the key is fetched - * from the log manager if the key doesn't exist in this properties. - * @param key any key. - * @return the value for the key or the default value for the key. - * @since JavaMail 1.4.5 - */ - @Override - public synchronized Object put(final Object key, final Object value) { - final Object def = preWrite(key); - final Object man = super.put(key, value); - return man == null ? def : man; - } - - /** - * Calls the put method directly. - * @param key any key. - * @return the value for the key or the default value for the key. - * @since JavaMail 1.4.5 - */ - @Override - public Object setProperty(String key, String value) { - return this.put(key, value); - } - - /** - * Required to work with PropUtil. An updated copy of the key is fetched - * from the log manager prior to returning. - * @param key any key. - * @return the value for the key or null. - * @since JavaMail 1.4.5 - */ - @Override - public boolean containsKey(final Object key) { - if (key instanceof String) { - return this.getProperty((String) key) != null; - } else { - return super.containsKey(key); - } - } - - /** - * Required to work with PropUtil. An updated copy of the key is fetched - * from the log manager if the key doesn't exist in this properties. - * @param key any key. - * @return the value for the key or the default value for the key. - * @since JavaMail 1.4.5 - */ - @Override - public synchronized Object remove(final Object key) { - final Object def = preWrite(key); - final Object man = super.remove(key); - return man == null ? def : man; - } - - /** - * It is assumed that this method will never be called. - * No way to get the property names from LogManager. - * @return the property names - */ - @Override - public Enumeration propertyNames() { - assert false; - return super.propertyNames(); - } - - /** - * It is assumed that this method will never be called. - * The prefix value is not used for the equals method. - * @param o any object or null. - * @return true if equal, otherwise false. - */ - @Override - public boolean equals(final Object o) { - if (o == null) { - return false; - } - if (o == this) { - return true; - } - if (o instanceof Properties == false) { - return false; - } - assert false : prefix; - return super.equals(o); - } - - /** - * It is assumed that this method will never be called. See equals. - * @return the hash code. - */ - @Override - public int hashCode() { - assert false : prefix.hashCode(); - return super.hashCode(); - } - - /** - * Called before a write operation of a key. - * Caches a key read from the log manager in this properties object. - * The key is only cached if it is an instance of a String and - * this properties doesn't contain a copy of the key. - * @param key the key to search. - * @return the default value for the key. - */ - private Object preWrite(final Object key) { - assert Thread.holdsLock(this); - Object value; - if (key instanceof String && !super.containsKey(key)) { - value = this.getProperty((String) key); //fetch and cache. - } else { - value = null; - } - return value; - } - - /** - * Creates a public snapshot of this properties object using - * the given parent properties. - * @param parent the defaults to use with the snapshot. - * @return the safe snapshot. - */ - private Properties exportCopy(final Properties parent) { - Thread.holdsLock(this); - final Properties child = new Properties(parent); - child.putAll(this); - return child; - } - - /** - * It is assumed that this method will never be called. - * We return a safe copy for export to avoid locking this properties - * object or the defaults during write. - * @return the parent properties. - * @throws ObjectStreamException if there is a problem. - */ - private synchronized Object writeReplace() throws ObjectStreamException { - assert false; - return exportCopy((Properties) defaults.clone()); - } -} diff --git a/src/main/java/com/sun/mail/util/logging/MailHandler.java b/src/main/java/com/sun/mail/util/logging/MailHandler.java deleted file mode 100644 index 88127d54..00000000 --- a/src/main/java/com/sun/mail/util/logging/MailHandler.java +++ /dev/null @@ -1,3477 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009-2013 Jason Mehrens. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package com.sun.mail.util.logging; - -import com.sun.mail.smtp.SMTPTransport; -import java.io.*; -import java.lang.reflect.Array; -import java.net.InetAddress; -import java.net.URLConnection; -import java.net.UnknownHostException; -import java.nio.charset.Charset; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; -import java.util.logging.*; -import java.util.logging.Formatter; -import javax.activation.DataHandler; -import javax.activation.DataSource; -import javax.activation.FileTypeMap; -import javax.activation.MimetypesFileTypeMap; -import javax.mail.*; -import javax.mail.internet.*; -import javax.mail.util.ByteArrayDataSource; - -/** - * Handler that formats log records as an email message. - * - *

    - * This Handler will store a fixed number of log records used to - * generate a single email message. When the internal buffer reaches capacity, - * all log records are formatted and placed in an email which is sent to an - * email server. The code to manually setup this handler can be as simple as - * the following: - * - *

    - *      Properties props = new Properties();
    - *      props.put("mail.smtp.host", "my-mail-server");
    - *      props.put("mail.to", "me@example.com");
    - *      props.put("verify", "local");
    - *      MailHandler h = new MailHandler(props);
    - *      h.setLevel(Level.WARNING);
    - * 
    - * - *

    - * Configuration: - * The LogManager should define at least one or more recipient addresses and a - * mail host for outgoing email. The code to setup this handler via the - * logging properties can be as simple as the following: - * - *

    - *      #Default MailHandler settings.
    - *      com.sun.mail.util.logging.MailHandler.mail.smtp.host = my-mail-server
    - *      com.sun.mail.util.logging.MailHandler.mail.to = me@example.com
    - *      com.sun.mail.util.logging.MailHandler.level = WARNING
    - *      com.sun.mail.util.logging.MailHandler.verify = local
    - * 
    - * - * For a custom handler, e.g. com.foo.MyHandler, the properties would be: - *
    - *      #Subclass com.foo.MyHandler settings.
    - *      com.foo.MyHandler.mail.smtp.host = my-mail-server
    - *      com.foo.MyHandler.mail.to = me@example.com
    - *      com.foo.MyHandler.level = WARNING
    - *      com.foo.MyHandler.verify = local
    - * 
    - * - * All mail properties documented in the Java Mail API cascade to the - * LogManager by prefixing a key using the fully qualified class name of this - * MailHandler or the fully qualified derived class name dot mail - * property. If the prefixed property is not found, then the mail property - * itself is searched in the LogManager. By default each MailHandler is - * initialized using the following LogManager configuration properties where - * refers to the fully-qualified class name of the - * handler. If properties are not defined, or contain invalid values, then the - * specified default values are used. - * - *
      - *
    • <handler-name>.attachment.filters a comma - * separated list of Filter class names used to create each attachment. - * The literal null is reserved for attachments that do not require - * filtering. (defaults to the - * {@linkplain java.util.logging.Handler#getFilter() body} filter) - * - *
    • <handler-name>.attachment.formatters a comma - * separated list of Formatter class names used to create each - * attachment. (default is no attachments) - * - *
    • <handler-name>.attachment.names a comma separated - * list of names or Formatter class names of each attachment. The - * attachment file names must not contain any line breaks. - * (default is {@linkplain java.util.logging.Formatter#toString() toString} - * of the attachment formatter) - * - *
    • <handler-name>.authenticator name of an - * {@linkplain javax.mail.Authenticator} class used to provide login credentials - * to the email server or string literal that is the password used with the - * {@linkplain Authenticator#getDefaultUserName() default} user name. - * (default is null) - * - *
    • <handler-name>.capacity the max number of - * LogRecord objects include in each email message. - * (defaults to 1000) - * - *
    • <handler-name>.comparator name of a - * {@linkplain java.util.Comparator} class used to sort the published - * LogRecord objects prior to all formatting. - * (defaults to null meaning records are unsorted). - * - *
    • <handler-name>.comparator.reverse a boolean - * true to reverse the order of the specified comparator or - * false to retain the original order. (defaults to false) - * - *
    • <handler-name>.encoding the name of the Java - * {@linkplain java.nio.charset.Charset#name() character set} to use for the - * email message. (defaults to null, the - * {@linkplain javax.mail.internet.MimeUtility#getDefaultJavaCharset() default} - * platform encoding). - * - *
    • <handler-name>.errorManager name of an - * ErrorManager class used to handle any configuration or mail - * transport problems. (defaults to java.util.logging.ErrorManager) - * - *
    • <handler-name>.filter name of a Filter - * class used for the body of the message. (defaults to null, - * allow all records) - * - *
    • <handler-name>.formatter name of a - * Formatter class used to format the body of this message. - * (defaults to java.util.logging.SimpleFormatter) - * - *
    • <handler-name>.level specifies the default level - * for this Handler (defaults to Level.WARNING). - * - *
    • <handler-name>.mail.bcc a comma separated list of - * addresses which will be blind carbon copied. Typically, this is set to the - * recipients that may need to be privately notified of a log message or - * notified that a log message was sent to a third party such as a support team. - * The empty string can be used to specify no blind carbon copied address. - * (defaults to null, none) - * - *
    • <handler-name>.mail.cc a comma separated list of - * addresses which will be carbon copied. Typically, this is set to the - * recipients that may need to be notified of a log message but, are not - * required to provide direct support. The empty string can be used to specify - * no carbon copied address. (defaults to null, none) - * - *
    • <handler-name>.mail.from a comma separated list of - * addresses which will be from addresses. Typically, this is set to the email - * address identifying the user running the application. The empty string can - * be used to override the default behavior and specify no from address. - * (defaults to the {@linkplain javax.mail.Message#setFrom() local address}) - * - *
    • <handler-name>.mail.host the host name or IP - * address of the email server. (defaults to null, use - * {@linkplain Transport#protocolConnect default} - * Java Mail behavior) - * - *
    • <handler-name>.mail.reply.to a comma separated - * list of addresses which will be reply-to addresses. Typically, this is set - * to the recipients that provide support for the application itself. The empty - * string can be used to specify no reply-to address. - * (defaults to null, none) - * - *
    • <handler-name>.mail.to a comma separated list of - * addresses which will be send-to addresses. Typically, this is set to the - * recipients that provide support for the application, system, and/or - * supporting infrastructure. The empty string can be used to specify no - * send-to address which overrides the default behavior. (defaults to - * {@linkplain javax.mail.internet.InternetAddress.getLocalAddress - * local address}.) - * - *
    • <handler-name>.mail.sender a single address - * identifying sender of the email; never equal to the from address. Typically, - * this is set to the email address identifying the application itself. The - * empty string can be used to specify no sender address. - * (defaults to null, none) - * - *
    • <handler-name>.subject the name of a - * Formatter class or string literal used to create the subject line. - * The empty string can be used to specify no subject. The subject line must - * not contain any line breaks. (defaults to the empty string) - * - *
    • <handler-name>.pushFilter the name of a - * Filter class used to trigger an early push. - * (defaults to null, no early push) - * - *
    • <handler-name>.pushLevel the level which will - * trigger an early push. (defaults to Level.OFF, only push when full) - * - *
    • <handler-name>.verify used to - * verify the Handler configuration prior to a push. - *
        - *
      • If the value is not set, equal to an empty string, or equal to the - * literal null then no settings are verified prior to a push. - *
      • If set to a value of limited then the Handler will - * verify minimal local machine settings. - *
      • If set to a value of local the Handler will verify - * all of settings of the local machine. - *
      • If set to a value of resolve, the Handler will - * verify all local settings and try to resolve the remote host name with - * the domain name server. - *
      • If set to a value of remote, the Handler will - * verify all local settings and try to establish a connection with the - * email server. - *
      - * If this Handler is only implicitly closed by the - * LogManager, then verification should be turned on. - * (defaults to null, no verify). - * - *

      - * Normalization: - * The error manager, filters, and formatters when loaded from the LogManager - * are converted into canonical form inside the MailHandler. The pool of - * interned values is limited to each MailHandler object such that no two - * MailHandler objects created by the LogManager will be created sharing - * identical error managers, filters, or formatters. If a filter or formatter - * should not be interned then it is recommended to retain the identity - * equals and identity hashCode methods as the implementation. For a filter or - * formatter to be interned the class must implement the - * {@linkplain java.lang.Object#equals(java.lang.Object) equals} - * and {@linkplain java.lang.Object#hashCode() hashCode} methods. - * The recommended code to use for stateless filters and formatters is: - *

      - * public boolean equals(Object obj) {
      - *     return obj == null ? false : obj.getClass() == getClass();
      - * }
      - *
      - * public int hashCode() {
      - *     return 31 * getClass().hashCode();
      - * }
      - * 
      - * - *

      - * Sorting: - * All LogRecord objects are ordered prior to formatting if this - * Handler has a non null comparator. Developers might be interested - * in sorting the formatted email by thread id, time, and sequence properties - * of a LogRecord. Where as system administrators might be interested - * in sorting the formatted email by thrown, level, time, and sequence - * properties of a LogRecord. If comparator for this handler is - * null then the order is unspecified. - * - *

      - * Formatting: - * The main message body is formatted using the Formatter returned by - * getFormatter(). Only records that pass the filter returned by - * getFilter() will be included in the message body. The subject - * Formatter will see all LogRecord objects that were - * published regardless of the current Filter. The MIME type of the - * message body can be {@linkplain FileTypeMap#setDefaultFileTypeMap overridden} - * by adding a MIME {@linkplain MimetypesFileTypeMap entry} using the simple - * class name of the body formatter as the file extension. The MIME type of the - * attachments can be overridden by changing the attachment file name extension - * or by editing the default MIME entry for a specific file name extension. - * - *

      - * Attachments: - * This Handler allows multiple attachments per each email. - * The attachment order maps directly to the array index order in this - * Handler with zero index being the first attachment. The number of - * attachment formatters controls the number of attachments per email and - * the content type of each attachment. The attachment filters determine if a - * LogRecord will be included in an attachment. If an attachment - * filter is null then all records are included for that attachment. - * Attachments without content will be omitted from email message. The - * attachment name formatters create the file name for an attachment. - * Custom attachment name formatters can be used to generate an attachment name - * based on the contents of the attachment. - * - *

      - * Push Level and Push Filter: - * The push method, push level, and optional push filter can be used to - * conditionally trigger a push at or prior to full capacity. When a push - * occurs, the current buffer is formatted into an email and is sent to the - * email server. If the push method, push level, or push filter trigger a push - * then the outgoing email is flagged as high importance with urgent priority. - * - *

      - * Buffering: - * Log records that are published are stored in an internal buffer. When this - * buffer reaches capacity the existing records are formatted and sent in an - * email. Any published records can be sent before reaching capacity by - * explictly calling the flush, push, or close - * methods. If a circular buffer is required then this handler can be wrapped - * with a {@linkplain java.util.logging.MemoryHandler} typically with an - * equivalent capacity, level, and push level. - * - *

      - * Error Handling: - * If the transport of an email message fails, the email is converted to - * a {@linkplain javax.mail.internet.MimeMessage#writeTo raw} - * {@linkplain java.io.ByteArrayOutputStream#toString(java.lang.String) string} - * and is then passed as the msg parameter to - * {@linkplain Handler#reportError reportError} along with the exception - * describing the cause of the failure. This allows custom error managers to - * store, {@linkplain javax.mail.internet.MimeMessage#MimeMessage( - * javax.mail.Session, java.io.InputStream) reconstruct}, and resend the - * original MimeMessage. The message parameter string is not a raw email - * if it starts with value returned from Level.SEVERE.getName(). - * Custom error managers can use the following test to determine if the - * msg parameter from this handler is a raw email: - * - *

      - * public void error(String msg, Exception ex, int code) {
      - *      if (msg == null || msg.length() == 0 || msg.startsWith(Level.SEVERE.getName())) {
      - *          super.error(msg, ex, code);
      - *      } else {
      - *          //The 'msg' parameter is a raw email.
      - *      }
      - * }
      - * 
      - * - * @author Jason Mehrens - * @since JavaMail 1.4.3 - */ -public class MailHandler extends Handler { - /** - * Use the emptyFilterArray method. - */ - private static final Filter[] EMPTY_FILTERS = new Filter[0]; - /** - * Use the emptyFormatterArray method. - */ - private static final Formatter[] EMPTY_FORMATTERS = new Formatter[0]; - /** - * Min byte size for header data. Used for initial arrays sizing. - */ - private static final int MIN_HEADER_SIZE = 1024; - /** - * Cache the off value. - */ - private static final int offValue = Level.OFF.intValue(); - /** - * The action to get and set the context class loader. - * Load this before it is loaded in the close method. - */ - private static final GetAndSetContext GET_AND_SET_CCL = - new GetAndSetContext(MailHandler.class); - /** - * A thread local mutex used to prevent logging loops. - * The MUTEX has 3 states: - * 1. MUTEX_RESET which is the null state. - * 2. MUTEX_PUBLISH on first entry of a push or publish. - * 3. MUTEX_REPORT when cycle of records is detected. - */ - private static final ThreadLocal MUTEX = new ThreadLocal(); - /** - * The marker object used to report a publishing state. - */ - private static final Level MUTEX_PUBLISH = Level.ALL; - /** - * The marker object used to report a error reporting state. - */ - private static final Level MUTEX_REPORT = Level.OFF; - /** - * Used to turn off security checks. - */ - private volatile boolean sealed; - /** - * Determines if we are inside of a push. - * Makes the handler properties read-only during a push. - */ - private boolean isWriting; - /** - * Holds all of the email server properties. - */ - private Properties mailProps; - /** - * Holds the authenticator required to login to the email server. - */ - private Authenticator auth; - /** - * Holds the session object used to generate emails. - * Sessions can be shared by multiple threads. - * See BUGID 6228391 - */ - private Session session; - /** - * Holds all of the log records that will be used to create the email. - */ - private LogRecord[] data; - /** - * The number of log records in the buffer. - */ - private int size; - /** - * The maximum number of log records to format per email. - * Used to roughly bound the size of an email. - * Every time the capacity is reached, the handler will push. - * The capacity will be negative if this handler is closed. - * Negative values are used to ensure all records are pushed. - */ - private int capacity; - /** - * Used to order all log records prior to formatting. The main email body - * and all attachments use the order determined by this comparator. If no - * comparator is present the log records will be in no specified order. - */ - private Comparator comparator; - /** - * Holds the formatter used to create the subject line of the email. - * A subject formatter is not required for the email message. - * All published records pass through the subject formatter. - */ - private Formatter subjectFormatter; - /** - * Holds the push level for this handler. - * This is only required if an email must be sent prior to shutdown - * or before the buffer is full. - */ - private Level pushLevel; - /** - * Holds the push filter for trigger conditions requiring an early push. - * Only gets called if the given log record is greater than or equal - * to the push level and the push level is not Level.OFF. - */ - private Filter pushFilter; - /** - * Holds the filters for each attachment. Filters are optional for - * each attachment. - */ - private Filter[] attachmentFilters; - /** - * Holds the formatters that create the content for each attachment. - * Each formatter maps directly to an attachment. The formatters - * getHead, format, and getTail methods are only called if one or more - * log records pass through the attachment filters. - */ - private Formatter[] attachmentFormatters; - /** - * Holds the formatters that create the file name for each attachment. - * Each formatter must produce a non null and non empty name. - * The final file name will be the concatenation of one getHead call, plus - * all of the format calls, plus one getTail call. - */ - private Formatter[] attachmentNames; - /** - * Used to override the content type for the body and set the content type - * for each attachment. - */ - private FileTypeMap contentTypes; - - /** - * Creates a MailHandler that is configured by the - * LogManager configuration properties. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - public MailHandler() { - init((Properties) null); - sealed = true; - } - - /** - * Creates a MailHandler that is configured by the - * LogManager configuration properties but overrides the - * LogManager capacity with the given capacity. - * @param capacity of the internal buffer. - * @throws IllegalArgumentException if capacity less than one. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - public MailHandler(final int capacity) { - init((Properties) null); - sealed = true; - setCapacity0(capacity); - } - - /** - * Creates a mail handler with the given mail properties. - * The key/value pairs are defined in the Java Mail API - * documentation. This Handler will also search the - * LogManager for defaults if needed. - * @param props a non null properties object. - * @throws NullPointerException if props is null. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - public MailHandler(final Properties props) { - if (props == null) { - throw new NullPointerException(); - } - init(props); - sealed = true; - setMailProperties0(props); - } - - /** - * Check if this Handler would actually log a given - * LogRecord into its internal buffer. - *

      - * This method checks if the LogRecord has an appropriate level and - * whether it satisfies any Filter including any attachment filters. - * However it does not check whether the LogRecord would - * result in a "push" of the buffer contents. - *

      - * @param record a LogRecord - * @return true if the LogRecord would be logged. - */ - @Override - public boolean isLoggable(final LogRecord record) { - int levelValue = getLevel().intValue(); - if (record.getLevel().intValue() < levelValue || levelValue == offValue) { - return false; - } - - Filter body = getFilter(); - if (body == null || body.isLoggable(record)) { - return true; - } - - return isAttachmentLoggable(record); - } - - /** - * Stores a LogRecord in the internal buffer. - *

      - * The isLoggable method is called to check if the given log record - * is loggable. If the given record is loggable, it is copied into - * an internal buffer. Then the record's level property is compared with - * the push level. If the given level of the LogRecord - * is greater than or equal to the push level then the push filter is - * called. If no push filter exists, the push filter returns true, - * or the capacity of the internal buffer has been reached then all buffered - * records are formatted into one email and sent to the server. - * - * @param record description of the log event. - */ - public void publish(final LogRecord record) { - /** - * It is possible for the handler to be closed after the - * call to isLoggable. In that case, the current thread - * will push to ensure that all published records are sent. - * See close(). - */ - if (tryMutex()) { - try { - if (isLoggable(record)) { - record.getSourceMethodName(); //Infer caller. - publish0(record); - } - } finally { - releaseMutex(); - } - } else { - reportUnPublishedError(record); - } - } - - /** - * Performs the publish after the record has been filtered. - * @param record the record. - * @since JavaMail 1.4.5 - */ - private void publish0(final LogRecord record) { - Message msg; - boolean priority; - synchronized (this) { - if (size == data.length && size < capacity) { - grow(); - } - - if (size < data.length) { - data[size] = record; - ++size; //Be nice to client compiler. - priority = isPushable(record); - if (priority || size >= capacity) { - msg = writeLogRecords(ErrorManager.WRITE_FAILURE); - } else { - msg = null; - } - } else { - priority = false; - msg = null; - } - } - - if (msg != null) { - send(msg, priority, ErrorManager.WRITE_FAILURE); - } - } - - /** - * Report to the error manager that a logging loop was detected and - * we are going to break the cycle of messages. It is possible that - * a custom error manager could continue the cycle in which case - * we will stop trying to report errors. - * @param record the record or null. - * @since JavaMail 1.4.6 - */ - private void reportUnPublishedError(LogRecord record) { - if (MUTEX_PUBLISH.equals(MUTEX.get())) { - MUTEX.set(MUTEX_REPORT); - try { - final String msg; - if (record != null) { - final SimpleFormatter f = new SimpleFormatter(); - msg = "Log record " + record.getSequenceNumber() - + " was not published. " - + head(f) + format(f, record) + tail(f, ""); - } else { - msg = null; - } - Exception e = new IllegalStateException( - "Recursive publish detected by thread " - + Thread.currentThread()); - reportError(msg, e, ErrorManager.WRITE_FAILURE); - } finally { - MUTEX.set(MUTEX_PUBLISH); - } - } - } - - /** - * Used to detect reentrance by the current thread to the publish method. - * This mutex is thread local scope and will not block other threads. - * The state is advanced on if the current thread is in a reset state. - * @return true if the mutex was acquired. - * @since JavaMail 1.4.6 - */ - private boolean tryMutex() { - if (MUTEX.get() == null) { - MUTEX.set(MUTEX_PUBLISH); - return true; - } else { - return false; - } - } - - /** - * Releases the mutex held by the current thread. - * This mutex is thread local scope and will not block other threads. - * @since JavaMail 1.4.6 - */ - private void releaseMutex() { - MUTEX.remove(); - } - - /** - * Pushes any buffered records to the email server as high importance with - * urgent priority. The internal buffer is then cleared. Does nothing if - * called from inside a push. - * @see #flush() - */ - public void push() { - push(true, ErrorManager.FLUSH_FAILURE); - } - - /** - * Pushes any buffered records to the email server as normal priority. - * The internal buffer is then cleared. Does nothing if called from inside - * a push. - * @see #push() - */ - public void flush() { - push(false, ErrorManager.FLUSH_FAILURE); - } - - /** - * Prevents any other records from being published. - * Pushes any buffered records to the email server as normal priority. - * The internal buffer is then cleared. Once this handler is closed it - * will remain closed. - *

      - * If this Handler is only implicitly closed by the - * LogManager, then verification should be - * turned on. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @see #flush() - */ - public void close() { - checkAccess(); //Security check first. - //The LogManager$Cleaner has a context class loader set to null. - //Set the CCL to this class loader for loading content handlers. - final Object ccl = getAndSetContextClassLoader(); - try { - Message msg = null; - synchronized (this) { - try { - msg = writeLogRecords(ErrorManager.CLOSE_FAILURE); - } finally { - super.setLevel(Level.OFF); //Change level after formatting. - /** - * The sign bit of the capacity is set to ensure that records - * that have passed isLoggable, but have yet to be added to - * the internal buffer, are immediately pushed as an email. - */ - if (this.capacity > 0) { - this.capacity = -this.capacity; - } - - //Ensure not inside a push. - if (size == 0 && data.length != 1) { - this.data = new LogRecord[1]; - } - } - } - - if (msg != null) { - send(msg, false, ErrorManager.CLOSE_FAILURE); - } - } finally { - setContextClassLoader(ccl); - } - } - - /** - * Set the log level specifying which message levels will be - * logged by this Handler. Message levels lower than this - * value will be discarded. - * @param newLevel the new value for the log level - * @throws NullPointerException if newLevel is null. - * @throws SecurityException if a security manager exists and - * the caller does not have LoggingPermission("control"). - */ - @Override - public synchronized void setLevel(final Level newLevel) { - if (this.capacity > 0) { - super.setLevel(newLevel); - } else { //Don't allow a closed handler to be opened (half way). - if (newLevel == null) { - throw new NullPointerException(); - } - checkAccess(); - } - } - - /** - * Gets the push level. The default is Level.OFF meaning that - * this Handler will only push when the internal buffer is full. - * @return the push level. - */ - public final synchronized Level getPushLevel() { - return this.pushLevel; - } - - /** - * Sets the push level. This level is used to trigger a push so that - * all pending records are formatted and sent to the email server. When - * the push level triggers a send, the resulting email is flagged as - * high importance with urgent priority. - * @param level Level object. - * @throws NullPointerException if level is null. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IllegalStateException if called from inside a push. - */ - public final synchronized void setPushLevel(final Level level) { - checkAccess(); - if (level == null) { - throw new NullPointerException(); - } - - if (isWriting) { - throw new IllegalStateException(); - } - this.pushLevel = level; - } - - /** - * Gets the push filter. The default is null. - * @return the push filter or null. - */ - public final synchronized Filter getPushFilter() { - return this.pushFilter; - } - - /** - * Sets the push filter. This filter is only called if the given - * LogRecord level was greater than the push level. If this - * filter returns true, all pending records are formatted and sent - * to the email server. When the push filter triggers a send, the resulting - * email is flagged as high importance with urgent priority. - * @param filter push filter or null - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IllegalStateException if called from inside a push. - */ - public final synchronized void setPushFilter(final Filter filter) { - checkAccess(); - if (isWriting) { - throw new IllegalStateException(); - } - this.pushFilter = filter; - } - - /** - * Gets the comparator used to order all LogRecord objects prior - * to formatting. If null then the order is unspecified. - * @return the LogRecord comparator. - */ - public final synchronized Comparator getComparator() { - return this.comparator; - } - - /** - * Sets the comparator used to order all LogRecord objects prior - * to formatting. If null then the order is unspecified. - * @param c the LogRecord comparator. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IllegalStateException if called from inside a push. - */ - public final synchronized void setComparator(Comparator c) { - checkAccess(); - if (isWriting) { - throw new IllegalStateException(); - } - this.comparator = c; - } - - /** - * Gets the number of log records the internal buffer can hold. When - * capacity is reached, Handler will format all LogRecord - * objects into one email message. - * @return the capacity. - */ - public final synchronized int getCapacity() { - assert capacity != Integer.MIN_VALUE && capacity != 0 : capacity; - return Math.abs(capacity); - } - - /** - * Gets the Authenticator used to login to the email server. - * @return an Authenticator or null if none is required. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - public final synchronized Authenticator getAuthenticator() { - checkAccess(); - return this.auth; - } - - /** - * Sets the Authenticator used to login to the email server. - * @param auth an Authenticator object or null if none is required. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IllegalStateException if called from inside a push. - */ - public final void setAuthenticator(final Authenticator auth) { - this.setAuthenticator0(auth); - } - - /** - * Sets the Authenticator used to login to the email server. - * @param password a password or null if none is required. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IllegalStateException if called from inside a push. - * @see String#toCharArray() - * @since JavaMail 1.4.6 - */ - public final void setAuthenticator(final char... password) { - if (password == null) { - setAuthenticator0((Authenticator) null); - } else { - setAuthenticator0(new DefaultAuthenticator(new String(password))); - } - } - - private void setAuthenticator0(final Authenticator auth) { - checkAccess(); - - Session settings; - synchronized (this) { - if (isWriting) { - throw new IllegalStateException(); - } - this.auth = auth; - settings = fixUpSession(); - } - verifySettings(settings); - } - - /** - * Sets the mail properties used for the session. The key/value pairs - * are defined in the Java Mail API documentation. This - * Handler will also search the LogManager for defaults - * if needed. - * @param props a non null properties object. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws NullPointerException if props is null. - * @throws IllegalStateException if called from inside a push. - */ - public final void setMailProperties(Properties props) { - this.setMailProperties0(props); - } - - private void setMailProperties0(Properties props) { - checkAccess(); - props = (Properties) props.clone(); //Allow subclass. - Session settings; - synchronized (this) { - if (isWriting) { - throw new IllegalStateException(); - } - this.mailProps = props; - settings = fixUpSession(); - } - verifySettings(settings); - } - - /** - * Gets a copy of the mail properties used for the session. - * @return a non null properties object. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - public final Properties getMailProperties() { - checkAccess(); - final Properties props; - synchronized (this) { - props = this.mailProps; - } - return (Properties) props.clone(); - } - - /** - * Gets the attachment filters. If the attachment filter does not - * allow any LogRecord to be formatted, the attachment may - * be omitted from the email. - * @return a non null array of attachment filters. - */ - public final Filter[] getAttachmentFilters() { - return readOnlyAttachmentFilters().clone(); - } - - /** - * Sets the attachment filters. - * @param filters a non null array of filters. A null - * index value is allowed. A null value means that all - * records are allowed for the attachment at that index. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws NullPointerException if filters is null - * @throws IndexOutOfBoundsException if the number of attachment - * name formatters do not match the number of attachment formatters. - * @throws IllegalStateException if called from inside a push. - */ - public final void setAttachmentFilters(Filter... filters) { - checkAccess(); - filters = copyOf(filters, filters.length, Filter[].class); - synchronized (this) { - if (this.attachmentFormatters.length != filters.length) { - throw attachmentMismatch(this.attachmentFormatters.length, filters.length); - } - - if (isWriting) { - throw new IllegalStateException(); - } - this.attachmentFilters = filters; - } - } - - /** - * Gets the attachment formatters. This Handler is using - * attachments only if the returned array length is non zero. - * @return a non null array of formatters. - */ - public final Formatter[] getAttachmentFormatters() { - Formatter[] formatters; - synchronized (this) { - formatters = this.attachmentFormatters; - } - return formatters.clone(); - } - - /** - * Sets the attachment Formatter object for this handler. - * The number of formatters determines the number of attachments per - * email. This method should be the first attachment method called. - * To remove all attachments, call this method with empty array. - * @param formatters a non null array of formatters. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws NullPointerException if the given array or any array index is - * null. - * @throws IllegalStateException if called from inside a push. - */ - public final void setAttachmentFormatters(Formatter... formatters) { - checkAccess(); - if (formatters.length == 0) { //Null check and length check. - formatters = emptyFormatterArray(); - } else { - formatters = copyOf(formatters, - formatters.length, Formatter[].class); - for (int i = 0; i < formatters.length; ++i) { - if (formatters[i] == null) { - throw new NullPointerException(atIndexMsg(i)); - } - } - } - - synchronized (this) { - if (isWriting) { - throw new IllegalStateException(); - } - - this.attachmentFormatters = formatters; - this.fixUpAttachmentFilters(); - this.fixUpAttachmentNames(); - } - } - - /** - * Gets the attachment name formatters. - * If the attachment names were set using explicit names then - * the names can be returned by calling toString on each - * attachment name formatter. - * @return non null array of attachment name formatters. - */ - public final Formatter[] getAttachmentNames() { - final Formatter[] formatters; - synchronized (this) { - formatters = this.attachmentNames; - } - return formatters.clone(); - } - - /** - * Sets the attachment file name for each attachment. The caller must - * ensure that the attachment file names do not contain any line breaks. - * This method will create a set of custom formatters. - * @param names an array of names. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IndexOutOfBoundsException if the number of attachment - * names do not match the number of attachment formatters. - * @throws IllegalArgumentException if any name is empty. - * @throws NullPointerException if any given array or name is null. - * @throws IllegalStateException if called from inside a push. - * @see Character#isISOControl(char) - * @see Character#isISOControl(int) - */ - public final void setAttachmentNames(final String... names) { - checkAccess(); - - final Formatter[] formatters; - if (names.length == 0) { - formatters = emptyFormatterArray(); - } else { - formatters = new Formatter[names.length]; - } - - for (int i = 0; i < names.length; ++i) { - final String name = names[i]; - if (name != null) { - if (name.length() > 0) { - formatters[i] = new TailNameFormatter(name); - } else { - throw new IllegalArgumentException(atIndexMsg(i)); - } - } else { - throw new NullPointerException(atIndexMsg(i)); - } - } - - synchronized (this) { - if (this.attachmentFormatters.length != names.length) { - throw attachmentMismatch(this.attachmentFormatters.length, names.length); - } - - if (isWriting) { - throw new IllegalStateException(); - } - this.attachmentNames = formatters; - } - } - - /** - * Sets the attachment file name formatters. The format method of each - * attachment formatter will see only the LogRecord objects that - * passed its attachment filter during formatting. The format method will - * typically return an empty string. Instead of being used to format - * records, it is used to gather information about the contents of an - * attachment. The getTail method should be used to construct the - * attachment file name and reset any formatter collected state. The - * formatter must ensure that the attachment file name does not contain any - * line breaks. The toString method of the given formatter should - * be overridden to provide a useful attachment file name, if possible. - * @param formatters and array of attachment name formatters. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws IndexOutOfBoundsException if the number of attachment - * name formatters do not match the number of attachment formatters. - * @throws NullPointerException if any given array or name is null. - * @throws IllegalStateException if called from inside a push. - * @see Character#isISOControl(char) - * @see Character#isISOControl(int) - */ - public final void setAttachmentNames(Formatter... formatters) { - checkAccess(); - - formatters = copyOf(formatters, formatters.length, Formatter[].class); - for (int i = 0; i < formatters.length; ++i) { - if (formatters[i] == null) { - throw new NullPointerException(atIndexMsg(i)); - } - } - - synchronized (this) { - if (this.attachmentFormatters.length != formatters.length) { - throw attachmentMismatch(this.attachmentFormatters.length, formatters.length); - } - - if (isWriting) { - throw new IllegalStateException(); - } - - this.attachmentNames = formatters; - } - } - - /** - * Gets the formatter used to create the subject line. - * If the subject was created using a literal string then - * the toString method can be used to get the subject line. - * @return the formatter. - */ - public final synchronized Formatter getSubject() { - return this.subjectFormatter; - } - - /** - * Sets a literal string for the email subject. The caller must ensure that - * the subject line does not contain any line breaks. - * @param subject a non null string. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws NullPointerException if subject is null. - * @throws IllegalStateException if called from inside a push. - * @see Character#isISOControl(char) - * @see Character#isISOControl(int) - */ - public final void setSubject(final String subject) { - if (subject != null) { - this.setSubject(new TailNameFormatter(subject)); - } else { - checkAccess(); - throw new NullPointerException(); - } - } - - /** - * Sets the subject formatter for email. The format method of the subject - * formatter will see all LogRecord objects that were published to - * this Handler during formatting and will typically return an - * empty string. This formatter is used to gather information to create a - * summary about what information is contained in the email. The - * getTail method should be used to construct the subject and reset - * any formatter collected state. The formatter must ensure that the - * subject line does not contain any line breaks. The toString - * method of the given formatter should be overridden to provide a useful - * subject, if possible. - * @param format the subject formatter. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - * @throws NullPointerException if format is null. - * @throws IllegalStateException if called from inside a push. - * @see Character#isISOControl(char) - * @see Character#isISOControl(int) - */ - public final void setSubject(final Formatter format) { - checkAccess(); - if (format == null) { - throw new NullPointerException(); - } - - synchronized (this) { - if (isWriting) { - throw new IllegalStateException(); - } - this.subjectFormatter = format; - } - } - - /** - * Protected convenience method to report an error to this Handler's - * ErrorManager. This method will prefix all non null error messages with - * Level.SEVERE.getName(). This allows the receiving error - * manager to determine if the msg parameter is a simple error - * message or a raw email message. - * @param msg a descriptive string (may be null) - * @param ex an exception (may be null) - * @param code an error code defined in ErrorManager - */ - @Override - protected void reportError(String msg, Exception ex, int code) { - if (msg != null) { - super.reportError(Level.SEVERE.getName() + ": " + msg, ex, code); - } else { - super.reportError(null, ex, code); - } - } - - /** - * Calls log manager checkAccess if this is sealed. - */ - final void checkAccess() { - if (sealed) { - LogManagerProperties.getLogManager().checkAccess(); - } - } - - /** - * Determines the mimeType of a formatter from the getHead call. - * This could be made protected, or a new class could be created to do - * this type of conversion. Currently, this is only used for the body - * since the attachments are computed by filename. - * Package-private for unit testing. - * @param head any head string. - * @return return the mime type or null for text/plain. - */ - final String contentTypeOf(String head) { - if (!isEmpty(head)) { - final int MAX_CHARS = 25; - if (head.length() > MAX_CHARS) { - head = head.substring(0, MAX_CHARS); - } - try { - final String encoding = getEncodingName(); - final ByteArrayInputStream in - = new ByteArrayInputStream(head.getBytes(encoding)); - - assert in.markSupported() : in.getClass().getName(); - return URLConnection.guessContentTypeFromStream(in); - } catch (final IOException IOE) { - reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE); - } - } - return null; //text/plain - } - - /** - * Determines if the given throwable is a no content exception. - * Package-private for unit testing. - * @param msg the message without content. - * @param t the throwable to test. - * @return true if the throwable is a missing content exception. - * @throws NullPointerException if any of the arguments are null. - * @since JavaMail 1.4.5 - */ - final boolean isMissingContent(Message msg, Throwable t) { - for (Throwable cause = t.getCause(); cause != null;) { - t = cause; - cause = cause.getCause(); - } - - try { - msg.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE)); - } catch (final RuntimeException RE) { - throw RE; //Avoid catch all. - } catch (final Exception noContent) { - final String txt = noContent.getMessage(); - if (!isEmpty(txt) && noContent.getClass() == t.getClass()) { - return txt.equals(t.getMessage()); - } - } - return false; - } - - /** - * Converts a mime message to a raw string or formats the reason - * why message can't be changed to raw string and reports it. - * @param msg the mime message. - * @param ex the original exception. - * @param code the ErrorManager code. - * @since JavaMail 1.4.5 - */ - private void reportError(Message msg, Exception ex, int code) { - try { //Use super call so we do not prefix raw email. - super.reportError(toRawString(msg), ex, code); - } catch (final MessagingException rawMe) { - reportError(toMsgString(rawMe), ex, code); - } catch (final IOException rawIo) { - reportError(toMsgString(rawIo), ex, code); - } - } - - /** - * Determines the mimeType from the given file name. - * Used to override the body content type and used for all attachments. - * @param name the file name or class name. - * @return the mime type or null for text/plain. - */ - private String getContentType(final String name) { - assert Thread.holdsLock(this); - final String type = contentTypes.getContentType(name); - if ("application/octet-stream".equalsIgnoreCase(type)) { - return null; //Formatters return strings, default to text/plain. - } - return type; - } - - /** - * Gets the encoding set for this handler, mime encoding, or file encoding. - * @return the java charset name, never null. - * @since JavaMail 1.4.5 - */ - private String getEncodingName() { - String encoding = getEncoding(); - if (encoding == null) { - encoding = MimeUtility.getDefaultJavaCharset(); - } - return encoding; - } - - /** - * Set the content for a part using the encoding assigned to the handler. - * @param part the part to assign. - * @param buf the formatted data. - * @param type the mime type. - * @throws MessagingException if there is a problem. - */ - private void setContent(MimeBodyPart part, CharSequence buf, String type) throws MessagingException { - final String encoding = getEncodingName(); - if (type != null && !"text/plain".equalsIgnoreCase(type)) { - type = contentWithEncoding(type, encoding); - try { - DataSource source = new ByteArrayDataSource(buf.toString(), type); - part.setDataHandler(new DataHandler(source)); - } catch (final IOException IOE) { - reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE); - part.setText(buf.toString(), encoding); - } - } else { - part.setText(buf.toString(), MimeUtility.mimeCharset(encoding)); - } - } - - /** - * Replaces the charset parameter with the current encoding. - * @param type the content type. - * @param encoding the java charset name. - * @return the type with a specified encoding. - */ - private String contentWithEncoding(String type, String encoding) { - assert encoding != null; - try { - final ContentType ct = new ContentType(type); - ct.setParameter("charset", MimeUtility.mimeCharset(encoding)); - encoding = ct.toString(); //See javax.mail.internet.ContentType. - if (!isEmpty(encoding)) { - type = encoding; - } - } catch (final MessagingException ME) { - reportError(type, ME, ErrorManager.FORMAT_FAILURE); - } - return type; - } - - /** - * Sets the capacity for this handler. This method is kept private - * because we would have to define a public policy for when the size is - * greater than the capacity. - * I.E. do nothing, flush now, truncate now, push now and resize. - * @param newCapacity the max number of records. - * @throws IllegalStateException if called from inside a push. - */ - private synchronized void setCapacity0(final int newCapacity) { - if (newCapacity <= 0) { - throw new IllegalArgumentException("Capacity must be greater than zero."); - } - - if (isWriting) { - throw new IllegalStateException(); - } - - if (this.capacity < 0) { //If closed, remain closed. - this.capacity = -newCapacity; - } else { - this.capacity = newCapacity; - } - } - - /** - * Gets the attachment filters under a lock. The attachment filters - * are treated as copy-on-write, so the returned array must never be - * modified or published outside this class. - * @return a read only array of filters. - */ - private synchronized Filter[] readOnlyAttachmentFilters() { - return this.attachmentFilters; - } - - /** - * Factory for empty formatter arrays. - * @return an empty array. - */ - private static Formatter[] emptyFormatterArray() { - return EMPTY_FORMATTERS; - } - - /** - * Factory for empty filter arrays. - * @return an empty array. - */ - private static Filter[] emptyFilterArray() { - return EMPTY_FILTERS; - } - - /** - * Expand or shrink the attachment name formatters. - * @return true if fixed. - */ - private boolean fixUpAttachmentNames() { - assert Thread.holdsLock(this); - boolean fixed = false; - final int expect = this.attachmentFormatters.length; - final int current = this.attachmentNames.length; - if (current != expect) { - this.attachmentNames = copyOf(attachmentNames, expect); - fixed = current != 0; - } - - //Copy of zero length array is cheap, warm up copyOf. - if (expect == 0) { - this.attachmentNames = emptyFormatterArray(); - assert this.attachmentNames.length == 0; - } else { - for (int i = 0; i < expect; ++i) { - if (this.attachmentNames[i] == null) { - this.attachmentNames[i] = new TailNameFormatter( - toString(this.attachmentFormatters[i])); - } - } - } - return fixed; - } - - /** - * Expand or shrink the attachment filters. - * @return true if fixed. - */ - private boolean fixUpAttachmentFilters() { - assert Thread.holdsLock(this); - - boolean fixed = false; - final int expect = this.attachmentFormatters.length; - final int current = this.attachmentFilters.length; - if (current != expect) { - this.attachmentFilters = copyOf(attachmentFilters, expect); - fixed = current != 0; - - //Array elements default to null so skip filling if body filter - //is null. If not null then only assign to expanded elements. - final Filter body = super.getFilter(); - if (body != null) { - for (int i = current; i < expect; ++i) { - this.attachmentFilters[i] = body; - } - } - } - - //Copy of zero length array is cheap, warm up copyOf. - if (expect == 0) { - this.attachmentFilters = emptyFilterArray(); - assert this.attachmentFilters.length == 0; - } - return fixed; - } - - /** - * Copies the given array. Can be removed when Java Mail requires Java 1.6. - * @param a the original array. - * @param len the new size. - * @return new copy - */ - private static T[] copyOf(final T[] a, final int len) { - return (T[]) copyOf(a, len, a.getClass()); - } - - /** - * Copies the given array to a new array type. - * Can be removed when Java Mail requires Java 1.6. - * @param a the original array. - * @param len the new size. - * @param type the array type. - * @return new copy - */ - private static T[] copyOf(U[] a, int len, Class type) { - final T[] copy = (T[]) Array.newInstance(type.getComponentType(), len); - System.arraycopy(a, 0, copy, 0, Math.min(len, a.length)); - return copy; - } - - /** - * Sets the size to zero and clears the current buffer. - */ - private void reset() { - assert Thread.holdsLock(this); - if (size < data.length) { - Arrays.fill(data, 0, size, null); - } else { - Arrays.fill(data, null); - } - this.size = 0; - } - - /** - * Expands the internal buffer up to the capacity. - */ - private void grow() { - assert Thread.holdsLock(this); - final int len = data.length; - int newCapacity = len + (len >> 1) + 1; - if (newCapacity > capacity || newCapacity < len) { - newCapacity = capacity; - } - assert len != capacity : len; - this.data = copyOf(data, newCapacity); - } - - /** - * Configures the handler properties from the log manager. - * @param props the given mail properties. Maybe null and are never - * captured by this handler. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). - */ - private synchronized void init(final Properties props) { - final LogManager manager = LogManagerProperties.getLogManager(); - final String p = getClass().getName(); - this.mailProps = new Properties(); //See method comments. - this.contentTypes = FileTypeMap.getDefaultFileTypeMap(); - - //Assign any custom error manager first so it can detect all failures. - initErrorManager(manager, p); - - initLevel(manager, p); - initFilter(manager, p); - initCapacity(manager, p); - initAuthenticator(manager, p); - - initEncoding(manager, p); - initFormatter(manager, p); - initComparator(manager, p); - initPushLevel(manager, p); - initPushFilter(manager, p); - - initSubject(manager, p); - - initAttachmentFormaters(manager, p); - initAttachmentFilters(manager, p); - initAttachmentNames(manager, p); - - if (props == null && manager.getProperty(p.concat(".verify")) != null) { - verifySettings(initSession()); - } - intern(); //Show verify warnings first. - } - - /** - * Interns the error manager, formatters, and filters contained in this - * handler. The comparator is not interned. This method can only be - * called from init after all of formatters and filters are in a constructed - * and in a consistent state. - * @since JavaMail 1.5.0 - */ - private void intern() { - assert Thread.holdsLock(this); - try { - Object canidate; - Object result; - final Map seen = new HashMap(); - try { - intern(seen, super.getErrorManager()); - } catch (final SecurityException se) { - reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); - } - - try { - canidate = super.getFilter(); - result = intern(seen, canidate); - if (result != canidate) { - super.setFilter(Filter.class.cast(result)); - } - - canidate = super.getFormatter(); - result = intern(seen, canidate); - if (result != canidate) { - super.setFormatter(Formatter.class.cast(result)); - } - } catch (final SecurityException se) { - reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); - } - - canidate = this.subjectFormatter; - result = intern(seen, canidate); - if (result != canidate) { - this.subjectFormatter = Formatter.class.cast(result); - } - - canidate = this.pushFilter; - result = intern(seen, canidate); - if (result != canidate) { - this.pushFilter = Filter.class.cast(result); - } - - for (int i = 0; i < attachmentFormatters.length; ++i) { - canidate = attachmentFormatters[i]; - result = intern(seen, canidate); - if (result != canidate) { - attachmentFormatters[i] = Formatter.class.cast(result); - } - - canidate = attachmentFilters[i]; - result = intern(seen, canidate); - if (result != canidate) { - attachmentFilters[i] = Filter.class.cast(result); - } - - canidate = attachmentNames[i]; - result = intern(seen, canidate); - if (result != canidate) { - attachmentNames[i] = Formatter.class.cast(result); - } - } - } catch (final Exception skip) { - reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE); - } - } - - /** - * If possible performs an intern of the given object into the - * map. If the object can not be interned the given object is returned. - * @param m the map used to record the interned values. - * @param o the object to try an intern. - * @return the original object or an intern replacement. - * @throws SecurityException if this operation is not allowed by the - * security manager. - * @throws Exception if there is an unexpected problem. - * @since JavaMail 1.5.0 - */ - private Object intern(Map m, Object o) throws Exception { - if (o == null) { - return null; - } - - /** - * The common case is that most objects will not intern. The given - * object has a public no argument constructor or is an instance of a - * TailNameFormatter. TailNameFormatter is safe use as a map key. - * For everything else we create a clone of the given object. - * This is done because of the following: - * 1. Clones can be used to test that a class provides an equals method - * and that the equals method works correctly. - * 2. Calling equals on the given object is assumed to be cheap. - * 3. The intern map can be filtered so it only contains objects that - * can be interned, which reduces the memory footprint. - * 4. Clones are method local garbage. - * 5. Hash code is only called on the clones so bias locking is not - * disabled on the objects the handler will use. - */ - final Object key; - if (o.getClass().getName().equals(TailNameFormatter.class.getName())) { - key = o; - } else { - //This call was already made in the LogManagerProperties so this - //shouldn't trigger loading of any lazy reflection code. - key = o.getClass().getConstructor().newInstance(); - } - - final Object use; - //Check the classloaders of each object avoiding the security manager. - if (key.getClass() == o.getClass()) { - Object found = m.get(key); //Transitive equals test. - if (found == null) { - //Ensure that equals is symmetric to prove intern is safe. - final boolean right = key.equals(o); - final boolean left = o.equals(key); - if (right && left) { - //Assume hashCode is defined at this point. - found = m.put(o, o); - if (found != null) { - reportNonDiscriminating(key, found); - found = m.remove(key); - if (found != o) { - reportNonDiscriminating(key, found); - m.clear(); //Try to restore order. - } - } - } else { - if (right != left) { - reportNonSymmetric(o, key); - } - } - use = o; - } else { - //Check for a discriminating equals method. - if (o.getClass() == found.getClass()) { - use = found; - } else { - reportNonDiscriminating(o, found); - use = o; - } - } - } else { - use = o; - } - return use; - } - - /** - * Checks a string value for null or empty. - * @param s the string. - * @return true if the given string is null or zero length. - */ - private static boolean isEmpty(final String s) { - return s == null || s.length() == 0; - } - - /** - * Checks that a string is not empty and not equal to the literal "null". - * @param name the string to check for a value. - * @return true if the string has a valid value. - */ - private static boolean hasValue(final String name) { - return !isEmpty(name) && !"null".equalsIgnoreCase(name); - } - - private void initAttachmentFilters(LogManager manager, String p) { - assert Thread.holdsLock(this); - assert this.attachmentFormatters != null; - final String list = manager.getProperty(p.concat(".attachment.filters")); - if (!isEmpty(list)) { - final String[] names = list.split(","); - Filter[] a = new Filter[names.length]; - for (int i = 0; i < a.length; ++i) { - names[i] = names[i].trim(); - if (!"null".equalsIgnoreCase(names[i])) { - try { - a[i] = LogManagerProperties.newFilter(names[i]); - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - } - - this.attachmentFilters = a; - if (fixUpAttachmentFilters()) { - reportError("Attachment filters.", - attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE); - } - } else { - this.attachmentFilters = emptyFilterArray(); - fixUpAttachmentFilters(); - } - } - - private void initAttachmentFormaters(LogManager manager, String p) { - assert Thread.holdsLock(this); - final String list = manager.getProperty(p.concat(".attachment.formatters")); - if (!isEmpty(list)) { - final Formatter[] a; - final String[] names = list.split(","); - if (names.length == 0) { - a = emptyFormatterArray(); - } else { - a = new Formatter[names.length]; - } - - for (int i = 0; i < a.length; ++i) { - names[i] = names[i].trim(); - if (!"null".equalsIgnoreCase(names[i])) { - try { - a[i] = LogManagerProperties.newFormatter(names[i]); - if (a[i] instanceof TailNameFormatter) { - final Exception CNFE = new ClassNotFoundException(a[i].toString()); - reportError("Attachment formatter.", CNFE, ErrorManager.OPEN_FAILURE); - a[i] = new SimpleFormatter(); - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - a[i] = new SimpleFormatter(); - } - } else { - final Exception NPE = new NullPointerException(atIndexMsg(i)); - reportError("Attachment formatter.", NPE, ErrorManager.OPEN_FAILURE); - a[i] = new SimpleFormatter(); - } - } - - this.attachmentFormatters = a; - } else { - this.attachmentFormatters = emptyFormatterArray(); - } - } - - private void initAttachmentNames(LogManager manager, String p) { - assert Thread.holdsLock(this); - assert this.attachmentFormatters != null; - - final String list = manager.getProperty(p.concat(".attachment.names")); - if (!isEmpty(list)) { - final String[] names = list.split(","); - final Formatter[] a = new Formatter[names.length]; - for (int i = 0; i < a.length; ++i) { - names[i] = names[i].trim(); - if (!"null".equalsIgnoreCase(names[i])) { - try { - try { - a[i] = LogManagerProperties.newFormatter(names[i]); - } catch (final ClassNotFoundException literal) { - a[i] = new TailNameFormatter(names[i]); - } catch (final ClassCastException literal) { - a[i] = new TailNameFormatter(names[i]); - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } else { - final Exception NPE = new NullPointerException(atIndexMsg(i)); - reportError("Attachment names.", NPE, ErrorManager.OPEN_FAILURE); - } - } - - this.attachmentNames = a; - if (fixUpAttachmentNames()) { //Any null indexes are repaired. - reportError("Attachment names.", - attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE); - } - } else { - this.attachmentNames = emptyFormatterArray(); - fixUpAttachmentNames(); - } - } - - private void initAuthenticator(LogManager manager, String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".authenticator")); - if (hasValue(name)) { - try { - this.auth = LogManagerProperties.newAuthenticator(name); - } catch (final SecurityException SE) { - throw SE; - } catch (final ClassNotFoundException literalAuth) { - this.auth = new DefaultAuthenticator(name); - } catch (final ClassCastException literalAuth) { - this.auth = new DefaultAuthenticator(name); - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - } - - private void initLevel(LogManager manager, String p) { - assert Thread.holdsLock(this); - try { - final String val = manager.getProperty(p.concat(".level")); - if (val != null) { - super.setLevel(Level.parse(val)); - } else { - super.setLevel(Level.WARNING); - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); - try { - super.setLevel(Level.WARNING); - } catch (final RuntimeException fail) { - reportError(fail.getMessage(), fail, ErrorManager.OPEN_FAILURE); - } - } - } - - private void initFilter(LogManager manager, String p) { - assert Thread.holdsLock(this); - try { - String name = manager.getProperty(p.concat(".filter")); - if (hasValue(name)) { - super.setFilter(LogManagerProperties.newFilter(name)); - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - - private void initCapacity(LogManager manager, String p) { - assert Thread.holdsLock(this); - final int DEFAULT_CAPACITY = 1000; - try { - final String value = manager.getProperty(p.concat(".capacity")); - if (value != null) { - this.setCapacity0(Integer.parseInt(value)); - } else { - this.setCapacity0(DEFAULT_CAPACITY); - } - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); - } - - if (capacity <= 0) { - capacity = DEFAULT_CAPACITY; - } - - this.data = new LogRecord[1]; - } - - private void initEncoding(LogManager manager, String p) { - assert Thread.holdsLock(this); - try { - super.setEncoding(manager.getProperty(p.concat(".encoding"))); - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final UnsupportedEncodingException UEE) { - reportError(UEE.getMessage(), UEE, ErrorManager.OPEN_FAILURE); - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); - } - } - - private void initErrorManager(LogManager manager, String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".errorManager")); - if (name != null) { - try { - ErrorManager em = LogManagerProperties.newErrorManager(name); - super.setErrorManager(em); - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - } - - private void initFormatter(LogManager manager, String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".formatter")); - if (hasValue(name)) { - try { - final Formatter formatter = LogManagerProperties.newFormatter(name); - assert formatter != null; - if (formatter instanceof TailNameFormatter == false) { - super.setFormatter(formatter); - } else { - super.setFormatter(new SimpleFormatter()); - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - try { - super.setFormatter(new SimpleFormatter()); - } catch (final RuntimeException fail) { - reportError(fail.getMessage(), fail, ErrorManager.OPEN_FAILURE); - } - } - } else { - super.setFormatter(new SimpleFormatter()); - } - } - - private void initComparator(LogManager manager, String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".comparator")); - String reverse = manager.getProperty(p.concat(".comparator.reverse")); - try { - if (hasValue(name)) { - comparator = LogManagerProperties.newComparator(name); - if (Boolean.parseBoolean(reverse)) { - assert comparator != null : "null"; - comparator = LogManagerProperties.reverseOrder(comparator); - } - } else { - if (!isEmpty(reverse)) { - throw new IllegalArgumentException( - "No comparator to reverse."); - } - } - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - - private void initPushLevel(LogManager manager, String p) { - assert Thread.holdsLock(this); - try { - final String val = manager.getProperty(p.concat(".pushLevel")); - if (val != null) { - this.pushLevel = Level.parse(val); - } - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); - } - - if (this.pushLevel == null) { - this.pushLevel = Level.OFF; - } - } - - private void initPushFilter(LogManager manager, String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".pushFilter")); - if (hasValue(name)) { - try { - this.pushFilter = LogManagerProperties.newFilter(name); - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } - } - - private void initSubject(LogManager manager, final String p) { - assert Thread.holdsLock(this); - String name = manager.getProperty(p.concat(".subject")); - if (hasValue(name)) { - try { - this.subjectFormatter = LogManagerProperties.newFormatter(name); - } catch (final SecurityException SE) { - throw SE; //Avoid catch all. - } catch (final ClassNotFoundException literalSubject) { - this.subjectFormatter = new TailNameFormatter(name); - } catch (final ClassCastException literalSubject) { - this.subjectFormatter = new TailNameFormatter(name); - } catch (final Exception E) { - this.subjectFormatter = new TailNameFormatter(name); - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } - } else { - if (name != null) { - this.subjectFormatter = new TailNameFormatter(name); - } - } - - if (this.subjectFormatter == null) { //Ensure not null. - this.subjectFormatter = new TailNameFormatter(""); - } - } - - /** - * Check if any attachment would actually format the given - * LogRecord. This method does not check if the handler - * is level is set to OFF or if the handler is closed. - * @param record a LogRecord - * @return true if the LogRecord would be formatted. - */ - private boolean isAttachmentLoggable(final LogRecord record) { - final Filter[] filters = readOnlyAttachmentFilters(); - for (int i = 0; i < filters.length; ++i) { - final Filter f = filters[i]; - if (f == null || f.isLoggable(record)) { - return true; - } - } - return false; - } - - /** - * Check if this Handler would push after storing the - * LogRecord into its internal buffer. - * @param record a LogRecord - * @return true if the LogRecord triggers an email push. - */ - private boolean isPushable(final LogRecord record) { - assert Thread.holdsLock(this); - final int value = getPushLevel().intValue(); - if (value == offValue || record.getLevel().intValue() < value) { - return false; - } - - final Filter filter = getPushFilter(); - return filter == null || filter.isLoggable(record); - } - - /** - * Used to perform push or flush. - * @param priority true for high priority otherwise false for normal. - * @param code the error manager code. - */ - private void push(final boolean priority, final int code) { - if (tryMutex()) { - try { - final Message msg = writeLogRecords(code); - if (msg != null) { - send(msg, priority, code); - } - } finally { - releaseMutex(); - } - } else { - reportUnPublishedError(null); - } - } - - /** - * Used to send the generated email or write its contents to the - * error manager for this handler. This method does not hold any - * locks so new records can be added to this handler during a send or - * failure. - * @param msg the message or null. - * @param priority true for high priority or false for normal. - * @param code the ErrorManager code. - * @throws NullPointerException if message is null. - */ - private void send(Message msg, boolean priority, int code) { - try { - envelopeFor(msg, priority); - Transport.send(msg); //Calls save changes. - } catch (final Exception E) { - reportError(msg, E, code); - } - } - - /** - * Performs a sort on the records if needed. - * Any exception thrown during a sort is considered a formatting error. - */ - private void sort() { - assert Thread.holdsLock(this); - if (comparator != null) { - try { - if (size != 1) { - Arrays.sort(data, 0, size, comparator); - } else { - comparator.compare(data[0], data[0]); - } - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); - } - } - } - - /** - * Formats all records in the buffer and places the output in a Message. - * This method holds a lock on this handler. - * @param code the error manager code. - * @return null if there are no records or is currently in a push. - * Otherwise a new message is created with a formatted message and - * attached session. - */ - private synchronized Message writeLogRecords(final int code) { - if (size == 0 || isWriting) { - return null; - } - - isWriting = true; - try { - sort(); - if (session == null) { - initSession(); - } - MimeMessage msg = new MimeMessage(session); - msg.setDescription(descriptionFrom(comparator, pushLevel, pushFilter)); - - /** - * Parts are lazily created when an attachment performs a getHead - * call. Therefore, a null part at an index means that the head is - * required. - */ - MimeBodyPart[] parts = new MimeBodyPart[attachmentFormatters.length]; - - /** - * The buffers are lazily created when the part requires a getHead. - */ - StringBuilder[] buffers = new StringBuilder[parts.length]; - - String contentType = null; - StringBuilder buf = null; - - appendSubject(msg, head(subjectFormatter)); - - final MimeBodyPart body = createBodyPart(); - final Formatter bodyFormat = getFormatter(); - final Filter bodyFilter = getFilter(); - - Locale lastLocale = null; - for (int ix = 0; ix < size; ++ix) { - boolean formatted = false; - final LogRecord r = data[ix]; - data[ix] = null; //Clear while formatting. - - final Locale locale = localeFor(r); - appendSubject(msg, format(subjectFormatter, r)); - - if (bodyFilter == null || bodyFilter.isLoggable(r)) { - if (buf == null) { - buf = new StringBuilder(); - final String head = head(bodyFormat); - buf.append(head); - contentType = contentTypeOf(head); - } - formatted = true; - buf.append(format(bodyFormat, r)); - if (locale != null && !locale.equals(lastLocale)) { - appendContentLang(body, locale); - } - } - - for (int i = 0; i < parts.length; ++i) { - final Filter af = attachmentFilters[i]; - if (af == null || af.isLoggable(r)) { - if (parts[i] == null) { - parts[i] = createBodyPart(i); - buffers[i] = new StringBuilder(); - buffers[i].append(head(attachmentFormatters[i])); - appendFileName(parts[i], head(attachmentNames[i])); - } - formatted = true; - appendFileName(parts[i], format(attachmentNames[i], r)); - buffers[i].append(format(attachmentFormatters[i], r)); - if (locale != null && !locale.equals(lastLocale)) { - appendContentLang(parts[i], locale); - } - } - } - - if (formatted) { - if (locale != null && !locale.equals(lastLocale)) { - appendContentLang(msg, locale); - } - } else { //Belongs to no mime part. - reportFilterError(r); - } - lastLocale = locale; - } - this.size = 0; - - for (int i = parts.length - 1; i >= 0; --i) { - if (parts[i] != null) { - appendFileName(parts[i], tail(attachmentNames[i], "err")); - buffers[i].append(tail(attachmentFormatters[i], "")); - - if (buffers[i].length() > 0) { - String name = parts[i].getFileName(); - if (isEmpty(name)) { //Exceptional case. - name = toString(attachmentFormatters[i]); - parts[i].setFileName(name); - } - setContent(parts[i], buffers[i], getContentType(name)); - } else { - setIncompleteCopy(msg); - parts[i] = null; //Skip this part. - } - buffers[i] = null; - } - } - - if (buf != null) { - buf.append(tail(bodyFormat, "")); - //This body part is always added, even if the buffer is empty, - //so the body is never considered an incomplete-copy. - } else { - buf = new StringBuilder(0); - } - - appendSubject(msg, tail(subjectFormatter, "")); - - MimeMultipart multipart = new MimeMultipart(); - String altType = getContentType(bodyFormat.getClass().getName()); - setContent(body, buf, altType == null ? contentType : altType); - multipart.addBodyPart(body); - - for (int i = 0; i < parts.length; ++i) { - if (parts[i] != null) { - multipart.addBodyPart(parts[i]); - } - } - - msg.setContent(multipart); - return msg; - } catch (final RuntimeException re) { - reportError(re.getMessage(), re, code); - } catch (final Exception e) { - reportError(e.getMessage(), e, code); - } finally { - isWriting = false; - if (size > 0) { - reset(); - } - } - return null; - } - - /** - * Checks all of the settings if the caller requests a verify and a verify - * was not performed yet and no verify is in progress. A verify is - * performed on create because this handler may be at the end of a handler - * chain and therefore may not see any log records until LogManager.reset() - * is called and at that time all of the settings have been cleared. - * @param session the current session or null. - * @since JavaMail 1.4.4 - */ - private void verifySettings(final Session session) { - if (session != null) { - final Properties props = session.getProperties(); - final Object check = props.put("verify", ""); - if (check instanceof String) { - String value = (String) check; - //Perform the verify if needed. - if (hasValue(value)) { - verifySettings0(session, value); - } - } else { - if (check != null) { //This call will fail. - verifySettings0(session, check.getClass().toString()); - } - } - } - } - - /** - * Checks all of the settings using the given setting. - * This triggers the LogManagerProperties to copy all of the mail - * settings without explictly knowing them. Once all of the properties - * are copied this handler can handle LogManager.reset clearing all of the - * properties. It is expected that this method is, at most, only called - * once per session. - * @param session the current session. - * @since JavaMail 1.4.4 - */ - private void verifySettings0(Session session, String verify) { - assert verify != null : (String) null; - if (!"local".equals(verify) && !"remote".equals(verify) - && !"limited".equals(verify) && !"resolve".equals(verify)) { - reportError("Verify must be 'limited', local', " - + "'resolve' or 'remote'.", - new IllegalArgumentException(verify), - ErrorManager.OPEN_FAILURE); - return; - } - - final MimeMessage abort = new MimeMessage(session); - final String msg; - if (!"limited".equals(verify)) { - msg = "Local address is " - + InternetAddress.getLocalAddress(session) + '.'; - - try { //Verify subclass or declared mime charset. - Charset.forName(getEncodingName()); - } catch (final RuntimeException RE) { - UnsupportedEncodingException UEE = - new UnsupportedEncodingException(RE.toString()); - UEE.initCause(RE); - reportError(msg, UEE, ErrorManager.FORMAT_FAILURE); - } - } else { - msg = "Skipping local address check."; - } - - //Perform all of the copy actions first. - synchronized (this) { //Create the subject. - appendSubject(abort, head(subjectFormatter)); - appendSubject(abort, tail(subjectFormatter, "")); - } - - setIncompleteCopy(abort); //Original body part is never added. - envelopeFor(abort, true); - try { - abort.saveChanges(); - } catch (final MessagingException ME) { - reportError(msg, ME, ErrorManager.FORMAT_FAILURE); - } - - try { - //Ensure transport provider is installed. - Address[] all = abort.getAllRecipients(); - if (all == null) { //Don't pass null to sendMessage. - all = new InternetAddress[0]; - } - Transport t; - try { - final Address[] any = all.length != 0 ? all : abort.getFrom(); - if (any != null && any.length != 0) { - t = session.getTransport(any[0]); - session.getProperty("mail.transport.protocol"); //Force copy - } else { - MessagingException me = new MessagingException( - "No recipient or from address."); - reportError(msg, me, ErrorManager.OPEN_FAILURE); - throw me; - } - } catch (final MessagingException protocol) { - try { - t = session.getTransport(); - } catch (final MessagingException fail) { - throw attach(protocol, fail); - } - } - - String local = null; - if ("remote".equals(verify)) { - MessagingException closed = null; - t.connect(); - try { - try { - //Capture localhost while connection is open. - if (t instanceof SMTPTransport) { - local = ((SMTPTransport) t).getLocalHost(); - } - //A message without content will fail at message writeTo - //when sendMessage is called. This allows the handler - //to capture all mail properties set in the LogManager. - t.sendMessage(abort, all); - } finally { - try { //Close the transport before reporting errors. - t.close(); - } catch (final MessagingException ME) { - closed = ME; - } - } - reportUnexpectedSend(abort, verify, null); - } catch (final SendFailedException sfe) { - Address[] recip = sfe.getInvalidAddresses(); - if (recip != null && recip.length != 0) { - fixUpContent(abort, verify, sfe); - reportError(abort, sfe, ErrorManager.OPEN_FAILURE); - } - - recip = sfe.getValidSentAddresses(); - if (recip != null && recip.length != 0) { - reportUnexpectedSend(abort, verify, sfe); - } - } catch (final MessagingException ME) { - if (!isMissingContent(abort, ME)) { - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } - } - - if (closed != null) { - fixUpContent(abort, verify, closed); - reportError(abort, closed, ErrorManager.CLOSE_FAILURE); - } - } else { - //Force a property copy. - final String protocol = t.getURLName().getProtocol(); - session.getProperty("mail.host"); - session.getProperty("mail.user"); - session.getProperty("mail." + protocol + ".host"); - session.getProperty("mail." + protocol + ".port"); - session.getProperty("mail." + protocol + ".user"); - local = session.getProperty("mail." + protocol + ".localhost"); - if (isEmpty(local)) { - local = session.getProperty("mail." - + protocol + ".localaddress"); - } - - if ("resolve".equals(verify)) { - try { //Resolve the remote host name. - verifyHost(t.getURLName().getHost()); - } catch (final IOException IOE) { - MessagingException ME = - new MessagingException(msg, IOE); - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } catch (final RuntimeException RE) { - MessagingException ME = - new MessagingException(msg, RE); - fixUpContent(abort, verify, RE); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } - } - } - - if (!"limited".equals(verify)) { - try { //Verify host name and hit the host name cache. - if (!"remote".equals(verify) - && t instanceof SMTPTransport) { - local = ((SMTPTransport) t).getLocalHost(); - } - verifyHost(local); - } catch (final IOException IOE) { - MessagingException ME = new MessagingException(msg, IOE); - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } catch (final RuntimeException RE) { - MessagingException ME = new MessagingException(msg, RE); - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } - - - try { //Verify that the DataHandler can be loaded. - final MimeMultipart multipart = new MimeMultipart(); - final MimeBodyPart body = new MimeBodyPart(); - body.setDisposition(Part.INLINE); - body.setDescription(verify); - setAcceptLang(body); - setContent(body, "", "text/plain"); - multipart.addBodyPart(body); - abort.setContent(multipart); - abort.saveChanges(); - abort.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE)); - } catch (final IOException IOE) { - MessagingException ME = new MessagingException(msg, IOE); - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.FORMAT_FAILURE); - } - } - - //Verify all recipients. - if (all.length != 0) { - verifyAddresses(all); - } else { - throw new MessagingException("No recipient addresses."); - } - - //Verify from and sender addresses. - Address[] from = abort.getFrom(); - Address sender = abort.getSender(); - if (sender instanceof InternetAddress) { - ((InternetAddress) sender).validate(); - } - - //If from address is declared then check sender. - if (abort.getHeader("From", ",") != null && from.length != 0) { - verifyAddresses(from); - for (int i = 0; i < from.length; ++i) { - if (from[i].equals(sender)) { - MessagingException ME = new MessagingException( - "Sender address '" + sender - + "' equals from address."); - throw new MessagingException(msg, ME); - } - } - } else { - if (sender == null) { - MessagingException ME = new MessagingException( - "No from or sender address."); - throw new MessagingException(msg, ME); - } - } - - //Verify reply-to addresses. - verifyAddresses(abort.getReplyTo()); - } catch (final MessagingException ME) { - fixUpContent(abort, verify, ME); - reportError(abort, ME, ErrorManager.OPEN_FAILURE); - } catch (final RuntimeException RE) { - fixUpContent(abort, verify, RE); - reportError(abort, RE, ErrorManager.OPEN_FAILURE); - } - } - - /** - * Perform a lookup of the host address or FQDN. - * @param host the host or null. - * @return the address. - * @throws IOException if the host name is not valid. - * @since JavaMail 1.5.0 - */ - private static InetAddress verifyHost(String host) throws IOException { - InetAddress a; - if (isEmpty(host)) { - a = InetAddress.getLocalHost(); - } else { - a = InetAddress.getByName(host); - } - if (a.getCanonicalHostName().length() == 0) { - throw new UnknownHostException(); - } - return a; - } - - /** - * Calls validate for every address given. - * If the addresses given are null, empty or not an InternetAddress then - * the check is skipped. - * @param all any address array, null or empty. - * @throws AddressException if there is a problem. - * @since JavaMail 1.4.5 - */ - private static void verifyAddresses(Address[] all) throws AddressException { - if (all != null) { - for (int i = 0; i < all.length; ++i) { - final Address a = all[i]; - if (a instanceof InternetAddress) { - ((InternetAddress) a).validate(); - } - } - } - } - - /** - * Reports that an empty content message was sent and should not have been. - * @param msg the MimeMessage. - * @param verify the verify enum. - * @param cause the exception that caused the problem or null. - * @since JavaMail 1.4.5 - */ - private void reportUnexpectedSend(MimeMessage msg, String verify, Exception cause) { - final MessagingException write = new MessagingException( - "An empty message was sent.", cause); - fixUpContent(msg, verify, write); - reportError(msg, write, ErrorManager.OPEN_FAILURE); - } - - /** - * Creates and sets the message content from the given Throwable. - * When verify fails, this method fixes the 'abort' message so that any - * created envelope data can be used in the error manager. - * @param msg the message with or without content. - * @param verify the verify enum. - * @param t the throwable or null. - * @since JavaMail 1.4.5 - */ - private void fixUpContent(MimeMessage msg, String verify, Throwable t) { - try { //Add content so toRawString doesn't fail. - final MimeBodyPart body; - final String subjectType; - final String msgDesc; - synchronized (this) { - body = createBodyPart(); - msgDesc = descriptionFrom(comparator, pushLevel, pushFilter); - subjectType = getClassId(subjectFormatter); - } - - body.setDescription("Formatted using " - + (t == null ? Throwable.class.getName() - : t.getClass().getName()) + ", filtered with " - + verify + ", and named by " - + subjectType + '.'); - setContent(body, toMsgString(t), "text/plain"); - final MimeMultipart multipart = new MimeMultipart(); - multipart.addBodyPart(body); - msg.setContent(multipart); - msg.setDescription(msgDesc); - setAcceptLang(msg); - msg.saveChanges(); - } catch (final MessagingException ME) { - reportError("Unable to create body.", ME, ErrorManager.OPEN_FAILURE); - } catch (final RuntimeException RE) { - reportError("Unable to create body.", RE, ErrorManager.OPEN_FAILURE); - } - } - - /** - * Used to update the cached session object based on changes in - * mail properties or authenticator. - * @return the current session or null if no verify is required. - */ - private Session fixUpSession() { - assert Thread.holdsLock(this); - final Session settings; - if (mailProps.getProperty("verify") != null) { - settings = initSession(); - assert settings == session; - } else { - session = null; //Remove old session. - settings = null; - } - return settings; - } - - private Session initSession() { - assert Thread.holdsLock(this); - final String p = getClass().getName(); - LogManagerProperties proxy = new LogManagerProperties(mailProps, p); - session = Session.getInstance(proxy, auth); - return session; - } - - /** - * Creates all of the envelope information for a message. - * This method is safe to call outside of a lock because the message - * provides the safe snapshot of the mail properties. - * @param msg the Message to write the envelope information. - * @param priority true for high priority. - */ - private void envelopeFor(Message msg, boolean priority) { - setAcceptLang(msg); - setFrom(msg); - if (!setRecipient(msg, "mail.to", Message.RecipientType.TO)) { - setDefaultRecipient(msg, Message.RecipientType.TO); - } - setRecipient(msg, "mail.cc", Message.RecipientType.CC); - setRecipient(msg, "mail.bcc", Message.RecipientType.BCC); - setReplyTo(msg); - setSender(msg); - setMailer(msg); - setAutoSubmitted(msg); - if (priority) { - setPriority(msg); - } - - try { - msg.setSentDate(new java.util.Date()); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - private MimeBodyPart createBodyPart() throws MessagingException { - assert Thread.holdsLock(this); - final MimeBodyPart part = new MimeBodyPart(); - part.setDisposition(Part.INLINE); - part.setDescription(descriptionFrom(getFormatter(), - getFilter(), subjectFormatter)); - setAcceptLang(part); - return part; - } - - private MimeBodyPart createBodyPart(int index) throws MessagingException { - assert Thread.holdsLock(this); - final MimeBodyPart part = new MimeBodyPart(); - part.setDisposition(Part.ATTACHMENT); - part.setDescription(descriptionFrom( - attachmentFormatters[index], - attachmentFilters[index], - attachmentNames[index])); - setAcceptLang(part); - return part; - } - - /** - * Gets the description for the MimeMessage itself. - * The push level and filter are included because they play a role in - * formatting of a message when triggered or not triggered. - * @param c the comparator. - * @param l the pushLevel. - * @param f the pushFilter - * @return the description. - * @throws NullPointerException if level is null. - * @since JavaMail 1.4.5 - */ - private String descriptionFrom(Comparator c, Level l, Filter f) { - return "Sorted using "+ (c == null ? "no comparator" - : c.getClass().getName()) + ", pushed when "+ l.getName() - + ", and " + (f == null ? "no push filter" - : f.getClass().getName()) + '.'; - } - - private String descriptionFrom(Formatter f, Filter filter, Formatter name) { - return "Formatted using " + getClassId(f) - + ", filtered with " + (filter == null ? "no filter" - : filter.getClass().getName()) +", and named by " - + getClassId(name) + '.'; - } - - /** - * Gets a class name represents the behavior of the formatter. - * The class name may not be assignable to a Formatter. - * @param f the formatter. - * @return a class name that represents the given formatter. - * @throws NullPointerException if the parameter is null. - * @since JavaMail 1.4.5 - */ - private String getClassId(final Formatter f) { - if (f instanceof TailNameFormatter) { - return String.class.getName(); //Literal string. - } else { - return f.getClass().getName(); - } - } - - /** - * Ensure that a formatter creates a valid string for a part name. - * @param f the formatter. - * @return the to string value or the class name. - */ - private String toString(final Formatter f) { - //Should never be null but, guard against formatter bugs. - final String name = f.toString(); - if (!isEmpty(name)) { - return name; - } else { - return getClassId(f); - } - } - - /** - * Constructs a file name from a formatter. This method is called often - * but, rarely does any work. - * @param part to append to. - * @param chunk non null string to append. - */ - private void appendFileName(final Part part, final String chunk) { - if (chunk != null) { - if (chunk.length() > 0) { - appendFileName0(part, chunk); - } - } else { - reportNullError(ErrorManager.FORMAT_FAILURE); - } - } - - /** - * It is assumed that file names are short and that in most cases - * getTail will be the only method that will produce a result. - * @param part to append to. - * @param chunk non null string to append. - */ - private void appendFileName0(final Part part, final String chunk) { - try { - final String old = part.getFileName(); - part.setFileName(old != null ? old.concat(chunk) : chunk); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Constructs a subject line from a formatter. - * @param msg to append to. - * @param chunk non null string to append. - */ - private void appendSubject(final Message msg, final String chunk) { - if (chunk != null) { - if (chunk.length() > 0) { - appendSubject0(msg, chunk); - } - } else { - reportNullError(ErrorManager.FORMAT_FAILURE); - } - } - - /** - * It is assumed that subject lines are short and that in most cases - * getTail will be the only method that will produce a result. - * @param msg to append to. - * @param chunk non null string to append. - */ - private void appendSubject0(final Message msg, final String chunk) { - try { - final String encoding = getEncodingName(); - final String old = msg.getSubject(); - assert msg instanceof MimeMessage; - ((MimeMessage) msg).setSubject(old != null ? old.concat(chunk) - : chunk, MimeUtility.mimeCharset(encoding)); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Gets the locale for the given log record from the resource bundle. - * If the resource bundle is using the root locale then the default locale - * is returned. - * @param r the log record. - * @return null if not localized otherwise, the locale of the record. - * @since JavaMail 1.4.5 - */ - private Locale localeFor(final LogRecord r) { - Locale l; - final ResourceBundle rb = r.getResourceBundle(); - if (rb != null) { - l = rb.getLocale(); - if (l == null || isEmpty(l.getLanguage())) { - //The language of the fallback bundle (root) is unknown. - //1. Use default locale. Should only be wrong if the app is - // used with a langauge that was unintended. (unlikely) - //2. Mark it as not localized (force null, info loss). - //3. Use the bundle name (encoded) as an experimental language. - l = Locale.getDefault(); - } - } else { - l = null; - } - return l; - } - - /** - * Appends the content language to the given mime part. - * The language tag is only appended if the given language has not been - * specified. This method is only used when we have LogRecords that are - * localized with an assigned resource bundle. - * @param p the mime part. - * @param l the locale to append. - * @throws NullPointerException if any argument is null. - * @since JavaMail 1.4.5 - */ - private void appendContentLang(final MimePart p, final Locale l) { - try { - String lang = LogManagerProperties.toLanguageTag(l); - if (lang.length() != 0) { - String header = p.getHeader("Content-Language", null); - if (isEmpty(header)) { - p.setHeader("Content-Language", lang); - } else if (!header.equalsIgnoreCase(lang)) { - lang = ",".concat(lang); - int idx = 0; - while ((idx = header.indexOf(lang, idx)) > -1) { - idx += lang.length(); - if (idx == header.length() - || header.charAt(idx) == ',') { - break; - } - } - - if (idx < 0) { - int len = header.lastIndexOf("\r\n\t"); - if (len < 0) { //If not folded. - len = (18 + 2) + header.length(); - } else { - len = (header.length() - len) + 8; - } - - //Perform folding of header if needed. - if ((len + lang.length()) > 76) { - header = header.concat("\r\n\t".concat(lang)); - } else { - header = header.concat(lang); - } - p.setHeader("Content-Language", header); - } - } - } - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Sets the accept language to the default locale of the JVM. - * If the locale is the root locale the header is not added. - * @param p the part to set. - * @since JavaMail 1.4.5 - */ - private void setAcceptLang(final Part p) { - try { - final String lang = LogManagerProperties - .toLanguageTag(Locale.getDefault()); - if (lang.length() != 0) { - p.setHeader("Accept-Language", lang); - } - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Used when a log record was loggable prior to being inserted - * into the buffer but at the time of formatting was no longer loggable. - * Filters were changed after publish but prior to a push or a bug in the - * body filter or one of the attachment filters. - * @param record that was not formatted. - * @since JavaMail 1.4.5 - */ - private void reportFilterError(final LogRecord record) { - assert Thread.holdsLock(this); - final SimpleFormatter f = new SimpleFormatter(); - final String msg = "Log record " + record.getSequenceNumber() - + " was filtered from all message parts. " - + head(f) + format(f, record) + tail(f, ""); - final String txt = getFilter() + ", " - + Arrays.asList(readOnlyAttachmentFilters()); - reportError(msg, new IllegalArgumentException(txt), - ErrorManager.FORMAT_FAILURE); - } - - /** - * Reports symmetric contract violations an equals implementation. - * @param o the test object must be non null. - * @param found the possible intern, must be non null. - * @throws NullPointerException if any argument is null. - * @since JavaMail 1.5.0 - */ - private void reportNonSymmetric(final Object o, final Object found) { - reportError("Non symmetric equals implementation." - , new IllegalArgumentException(o.getClass().getName() - + " is not equal to " + found.getClass().getName()) - , ErrorManager.OPEN_FAILURE); - } - - /** - * Reports equals implementations that do not discriminate between objects - * of different types or subclass types. - * @param o the test object must be non null. - * @param found the possible intern, must be non null. - * @throws NullPointerException if any argument is null. - * @since JavaMail 1.5.0 - */ - private void reportNonDiscriminating(final Object o, final Object found) { - reportError("Non discriminating equals implementation." - , new IllegalArgumentException(o.getClass().getName() - + " should not be equal to " + found.getClass().getName()) - , ErrorManager.OPEN_FAILURE); - } - - private void reportNullError(final int code) { - reportError("null", new NullPointerException(), code); - } - - private String head(final Formatter f) { - try { - return f.getHead(this); - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); - return ""; - } - } - - private String format(final Formatter f, final LogRecord r) { - try { - return f.format(r); - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); - return ""; - } - } - - private String tail(final Formatter f, final String def) { - try { - return f.getTail(this); - } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); - return def; - } - } - - private void setMailer(final Message msg) { - try { - final Class mail = MailHandler.class; - final Class k = getClass(); - String value; - if (k == mail) { - value = mail.getName(); - } else { - try { - value = MimeUtility.encodeText(k.getName()); - } catch (final UnsupportedEncodingException E) { - reportError(E.getMessage(), E, ErrorManager.FORMAT_FAILURE); - value = k.getName().replaceAll("[^\\x00-\\x7F]", "\uu001A"); - } - value = MimeUtility.fold(10, mail.getName() + " using the " - + value + " extension."); - } - msg.setHeader("X-Mailer", value); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - private void setPriority(final Message msg) { - try { - msg.setHeader("Importance", "High"); - msg.setHeader("Priority", "urgent"); - msg.setHeader("X-Priority", "2"); //High - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Used to signal that body parts are missing from a message. Also used - * when LogRecords were passed to an attachment formatter but the formatter - * produced no output, which is allowed. Used during a verify because all - * parts are omitted, none of the content formatters are used. This is - * not used when a filter prevents LogRecords from being formatted. - * This header is defined in RFC 2156 and RFC 4021. - * @param msg the message. - * @since JavaMail 1.4.5 - */ - private void setIncompleteCopy(final Message msg) { - try { - msg.setHeader("Incomplete-Copy", ""); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Signals that this message was generated by automatic process. - * This header is defined in RFC 3834 section 5. - * @param msg the message. - * @since JavaMail 1.4.6 - */ - private void setAutoSubmitted(final Message msg) { - try { - msg.setHeader("auto-submitted", "auto-generated"); //RFC 3834 (5.2) - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - private void setFrom(final Message msg) { - final String from = msg.getSession().getProperty("mail.from"); - if (from != null) { - try { - final Address[] address = InternetAddress.parse(from, false); - if (address.length > 0) { - if (address.length == 1) { - msg.setFrom(address[0]); - } else { //Greater than 1 address. - msg.addFrom(address); - } - } - //Can't place an else statement here because the 'from' is - //not null which causes the local address computation - //to fail. Assume the user wants to omit the from address - //header. - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - setDefaultFrom(msg); - } - } else { - setDefaultFrom(msg); - } - } - - private void setDefaultFrom(final Message msg) { - try { - msg.setFrom(); - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - - /** - * Computes the default to-address if none was specified. This can - * fail if the local address can't be computed. - * @param msg the message - * @param type the recipient type. - * @since JavaMail 1.5.0 - */ - private void setDefaultRecipient(final Message msg, - final Message.RecipientType type) { - try { - Address a = InternetAddress.getLocalAddress(msg.getSession()); - if (a != null) { - msg.setRecipient(type, a); - } else { - final MimeMessage m = new MimeMessage(msg.getSession()); - m.setFrom(); //Should throw an exception with a cause. - Address[] from = m.getFrom(); - if (from.length > 0) { - msg.setRecipients(type, from); - } else { - throw new MessagingException("No local address."); - } - } - } catch (final MessagingException ME) { - reportError("Unable to compute a default recipient.", - ME, ErrorManager.FORMAT_FAILURE); - } catch (final RuntimeException RE) { - reportError("Unable to compute a default recipient.", - RE, ErrorManager.FORMAT_FAILURE); - } - } - - private void setReplyTo(final Message msg) { - final String reply = msg.getSession().getProperty("mail.reply.to"); - if (!isEmpty(reply)) { - try { - final Address[] address = InternetAddress.parse(reply, false); - if (address.length > 0) { - msg.setReplyTo(address); - } - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - } - - private void setSender(final Message msg) { - assert msg instanceof MimeMessage : msg; - final String sender = msg.getSession().getProperty("mail.sender"); - if (!isEmpty(sender)) { - try { - final InternetAddress[] address = - InternetAddress.parse(sender, false); - if (address.length > 0) { - ((MimeMessage) msg).setSender(address[0]); - if (address.length > 1) { - reportError("Ignoring other senders.", - tooManyAddresses(address, 1), - ErrorManager.FORMAT_FAILURE); - } - } - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - } - - private AddressException tooManyAddresses(Address[] address, int offset) { - Object l = Arrays.asList(address).subList(offset, address.length); - return new AddressException(l.toString()); - } - - /** - * Sets the recipient for the given message. - * @param msg the message. - * @param key the key to search in the session. - * @param type the recipient type. - * @return true if the key was contained in the session. - */ - private boolean setRecipient(final Message msg, - final String key, final Message.RecipientType type) { - boolean containsKey; - final String value = msg.getSession().getProperty(key); - containsKey = value != null; - if (!isEmpty(value)) { - try { - final Address[] address = InternetAddress.parse(value, false); - if (address.length > 0) { - msg.setRecipients(type, address); - } - } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); - } - } - return containsKey; - } - - /** - * Converts an email message to a raw string. This raw string - * is passed to the error manager to allow custom error managers - * to recreate the original MimeMessage object. - * @param msg a Message object. - * @return the raw string or null if msg was null. - * @throws MessagingException if there was a problem with the message. - * @throws IOException if there was a problem. - */ - private String toRawString(final Message msg) throws MessagingException, IOException { - if (msg != null) { - final int nbytes = Math.max(msg.getSize() + MIN_HEADER_SIZE, MIN_HEADER_SIZE); - final ByteArrayOutputStream out = new ByteArrayOutputStream(nbytes); - msg.writeTo(out); - return out.toString("US-ASCII"); //Raw message is always ASCII. - } else { //Must match this.reportError behavior, see push method. - return null; //Null is the safe choice. - } - } - - /** - * Converts a throwable to a message string. - * @param t any throwable or null. - * @return the throwable with a stack trace or the literal null. - */ - private String toMsgString(final Throwable t) { - if (t == null) { - return "null"; - } - - final String encoding = getEncodingName(); - try { - final ByteArrayOutputStream out = - new ByteArrayOutputStream(MIN_HEADER_SIZE); - - //Create an output stream writer so streams are not double buffered. - final PrintWriter pw = - new PrintWriter(new OutputStreamWriter(out, encoding)); - pw.println(t.getMessage()); - t.printStackTrace(pw); - pw.flush(); - pw.close(); //BUG ID 6995537 - return out.toString(encoding); - } catch (IOException badMimeCharset) { - return t.toString() + ' ' + badMimeCharset.toString(); - } - } - - /** - * Replaces the current context class loader with our class loader. - * @return null for the boot class loader, a class loader, or a marker - * object to signal that no modification was required. - * @since JavaMail 1.4.6 - */ - private Object getAndSetContextClassLoader() { - try { - return AccessController.doPrivileged(GET_AND_SET_CCL); - } catch (final SecurityException ignore) { - return GET_AND_SET_CCL; //return not modified. - } - } - - /** - * Restores the original context class loader. - * @param ccl null for the boot class loader, a class loader, or a - * marker object to signal that no modification is required. - * @since JavaMail 1.4.6 - */ - private void setContextClassLoader(final Object ccl) { - //Boot class loader or a new context class loader. - if (ccl == null || ccl instanceof ClassLoader) { - AccessController.doPrivileged(new GetAndSetContext(ccl)); - } - } - - private static RuntimeException attachmentMismatch(final String msg) { - return new IndexOutOfBoundsException(msg); - } - - private static RuntimeException attachmentMismatch(int expected, int found) { - return attachmentMismatch("Attachments mismatched, expected " - + expected + " but given " + found + '.'); - } - - private static MessagingException attach( - MessagingException required, Exception optional) { - if (optional != null && !required.setNextException(optional)) { - if (optional instanceof MessagingException) { - final MessagingException head = (MessagingException) optional; - if (head.setNextException(required)) { - return head; - } - } - } - return required; - } - - private static String atIndexMsg(final int i) { - return "At index: " + i + '.'; - } - - /** - * Used for storing a password from the LogManager or literal string. - * @since JavaMail 1.4.6 - */ - private static final class DefaultAuthenticator extends Authenticator { - - private final String pass; - - DefaultAuthenticator(final String pass) { - assert pass != null; - this.pass = pass; - } - - @Override - protected final PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(getDefaultUserName(), pass); - } - } - - /** - * Performs a get and set of the context class loader with privileges - * enabled. - * @since JavaMail 1.4.6 - */ - private static final class GetAndSetContext implements PrivilegedAction { - /** - * The source containing the class loader. - */ - private final Object source; - /** - * Create the action. - * @param source null for boot class loader, a class loader, a class - * used to get the class loader, or a source object to get the class - * loader. - */ - GetAndSetContext(final Object source) { - this.source = source; - } - - /** - * Gets the class loader from the source and sets the CCL only if - * the source and CCL are not the same. - * @return the replaced context class loader which can be null or this - * to indicate that nothing was modified. - */ - public final Object run() { - final Thread current = Thread.currentThread(); - final ClassLoader ccl = current.getContextClassLoader(); - final ClassLoader loader; - if (source == null) { - loader = null; //boot class loader - } else if (source instanceof ClassLoader) { - loader = (ClassLoader) source; - } else if (source instanceof Class) { - loader = ((Class) source).getClassLoader(); - } else { - loader = source.getClass().getClassLoader(); - } - - if (ccl != loader) { - current.setContextClassLoader(loader); - return ccl; - } else { - return this; //Unchanged, return non null and non classloader. - } - } - } - - /** - * Used for naming attachment file names and the main subject line. - */ - private static final class TailNameFormatter extends Formatter { - - private final String name; - - /** - * Creates the formatter with the given name. - * Default access to avoid extra generated class files. - * @param name any not null string. - */ - TailNameFormatter(final String name) { - assert name != null; - this.name = name; - } - - public final String format(LogRecord record) { - return ""; - } - - @Override - public final String getTail(Handler h) { - return name; - } - - /** - * Equals method. - * @param o the other object. - * @return true if equal - * @since JavaMail 1.4.4 - */ - @Override - public final boolean equals(Object o) { - if (o instanceof TailNameFormatter) { - return name.equals(((TailNameFormatter) o).name); - } - return false; - } - - /** - * Hash code method. - * @return the hash code. - * @since JavaMail 1.4.4 - */ - @Override - public final int hashCode() { - return getClass().hashCode() + name.hashCode(); - } - - @Override - public final String toString() { - return name; - } - } -} diff --git a/src/main/java/com/sun/mail/util/logging/package.html b/src/main/java/com/sun/mail/util/logging/package.html deleted file mode 100644 index cfdac1a7..00000000 --- a/src/main/java/com/sun/mail/util/logging/package.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - Contains JavaMailTM extensions for - the JavaTM platform's core logging - facilities. This package contains classes used to export log messages - as a formatted email message. Classes in this package typically use - LogManager properties to set default values; see the specific - documentation for each concrete class. - - diff --git a/src/main/java/com/sun/mail/util/package.html b/src/main/java/com/sun/mail/util/package.html deleted file mode 100644 index 86637453..00000000 --- a/src/main/java/com/sun/mail/util/package.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - -Utility classes for use with the JavaMail API. -These utility classes are not part of the JavaMail specification. -While this package contains many classes used by the JavaMail implementation -and not intended for direct use by applications, the classes documented -here may be of use to applications. -

      -Classes in this package log debugging information using -{@link java.util.logging} as described in the following table: -

      - - - - - - - - - - - - -
      Logger NameLogging LevelPurpose
      com.sun.mail.util.socketFINERDebugging output related to creating sockets
      - -

      -WARNING: The APIs in this package should be -considered EXPERIMENTAL. They may be changed in the -future in ways that are incompatible with applications using the -current APIs. - - - diff --git a/src/main/java/javax/mail/Address.java b/src/main/java/javax/mail/Address.java deleted file mode 100644 index ae449e30..00000000 --- a/src/main/java/javax/mail/Address.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.Serializable; - -/** - * This abstract class models the addresses in a message. - * Subclasses provide specific implementations. Subclasses - * will typically be serializable so that (for example) the - * use of Address objects in search terms can be serialized - * along with the search terms. - * - * @author John Mani - * @author Bill Shannon - */ - -public abstract class Address implements Serializable { - - private static final long serialVersionUID = -5822459626751992278L; - - /** - * Return a type string that identifies this address type. - * - * @return address type - * @see javax.mail.internet.InternetAddress - */ - public abstract String getType(); - - /** - * Return a String representation of this address object. - * - * @return string representation of this address - */ - public abstract String toString(); - - /** - * The equality operator. Subclasses should provide an - * implementation of this method that supports value equality - * (do the two Address objects represent the same destination?), - * not object reference equality. A subclass must also provide - * a corresponding implementation of the hashCode - * method that preserves the general contract of - * equals and hashCode - objects that - * compare as equal must have the same hashCode. - * - * @param address Address object - */ - public abstract boolean equals(Object address); -} diff --git a/src/main/java/javax/mail/AuthenticationFailedException.java b/src/main/java/javax/mail/AuthenticationFailedException.java deleted file mode 100644 index 1477fa88..00000000 --- a/src/main/java/javax/mail/AuthenticationFailedException.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when the connect method on a Store or - * Transport object fails due to an authentication failure (e.g., - * bad user name or password). - * - * @author Bill Shannon - */ - -public class AuthenticationFailedException extends MessagingException { - - private static final long serialVersionUID = 492080754054436511L; - - /** - * Constructs an AuthenticationFailedException. - */ - public AuthenticationFailedException() { - super(); - } - - /** - * Constructs an AuthenticationFailedException with the specified - * detail message. - * - * @param message The detailed error message - */ - public AuthenticationFailedException(String message) { - super(message); - } - - /** - * Constructs an AuthenticationFailedException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param message The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public AuthenticationFailedException(String message, Exception e) { - super(message, e); - } -} diff --git a/src/main/java/javax/mail/Authenticator.java b/src/main/java/javax/mail/Authenticator.java deleted file mode 100644 index e57b4830..00000000 --- a/src/main/java/javax/mail/Authenticator.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.net.InetAddress; - -/** - * The class Authenticator represents an object that knows how to obtain - * authentication for a network connection. Usually, it will do this - * by prompting the user for information. - *

      - * Applications use this class by creating a subclass, and registering - * an instance of that subclass with the session when it is created. - * When authentication is required, the system will invoke a method - * on the subclass (like getPasswordAuthentication). The subclass's - * method can query about the authentication being requested with a - * number of inherited methods (getRequestingXXX()), and form an - * appropriate message for the user. - *

      - * All methods that request authentication have a default implementation - * that fails. - * - * @see java.net.Authenticator - * @see javax.mail.Session#getInstance(java.util.Properties, - * javax.mail.Authenticator) - * @see javax.mail.Session#getDefaultInstance(java.util.Properties, - * javax.mail.Authenticator) - * @see javax.mail.Session#requestPasswordAuthentication - * @see javax.mail.PasswordAuthentication - * - * @author Bill Foote - * @author Bill Shannon - */ - -// There are no abstract methods, but to be useful the user must -// subclass. -public abstract class Authenticator { - - private InetAddress requestingSite; - private int requestingPort; - private String requestingProtocol; - private String requestingPrompt; - private String requestingUserName; - - private void reset() { - requestingSite = null; - requestingPort = -1; - requestingProtocol = null; - requestingPrompt = null; - requestingUserName = null; - } - - /** - * Ask the authenticator for a password. - *

      - * - * @param addr The InetAddress of the site requesting authorization, - * or null if not known. - * @param port the port for the requested connection - * @param protocol The protocol that's requesting the connection - * (@see java.net.Authenticator.getProtocol()) - * @param prompt A prompt string for the user - * - * @return The username/password, or null if one can't be gotten. - */ - final PasswordAuthentication requestPasswordAuthentication( - InetAddress addr, int port, String protocol, - String prompt, String defaultUserName) { - - reset(); - requestingSite = addr; - requestingPort = port; - requestingProtocol = protocol; - requestingPrompt = prompt; - requestingUserName = defaultUserName; - return getPasswordAuthentication(); - } - - /** - * @return the InetAddress of the site requesting authorization, or null - * if it's not available. - */ - protected final InetAddress getRequestingSite() { - return requestingSite; - } - - /** - * @return the port for the requested connection - */ - protected final int getRequestingPort() { - return requestingPort; - } - - /** - * Give the protocol that's requesting the connection. Often this - * will be based on a URLName. - * - * @return the protcol - * - * @see javax.mail.URLName#getProtocol - */ - protected final String getRequestingProtocol() { - return requestingProtocol; - } - - /** - * @return the prompt string given by the requestor - */ - protected final String getRequestingPrompt() { - return requestingPrompt; - } - - /** - * @return the default user name given by the requestor - */ - protected final String getDefaultUserName() { - return requestingUserName; - } - - /** - * Called when password authentication is needed. Subclasses should - * override the default implementation, which returns null.

      - * - * Note that if this method uses a dialog to prompt the user for this - * information, the dialog needs to block until the user supplies the - * information. This method can not simply return after showing the - * dialog. - * @return The PasswordAuthentication collected from the - * user, or null if none is provided. - */ - protected PasswordAuthentication getPasswordAuthentication() { - return null; - } -} diff --git a/src/main/java/javax/mail/BodyPart.java b/src/main/java/javax/mail/BodyPart.java deleted file mode 100644 index f95e3b08..00000000 --- a/src/main/java/javax/mail/BodyPart.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This class models a Part that is contained within a Multipart. - * This is an abstract class. Subclasses provide actual implementations.

      - * - * BodyPart implements the Part interface. Thus, it contains a set of - * attributes and a "content". - * - * @author John Mani - * @author Bill Shannon - */ - -public abstract class BodyPart implements Part { - - /** - * The Multipart object containing this BodyPart, - * if known. - * @since JavaMail 1.1 - */ - protected Multipart parent; - - /** - * Return the containing Multipart object, - * or null if not known. - */ - public Multipart getParent() { - return parent; - } - - /** - * Set the parent of this BodyPart to be the specified - * Multipart. Normally called by Multipart's - * addBodyPart method. parent may be - * null if the BodyPart is being removed - * from its containing Multipart. - * @since JavaMail 1.1 - */ - void setParent(Multipart parent) { - this.parent = parent; - } -} diff --git a/src/main/java/javax/mail/EncodingAware.java b/src/main/java/javax/mail/EncodingAware.java deleted file mode 100644 index 8deca190..00000000 --- a/src/main/java/javax/mail/EncodingAware.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * A {@link javax.activation.DataSource DataSource} that also implements - * EncodingAware may specify the Content-Transfer-Encoding - * to use for its data. Valid Content-Transfer-Encoding values specified - * by RFC 2045 are "7bit", "8bit", "quoted-printable", "base64", and "binary". - *

      - * For example, a {@link javax.activation.FileDataSource FileDataSource} - * could be created that forces all files to be base64 encoded:

      - *

      - *  public class Base64FileDataSource extends FileDataSource
      - *					implements EncodingAware {
      - *	public Base64FileDataSource(File file) {
      - *	    super(file);
      - *	}
      - *
      - *	// implements EncodingAware.getEncoding()
      - *	public String getEncoding() {
      - *	    return "base64";
      - *	}
      - *  }
      - * 

      - * - * @since JavaMail 1.5 - * @author Bill Shannon - */ - -public interface EncodingAware { - - /** - * Return the MIME Content-Transfer-Encoding to use for this data, - * or null to indicate that an appropriate value should be chosen - * by the caller. - * - * @return the Content-Transfer-Encoding value, or null - */ - public String getEncoding(); -} diff --git a/src/main/java/javax/mail/EventQueue.java b/src/main/java/javax/mail/EventQueue.java deleted file mode 100644 index 9d8643c6..00000000 --- a/src/main/java/javax/mail/EventQueue.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.*; -import java.util.Vector; -import javax.mail.event.MailEvent; - -/** - * Package private class used by Store & Folder to dispatch events. - * This class implements an event queue, and a dispatcher thread that - * dequeues and dispatches events from the queue. - * - * Pieces stolen from sun.misc.Queue. - * - * @author Bill Shannon - */ -class EventQueue implements Runnable { - - static class QueueElement { - QueueElement next = null; - QueueElement prev = null; - MailEvent event = null; - Vector vector = null; - - QueueElement(MailEvent event, Vector vector) { - this.event = event; - this.vector = vector; - } - } - - private QueueElement head = null; - private QueueElement tail = null; - private Thread qThread; - - public EventQueue() { - qThread = new Thread(this, "JavaMail-EventQueue"); - qThread.setDaemon(true); // not a user thread - qThread.start(); - } - - /** - * Enqueue an event. - */ - public synchronized void enqueue(MailEvent event, Vector vector) { - QueueElement newElt = new QueueElement(event, vector); - - if (head == null) { - head = newElt; - tail = newElt; - } else { - newElt.next = head; - head.prev = newElt; - head = newElt; - } - notifyAll(); - } - - /** - * Dequeue the oldest object on the queue. - * Used only by the run() method. - * - * @return the oldest object on the queue. - * @exception java.lang.InterruptedException if another thread has - * interrupted this thread. - */ - private synchronized QueueElement dequeue() - throws InterruptedException { - while (tail == null) - wait(); - QueueElement elt = tail; - tail = elt.prev; - if (tail == null) { - head = null; - } else { - tail.next = null; - } - elt.prev = elt.next = null; - return elt; - } - - /** - * Pull events off the queue and dispatch them. - */ - public void run() { - QueueElement qe; - - try { - loop: - for (;;) { - qe = dequeue(); // blocks until an item is available - MailEvent e = qe.event; - Vector v = qe.vector; - - for (int i = 0; i < v.size(); i++) - try { - e.dispatch(v.elementAt(i)); - } catch (Throwable t) { - if (t instanceof InterruptedException) - break loop; - // ignore anything else thrown by the listener - } - - qe = null; e = null; v = null; - } - } catch (InterruptedException e) { - // just die - } - } - - /** - * Stop the dispatcher so we can be destroyed. - */ - void stop() { - if (qThread != null) { - qThread.interrupt(); // kill our thread - qThread = null; - } - } -} diff --git a/src/main/java/javax/mail/FetchProfile.java b/src/main/java/javax/mail/FetchProfile.java deleted file mode 100644 index e254af81..00000000 --- a/src/main/java/javax/mail/FetchProfile.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.Vector; - -/** - * Clients use a FetchProfile to list the Message attributes that - * it wishes to prefetch from the server for a range of messages.

      - * - * Messages obtained from a Folder are light-weight objects that - * typically start off as empty references to the actual messages. - * Such a Message object is filled in "on-demand" when the appropriate - * get*() methods are invoked on that particular Message. Certain - * server-based message access protocols (Ex: IMAP) allow batch - * fetching of message attributes for a range of messages in a single - * request. Clients that want to use message attributes for a range of - * Messages (Example: to display the top-level headers in a headerlist) - * might want to use the optimization provided by such servers. The - * FetchProfile allows the client to indicate this desire - * to the server.

      - * - * Note that implementations are not obligated to support - * FetchProfiles, since there might be cases where the backend service - * does not allow easy, efficient fetching of such profiles.

      - * - * Sample code that illustrates the use of a FetchProfile is given - * below:

      - *

      - *
      - *
      - *  Message[] msgs = folder.getMessages();
      - *
      - *  FetchProfile fp = new FetchProfile();
      - *  fp.add(FetchProfile.Item.ENVELOPE);
      - *  fp.add("X-mailer");
      - *  folder.fetch(msgs, fp);
      - *
      - * 

      - * - * @see javax.mail.Folder#fetch - * @author John Mani - * @author Bill Shannon - */ - -public class FetchProfile { - - private Vector specials; // specials - private Vector headers; // vector of header names - - /** - * This inner class is the base class of all items that - * can be requested in a FetchProfile. The items currently - * defined here are ENVELOPE, CONTENT_INFO - * and FLAGS. The UIDFolder interface - * defines the UID Item as well.

      - * - * Note that this class only has a protected constructor, therby - * restricting new Item types to either this class or subclasses. - * This effectively implements a enumeration of allowed Item types. - * - * @see UIDFolder - */ - - public static class Item { - /** - * This is the Envelope item.

      - * - * The Envelope is an aggregration of the common attributes - * of a Message. Implementations should include the following - * attributes: From, To, Cc, Bcc, ReplyTo, Subject and Date. - * More items may be included as well.

      - * - * For implementations of the IMAP4 protocol (RFC 2060), the - * Envelope should include the ENVELOPE data item. More items - * may be included too. - */ - public static final Item ENVELOPE = new Item("ENVELOPE"); - - /** - * This item is for fetching information about the - * content of the message.

      - * - * This includes all the attributes that describe the content - * of the message. Implementations should include the following - * attributes: ContentType, ContentDisposition, - * ContentDescription, Size and LineCount. Other items may be - * included as well. - */ - public static final Item CONTENT_INFO = new Item("CONTENT_INFO"); - - /** - * SIZE is a fetch profile item that can be included in a - * FetchProfile during a fetch request to a Folder. - * This item indicates that the sizes of the messages in the specified - * range should be prefetched.

      - * - * @since JavaMail 1.5 - */ - public static final Item SIZE = new Item("SIZE"); - - /** - * This is the Flags item. - */ - public static final Item FLAGS = new Item("FLAGS"); - - private String name; - - /** - * Constructor for an item. The name is used only for debugging. - */ - protected Item(String name) { - this.name = name; - } - - /** - * Include the name in the toString return value for debugging. - */ - public String toString() { - return getClass().getName() + "[" + name + "]"; - } - } - - /** - * Create an empty FetchProfile. - */ - public FetchProfile() { - specials = null; - headers = null; - } - - /** - * Add the given special item as one of the attributes to - * be prefetched. - * - * @param item the special item to be fetched - * @see FetchProfile.Item#ENVELOPE - * @see FetchProfile.Item#CONTENT_INFO - * @see FetchProfile.Item#FLAGS - */ - public void add(Item item) { - if (specials == null) - specials = new Vector(); - specials.addElement(item); - } - - /** - * Add the specified header-field to the list of attributes - * to be prefetched. - * - * @param headerName header to be prefetched - */ - public void add(String headerName) { - if (headers == null) - headers = new Vector(); - headers.addElement(headerName); - } - - /** - * Returns true if the fetch profile contains given special item. - */ - public boolean contains(Item item) { - return specials != null && specials.contains(item); - } - - /** - * Returns true if the fetch profile contains given header name. - */ - public boolean contains(String headerName) { - return headers != null && headers.contains(headerName); - } - - /** - * Get the items set in this profile. - * - * @return items set in this profile - */ - public Item[] getItems() { - if (specials == null) - return new Item[0]; - - Item[] s = new Item[specials.size()]; - specials.copyInto(s); - return s; - } - - /** - * Get the names of the header-fields set in this profile. - * - * @return headers set in this profile - */ - public String[] getHeaderNames() { - if (headers == null) - return new String[0]; - - String[] s = new String[headers.size()]; - headers.copyInto(s); - return s; - } -} diff --git a/src/main/java/javax/mail/Flags.java b/src/main/java/javax/mail/Flags.java deleted file mode 100644 index 7bfa63ef..00000000 --- a/src/main/java/javax/mail/Flags.java +++ /dev/null @@ -1,589 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.Serializable; -import java.util.*; - -/** - * The Flags class represents the set of flags on a Message. Flags - * are composed of predefined system flags, and user defined flags.

      - * - * A System flag is represented by the Flags.Flag - * inner class. A User defined flag is represented as a String. - * User flags are case-independent.

      - * - * A set of standard system flags are predefined. Most folder - * implementations are expected to support these flags. Some - * implementations may also support arbitrary user-defined flags. The - * getPermanentFlags method on a Folder returns a Flags - * object that holds all the flags that are supported by that folder - * implementation.

      - * - * A Flags object is serializable so that (for example) the - * use of Flags objects in search terms can be serialized - * along with the search terms.

      - * - * Warning: - * Serialized objects of this class may not be compatible with future - * JavaMail API releases. The current serialization support is - * appropriate for short term storage.

      - * - * The below code sample illustrates how to set, examine, and get the - * flags for a message.

      - *

      - *
      - * Message m = folder.getMessage(1);
      - * m.setFlag(Flags.Flag.DELETED, true); // set the DELETED flag
      - *
      - * // Check if DELETED flag is set on this message
      - * if (m.isSet(Flags.Flag.DELETED))
      - *	System.out.println("DELETED message");
      - *
      - * // Examine ALL system flags for this message
      - * Flags flags = m.getFlags();
      - * Flags.Flag[] sf = flags.getSystemFlags();
      - * for (int i = 0; i < sf.length; i++) {
      - *	if (sf[i] == Flags.Flag.DELETED)
      - *            System.out.println("DELETED message");
      - *	else if (sf[i] == Flags.Flag.SEEN)
      - *            System.out.println("SEEN message");
      - *      ......
      - *      ......
      - * }
      - * 
      - *

      - * - * @see Folder#getPermanentFlags - * @author John Mani - * @author Bill Shannon - */ - -public class Flags implements Cloneable, Serializable { - - private int system_flags = 0; - private Hashtable user_flags = null; - - private final static int ANSWERED_BIT = 0x01; - private final static int DELETED_BIT = 0x02; - private final static int DRAFT_BIT = 0x04; - private final static int FLAGGED_BIT = 0x08; - private final static int RECENT_BIT = 0x10; - private final static int SEEN_BIT = 0x20; - private final static int USER_BIT = 0x80000000; - - private static final long serialVersionUID = 6243590407214169028L; - - /** - * This inner class represents an individual system flag. A set - * of standard system flag objects are predefined here. - */ - public static final class Flag { - /** - * This message has been answered. This flag is set by clients - * to indicate that this message has been answered to. - */ - public static final Flag ANSWERED = new Flag(ANSWERED_BIT); - - /** - * This message is marked deleted. Clients set this flag to - * mark a message as deleted. The expunge operation on a folder - * removes all messages in that folder that are marked for deletion. - */ - public static final Flag DELETED = new Flag(DELETED_BIT); - - /** - * This message is a draft. This flag is set by clients - * to indicate that the message is a draft message. - */ - public static final Flag DRAFT = new Flag(DRAFT_BIT); - - /** - * This message is flagged. No semantic is defined for this flag. - * Clients alter this flag. - */ - public static final Flag FLAGGED = new Flag(FLAGGED_BIT); - - /** - * This message is recent. Folder implementations set this flag - * to indicate that this message is new to this folder, that is, - * it has arrived since the last time this folder was opened.

      - * - * Clients cannot alter this flag. - */ - public static final Flag RECENT = new Flag(RECENT_BIT); - - /** - * This message is seen. This flag is implicitly set by the - * implementation when the this Message's content is returned - * to the client in some form. The getInputStream - * and getContent methods on Message cause this - * flag to be set.

      - * - * Clients can alter this flag. - */ - public static final Flag SEEN = new Flag(SEEN_BIT); - - /** - * A special flag that indicates that this folder supports - * user defined flags.

      - * - * The implementation sets this flag. Clients cannot alter - * this flag but can use it to determine if a folder supports - * user defined flags by using - * folder.getPermanentFlags().contains(Flags.Flag.USER). - */ - public static final Flag USER = new Flag(USER_BIT); - - // flags are stored as bits for efficiency - private int bit; - private Flag(int bit) { - this.bit = bit; - } - } - - - /** - * Construct an empty Flags object. - */ - public Flags() { } - - /** - * Construct a Flags object initialized with the given flags. - * - * @param flags the flags for initialization - */ - public Flags(Flags flags) { - this.system_flags = flags.system_flags; - if (flags.user_flags != null) - this.user_flags = (Hashtable)flags.user_flags.clone(); - } - - /** - * Construct a Flags object initialized with the given system flag. - * - * @param flag the flag for initialization - */ - public Flags(Flag flag) { - this.system_flags |= flag.bit; - } - - /** - * Construct a Flags object initialized with the given user flag. - * - * @param flag the flag for initialization - */ - public Flags(String flag) { - user_flags = new Hashtable(1); - user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag); - } - - /** - * Add the specified system flag to this Flags object. - * - * @param flag the flag to add - */ - public void add(Flag flag) { - system_flags |= flag.bit; - } - - /** - * Add the specified user flag to this Flags object. - * - * @param flag the flag to add - */ - public void add(String flag) { - if (user_flags == null) - user_flags = new Hashtable(1); - user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag); - } - - /** - * Add all the flags in the given Flags object to this - * Flags object. - * - * @param f Flags object - */ - public void add(Flags f) { - system_flags |= f.system_flags; // add system flags - - if (f.user_flags != null) { // add user-defined flags - if (user_flags == null) - user_flags = new Hashtable(1); - - Enumeration e = f.user_flags.keys(); - - while (e.hasMoreElements()) { - String s = (String)e.nextElement(); - user_flags.put(s, f.user_flags.get(s)); - } - } - } - - /** - * Remove the specified system flag from this Flags object. - * - * @param flag the flag to be removed - */ - public void remove(Flag flag) { - system_flags &= ~flag.bit; - } - - /** - * Remove the specified user flag from this Flags object. - * - * @param flag the flag to be removed - */ - public void remove(String flag) { - if (user_flags != null) - user_flags.remove(flag.toLowerCase(Locale.ENGLISH)); - } - - /** - * Remove all flags in the given Flags object from this - * Flags object. - * - * @param f the flag to be removed - */ - public void remove(Flags f) { - system_flags &= ~f.system_flags; // remove system flags - - if (f.user_flags != null) { - if (user_flags == null) - return; - - Enumeration e = f.user_flags.keys(); - while (e.hasMoreElements()) - user_flags.remove(e.nextElement()); - } - } - - /** - * Check whether the specified system flag is present in this Flags object. - * - * @return true of the given flag is present, otherwise false. - */ - public boolean contains(Flag flag) { - return (system_flags & flag.bit) != 0; - } - - /** - * Check whether the specified user flag is present in this Flags object. - * - * @return true of the given flag is present, otherwise false. - */ - public boolean contains(String flag) { - if (user_flags == null) - return false; - else - return user_flags.containsKey(flag.toLowerCase(Locale.ENGLISH)); - } - - /** - * Check whether all the flags in the specified Flags object are - * present in this Flags object. - * - * @return true if all flags in the given Flags object are present, - * otherwise false. - */ - public boolean contains(Flags f) { - // Check system flags - if ((f.system_flags & system_flags) != f.system_flags) - return false; - - // Check user flags - if (f.user_flags != null) { - if (user_flags == null) - return false; - Enumeration e = f.user_flags.keys(); - - while (e.hasMoreElements()) { - if (!user_flags.containsKey(e.nextElement())) - return false; - } - } - - // If we've made it till here, return true - return true; - } - - /** - * Check whether the two Flags objects are equal. - * - * @return true if they're equal - */ - public boolean equals(Object obj) { - if (!(obj instanceof Flags)) - return false; - - Flags f = (Flags)obj; - - // Check system flags - if (f.system_flags != this.system_flags) - return false; - - // Check user flags - if (f.user_flags == null && this.user_flags == null) - return true; - if (f.user_flags != null && this.user_flags != null && - f.user_flags.size() == this.user_flags.size()) { - Enumeration e = f.user_flags.keys(); - - while (e.hasMoreElements()) { - if (!this.user_flags.containsKey(e.nextElement())) - return false; - } - return true; - } - - return false; - } - - /** - * Compute a hash code for this Flags object. - * - * @return the hash code - */ - public int hashCode() { - int hash = system_flags; - if (user_flags != null) { - Enumeration e = user_flags.keys(); - while (e.hasMoreElements()) - hash += ((String)e.nextElement()).hashCode(); - } - return hash; - } - - /** - * Return all the system flags in this Flags object. Returns - * an array of size zero if no flags are set. - * - * @return array of Flags.Flag objects representing system flags - */ - public Flag[] getSystemFlags() { - Vector v = new Vector(); - if ((system_flags & ANSWERED_BIT) != 0) - v.addElement(Flag.ANSWERED); - if ((system_flags & DELETED_BIT) != 0) - v.addElement(Flag.DELETED); - if ((system_flags & DRAFT_BIT) != 0) - v.addElement(Flag.DRAFT); - if ((system_flags & FLAGGED_BIT) != 0) - v.addElement(Flag.FLAGGED); - if ((system_flags & RECENT_BIT) != 0) - v.addElement(Flag.RECENT); - if ((system_flags & SEEN_BIT) != 0) - v.addElement(Flag.SEEN); - if ((system_flags & USER_BIT) != 0) - v.addElement(Flag.USER); - - Flag[] f = new Flag[v.size()]; - v.copyInto(f); - return f; - } - - /** - * Return all the user flags in this Flags object. Returns - * an array of size zero if no flags are set. - * - * @return array of Strings, each String represents a flag. - */ - public String[] getUserFlags() { - Vector v = new Vector(); - if (user_flags != null) { - Enumeration e = user_flags.elements(); - - while (e.hasMoreElements()) - v.addElement(e.nextElement()); - } - - String[] f = new String[v.size()]; - v.copyInto(f); - return f; - } - - /** - * Returns a clone of this Flags object. - */ - public Object clone() { - Flags f = null; - try { - f = (Flags)super.clone(); - } catch (CloneNotSupportedException cex) { - // ignore, can't happen - } - if (this.user_flags != null) - f.user_flags = (Hashtable)this.user_flags.clone(); - return f; - } - - /***** - public static void main(String argv[]) throws Exception { - // a new flags object - Flags f1 = new Flags(); - f1.add(Flags.Flag.DELETED); - f1.add(Flags.Flag.SEEN); - f1.add(Flags.Flag.RECENT); - f1.add(Flags.Flag.ANSWERED); - - // check copy constructor with only system flags - Flags fc = new Flags(f1); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check clone with only system flags - fc = (Flags)f1.clone(); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // add a user flag and make sure it still works right - f1.add("MyFlag"); - - // shouldn't be equal here - if (!f1.equals(fc) && !fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check clone - fc = (Flags)f1.clone(); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // make sure user flag hash tables are separate - fc.add("AnotherFlag"); - if (!f1.equals(fc) && !fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // check copy constructor - fc = new Flags(f1); - if (f1.equals(fc) && fc.equals(f1)) - System.out.println("success"); - else - System.out.println("fail"); - - // another new flags object - Flags f2 = new Flags(Flags.Flag.ANSWERED); - f2.add("MyFlag"); - - if (f1.contains(Flags.Flag.DELETED)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains(Flags.Flag.SEEN)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains(Flags.Flag.RECENT)) - System.out.println("success"); - else - System.out.println("fail"); - - if (f1.contains("MyFlag")) - System.out.println("success"); - else - System.out.println("fail"); - - if (f2.contains(Flags.Flag.ANSWERED)) - System.out.println("success"); - else - System.out.println("fail"); - - - System.out.println("----------------"); - - String[] s = f1.getUserFlags(); - for (int i = 0; i < s.length; i++) - System.out.println(s[i]); - System.out.println("----------------"); - s = f2.getUserFlags(); - for (int i = 0; i < s.length; i++) - System.out.println(s[i]); - - System.out.println("----------------"); - - if (f1.contains(f2)) // this should be true - System.out.println("success"); - else - System.out.println("fail"); - - if (!f2.contains(f1)) // this should be false - System.out.println("success"); - else - System.out.println("fail"); - - Flags f3 = new Flags(); - f3.add(Flags.Flag.DELETED); - f3.add(Flags.Flag.SEEN); - f3.add(Flags.Flag.RECENT); - f3.add(Flags.Flag.ANSWERED); - f3.add("ANOTHERFLAG"); - f3.add("MYFLAG"); - - f1.add("AnotherFlag"); - - if (f1.equals(f3)) - System.out.println("equals success"); - else - System.out.println("fail"); - if (f3.equals(f1)) - System.out.println("equals success"); - else - System.out.println("fail"); - System.out.println("f1 hash code " + f1.hashCode()); - System.out.println("f3 hash code " + f3.hashCode()); - if (f1.hashCode() == f3.hashCode()) - System.out.println("success"); - else - System.out.println("fail"); - } - ****/ -} diff --git a/src/main/java/javax/mail/Folder.java b/src/main/java/javax/mail/Folder.java deleted file mode 100644 index c02e4823..00000000 --- a/src/main/java/javax/mail/Folder.java +++ /dev/null @@ -1,1662 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.*; -import java.lang.*; -import java.util.Vector; -import java.util.StringTokenizer; -import javax.mail.search.SearchTerm; -import javax.mail.event.*; - -/** - * Folder is an abstract class that represents a folder for mail - * messages. Subclasses implement protocol specific Folders.

      - * - * Folders can contain Messages, other Folders or both, thus providing - * a tree-like hierarchy rooted at the Store's default folder. (Note - * that some Folder implementations may not allow both Messages and - * other Folders in the same Folder).

      - * - * The interpretation of folder names is implementation dependent. - * The different levels of hierarchy in a folder's full name - * are separated from each other by the hierarchy delimiter - * character.

      - * - * The case-insensitive full folder name (that is, the full name - * relative to the default folder for a Store) INBOX - * is reserved to mean the "primary folder for this user on this - * server". Not all Stores will provide an INBOX folder, and not - * all users will have an INBOX folder at all times. The name - * INBOX is reserved to refer to this folder, - * when it exists, in Stores that provide it.

      - * - * A Folder object obtained from a Store need not actually exist - * in the backend store. The exists method tests whether - * the folder exists or not. The create method - * creates a Folder.

      - * - * A Folder is initially in the closed state. Certain methods are valid - * in this state; the documentation for those methods note this. A - * Folder is opened by calling its 'open' method. All Folder methods, - * except open, delete and - * renameTo, are valid in this state.

      - * - * The only way to get a Folder is by invoking the - * getFolder method on Store, Folder, or Session, or by invoking - * the list or listSubscribed methods - * on Folder. Folder objects returned by the above methods are not - * cached by the Store. Thus, invoking the getFolder method - * with the same folder name multiple times will return distinct Folder - * objects. Likewise for the list and listSubscribed - * methods.

      - * - * The Message objects within the Folder are cached by the Folder. - * Thus, invoking getMessage(msgno) on the same message number - * multiple times will return the same Message object, until an - * expunge is done on this Folder.

      - * - * Message objects from a Folder are only valid while a Folder is open - * and should not be accessed after the Folder is closed, even if the - * Folder is subsequently reopened. Instead, new Message objects must - * be fetched from the Folder after the Folder is reopened.

      - * - * Note that a Message's message number can change within a - * session if the containing Folder is expunged using the expunge - * method. Clients that use message numbers as references to messages - * should be aware of this and should be prepared to deal with - * situation (probably by flushing out existing message number references - * and reloading them). Because of this complexity, it is better for - * clients to use Message objects as references to messages, rather than - * message numbers. Expunged Message objects still have to be - * pruned, but other Message objects in that folder are not affected by the - * expunge. - * - * @author John Mani - * @author Bill Shannon - */ - -public abstract class Folder { - - /** - * The parent store. - */ - protected Store store; - - /** - * The open mode of this folder. The open mode is - * Folder.READ_ONLY, Folder.READ_WRITE, - * or -1 if not known. - * @since JavaMail 1.1 - */ - protected int mode = -1; - - /** - * Constructor that takes a Store object. - * - * @param store the Store that holds this folder - */ - protected Folder(Store store) { - this.store = store; - } - - /** - * Returns the name of this Folder.

      - * - * This method can be invoked on a closed Folder. - * - * @return name of the Folder - */ - public abstract String getName(); - - /** - * Returns the full name of this Folder. If the folder resides under - * the root hierarchy of this Store, the returned name is relative - * to the root. Otherwise an absolute name, starting with the - * hierarchy delimiter, is returned.

      - * - * This method can be invoked on a closed Folder. - * - * @return full name of the Folder - */ - public abstract String getFullName(); - - /** - * Return a URLName representing this folder. The returned URLName - * does not include the password used to access the store. - * - * @return the URLName representing this folder - * @see URLName - * @since JavaMail 1.1 - */ - public URLName getURLName() throws MessagingException { - URLName storeURL = getStore().getURLName(); - String fullname = getFullName(); - StringBuffer encodedName = new StringBuffer(); - - if (fullname != null) { - /* - // We need to encode each of the folder's names. - char separator = getSeparator(); - StringTokenizer tok = new StringTokenizer( - fullname, new Character(separator).toString(), true); - - while (tok.hasMoreTokens()) { - String s = tok.nextToken(); - if (s.charAt(0) == separator) - encodedName.append(separator); - else - // XXX - should encode, but since there's no decoder... - //encodedName.append(java.net.URLEncoder.encode(s)); - encodedName.append(s); - } - */ - // append the whole thing, until we can encode - encodedName.append(fullname); - } - - /* - * Sure would be convenient if URLName had a - * constructor that took a base URLName. - */ - return new URLName(storeURL.getProtocol(), storeURL.getHost(), - storeURL.getPort(), encodedName.toString(), - storeURL.getUsername(), - null /* no password */); - } - - /** - * Returns the Store that owns this Folder object. - * This method can be invoked on a closed Folder. - * @return the Store - */ - public Store getStore() { - return store; - } - - /** - * Returns the parent folder of this folder. - * This method can be invoked on a closed Folder. If this folder - * is the top of a folder hierarchy, this method returns null.

      - * - * Note that since Folder objects are not cached, invoking this method - * returns a new distinct Folder object. - * - * @return Parent folder - */ - public abstract Folder getParent() throws MessagingException; - - /** - * Tests if this folder physically exists on the Store. - * This method can be invoked on a closed Folder. - * - * @return true if the folder exists, otherwise false - * @see #create - * @exception MessagingException typically if the connection - * to the server is lost. - */ - public abstract boolean exists() throws MessagingException; - - /** - * Returns a list of Folders belonging to this Folder's namespace - * that match the specified pattern. Patterns may contain the wildcard - * characters "%", which matches any character except hierarchy - * delimiters, and "*", which matches any character.

      - * - * As an example, given the folder hierarchy:

      -     *    Personal/
      -     *       Finance/
      -     *          Stocks
      -     *          Bonus
      -     *          StockOptions
      -     *       Jokes
      -     * 
      - * list("*") on "Personal" will return the whole - * hierarchy.
      - * list("%") on "Personal" will return "Finance" and - * "Jokes".
      - * list("Jokes") on "Personal" will return "Jokes".
      - * list("Stock*") on "Finance" will return "Stocks" - * and "StockOptions".

      - * - * Folder objects are not cached by the Store, so invoking this - * method on the same pattern multiple times will return that many - * distinct Folder objects.

      - * - * This method can be invoked on a closed Folder. - * - * @param pattern the match pattern - * @return array of matching Folder objects. An empty - * array is returned if no matching Folders exist. - * @see #listSubscribed - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public abstract Folder[] list(String pattern) throws MessagingException; - - /** - * Returns a list of subscribed Folders belonging to this Folder's - * namespace that match the specified pattern. If the folder does - * not support subscription, this method should resolve to - * list. - * (The default implementation provided here, does just this). - * The pattern can contain wildcards as for list.

      - * - * Note that, at a given level of the folder hierarchy, a particular - * folder may not be subscribed, but folders underneath that folder - * in the folder hierarchy may be subscribed. In order to allow - * walking the folder hierarchy, such unsubscribed folders may be - * returned, indicating that a folder lower in the hierarchy is - * subscribed. The isSubscribed method on a folder will - * tell whether any particular folder is actually subscribed.

      - * - * Folder objects are not cached by the Store, so invoking this - * method on the same pattern multiple times will return that many - * distinct Folder objects.

      - * - * This method can be invoked on a closed Folder. - * - * @param pattern the match pattern - * @return array of matching subscribed Folder objects. An - * empty array is returned if no matching - * subscribed folders exist. - * @see #list - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public Folder[] listSubscribed(String pattern) throws MessagingException { - return list(pattern); - } - - /** - * Convenience method that returns the list of folders under this - * Folder. This method just calls the list(String pattern) - * method with "%" as the match pattern. This method can - * be invoked on a closed Folder. - * - * @return array of Folder objects under this Folder. An - * empty array is returned if no subfolders exist. - * @see #list - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - - public Folder[] list() throws MessagingException { - return list("%"); - } - - /** - * Convenience method that returns the list of subscribed folders - * under this Folder. This method just calls the - * listSubscribed(String pattern) method with "%" - * as the match pattern. This method can be invoked on a closed Folder. - * - * @return array of subscribed Folder objects under this - * Folder. An empty array is returned if no subscribed - * subfolders exist. - * @see #listSubscribed - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public Folder[] listSubscribed() throws MessagingException { - return listSubscribed("%"); - } - - /** - * Return the delimiter character that separates this Folder's pathname - * from the names of immediate subfolders. This method can be invoked - * on a closed Folder. - * - * @exception FolderNotFoundException if the implementation - * requires the folder to exist, but it does not - * @return Hierarchy separator character - */ - public abstract char getSeparator() throws MessagingException; - - /** - * This folder can contain messages - */ - public final static int HOLDS_MESSAGES = 0x01; - - /** - * This folder can contain other folders - */ - public final static int HOLDS_FOLDERS = 0x02; - - /** - * Returns the type of this Folder, that is, whether this folder can hold - * messages or subfolders or both. The returned value is an integer - * bitfield with the appropriate bits set. This method can be invoked - * on a closed folder. - * - * @return integer with appropriate bits set - * @exception FolderNotFoundException if this folder does - * not exist. - * @see #HOLDS_FOLDERS - * @see #HOLDS_MESSAGES - */ - public abstract int getType() throws MessagingException; - - /** - * Create this folder on the Store. When this folder is created, any - * folders in its path that do not exist are also created.

      - * - * If the creation is successful, a CREATED FolderEvent is delivered - * to any FolderListeners registered on this Folder and this Store. - * - * @param type The type of this folder. - * - * @return true if the creation succeeds, else false. - * @exception MessagingException - * @see #HOLDS_FOLDERS - * @see #HOLDS_MESSAGES - * @see javax.mail.event.FolderEvent - */ - public abstract boolean create(int type) throws MessagingException; - - /** - * Returns true if this Folder is subscribed.

      - * - * This method can be invoked on a closed Folder.

      - * - * The default implementation provided here just returns true. - * - * @return true if this Folder is subscribed - */ - public boolean isSubscribed() { - return true; - } - - /** - * Subscribe or unsubscribe this Folder. Not all Stores support - * subscription.

      - * - * This method can be invoked on a closed Folder.

      - * - * The implementation provided here just throws the - * MethodNotSupportedException. - * - * @param subscribe true to subscribe, false to unsubscribe - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MethodNotSupportedException if this store - * does not support subscription - * @exception MessagingException - */ - public void setSubscribed(boolean subscribe) - throws MessagingException { - throw new MethodNotSupportedException(); - } - - /** - * Returns true if this Folder has new messages since the last time - * this indication was reset. When this indication is set or reset - * depends on the Folder implementation (and in the case of IMAP, - * depends on the server). This method can be used to implement - * a lightweight "check for new mail" operation on a Folder without - * opening it. (For example, a thread that monitors a mailbox and - * flags when it has new mail.) This method should indicate whether - * any messages in the Folder have the RECENT flag set.

      - * - * Note that this is not an incremental check for new mail, i.e., - * it cannot be used to determine whether any new messages have - * arrived since the last time this method was invoked. To - * implement incremental checks, the Folder needs to be opened.

      - * - * This method can be invoked on a closed Folder that can contain - * Messages. - * - * @return true if the Store has new Messages - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public abstract boolean hasNewMessages() throws MessagingException; - - /** - * Return the Folder object corresponding to the given name. Note that - * this folder does not physically have to exist in the Store. The - * exists() method on a Folder indicates whether it really - * exists on the Store.

      - * - * In some Stores, name can be an absolute path if it starts with the - * hierarchy delimiter. Otherwise, it is interpreted relative to - * this Folder.

      - * - * Folder objects are not cached by the Store, so invoking this - * method on the same name multiple times will return that many - * distinct Folder objects.

      - * - * This method can be invoked on a closed Folder. - * - * @param name name of the Folder - * @return Folder object - * @exception MessagingException - */ - public abstract Folder getFolder(String name) - throws MessagingException; - - /** - * Delete this Folder. This method will succeed only on a closed - * Folder.

      - * - * The recurse flag controls whether the deletion affects - * subfolders or not. If true, all subfolders are deleted, then this - * folder itself is deleted. If false, the behaviour is dependent on - * the folder type and is elaborated below:

      - * - *

        - *
      • - * The folder can contain only messages: (type == HOLDS_MESSAGES). - *
        - * All messages within the folder are removed. The folder - * itself is then removed. An appropriate FolderEvent is generated by - * the Store and this folder.

        - * - *

      • - * The folder can contain only subfolders: (type == HOLDS_FOLDERS). - *
        - * If this folder is empty (does not contain any - * subfolders at all), it is removed. An appropriate FolderEvent is - * generated by the Store and this folder.
        - * If this folder contains any subfolders, the delete fails - * and returns false.

        - * - *

      • - * The folder can contain subfolders as well as messages:
        - * If the folder is empty (no messages or subfolders), it - * is removed. If the folder contains no subfolders, but only messages, - * then all messages are removed. The folder itself is then removed. - * In both the above cases, an appropriate FolderEvent is - * generated by the Store and this folder.

        - * - * If the folder contains subfolders there are 3 possible - * choices an implementation is free to do:

        - * - *

          - *
        1. The operation fails, irrespective of whether this folder - * contains messages or not. Some implementations might elect to go - * with this simple approach. The delete() method returns false. - * - *
        2. Any messages within the folder are removed. Subfolders - * are not removed. The folder itself is not removed or affected - * in any manner. The delete() method returns true. And the - * exists() method on this folder will return true indicating that - * this folder still exists.
          - * An appropriate FolderEvent is generated by the Store and this folder. - * - *
        3. Any messages within the folder are removed. Subfolders are - * not removed. The folder itself changes its type from - * HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new - * messages cannot be added to this folder, but new subfolders can - * be created underneath. The delete() method returns true indicating - * success. The exists() method on this folder will return true - * indicating that this folder still exists.
          - * An appropriate FolderEvent is generated by the Store and this folder. - *
        - *
      - * - * @return true if the Folder is deleted successfully - * @exception FolderNotFoundException if this folder does - * not exist - * @exception IllegalStateException if this folder is not in - * the closed state. - * @exception MessagingException - * @see javax.mail.event.FolderEvent - */ - public abstract boolean delete(boolean recurse) - throws MessagingException; - - /** - * Rename this Folder. This method will succeed only on a closed - * Folder.

      - * - * If the rename is successful, a RENAMED FolderEvent is delivered - * to FolderListeners registered on this folder and its containing - * Store. - * - * @param f a folder representing the new name for this Folder - * @return true if the Folder is renamed successfully - * @exception FolderNotFoundException if this folder does - * not exist - * @exception IllegalStateException if this folder is not in - * the closed state. - * @exception MessagingException - * @see javax.mail.event.FolderEvent - */ - public abstract boolean renameTo(Folder f) throws MessagingException; - - /** - * The Folder is read only. The state and contents of this - * folder cannot be modified. - */ - public static final int READ_ONLY = 1; - - /** - * The state and contents of this folder can be modified. - */ - public static final int READ_WRITE = 2; - - /** - * Open this Folder. This method is valid only on Folders that - * can contain Messages and that are closed.

      - * - * If this folder is opened successfully, an OPENED ConnectionEvent - * is delivered to any ConnectionListeners registered on this - * Folder.

      - * - * The effect of opening multiple connections to the same folder - * on a specifc Store is implementation dependent. Some implementations - * allow multiple readers, but only one writer. Others allow - * multiple writers as well as readers. - * - * @param mode open the Folder READ_ONLY or READ_WRITE - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not in - * the closed state. - * @exception MessagingException - * @see #READ_ONLY - * @see #READ_WRITE - * @see #getType() - * @see javax.mail.event.ConnectionEvent - */ - public abstract void open(int mode) throws MessagingException; - - /** - * Close this Folder. This method is valid only on open Folders.

      - * - * A CLOSED ConnectionEvent is delivered to any ConnectionListeners - * registered on this Folder. Note that the folder is closed even - * if this method terminates abnormally by throwing a - * MessagingException. - * - * @param expunge expunges all deleted messages if this flag is true - * @exception IllegalStateException if this folder is not opened - * @exception MessagingException - * @see javax.mail.event.ConnectionEvent - */ - public abstract void close(boolean expunge) throws MessagingException; - - /** - * Indicates whether this Folder is in the 'open' state. - * @return true if this Folder is in the 'open' state. - */ - public abstract boolean isOpen(); - - /** - * Return the open mode of this folder. Returns - * Folder.READ_ONLY, Folder.READ_WRITE, - * or -1 if the open mode is not known (usually only because an older - * Folder provider has not been updated to use this new - * method). - * - * @exception IllegalStateException if this folder is not opened - * @return the open mode of this folder - * @since JavaMail 1.1 - */ - public synchronized int getMode() { - if (!isOpen()) - throw new IllegalStateException("Folder not open"); - return mode; - } - - /** - * Get the permanent flags supported by this Folder. Returns a Flags - * object that contains all the flags supported.

      - * - * The special flag Flags.Flag.USER indicates that this Folder - * supports arbitrary user-defined flags.

      - * - * The supported permanent flags for a folder may not be available - * until the folder is opened. - * - * @return permanent flags, or null if not known - */ - public abstract Flags getPermanentFlags(); - - /** - * Get total number of messages in this Folder.

      - * - * This method can be invoked on a closed folder. However, note - * that for some folder implementations, getting the total message - * count can be an expensive operation involving actually opening - * the folder. In such cases, a provider can choose not to support - * this functionality in the closed state, in which case this method - * must return -1.

      - * - * Clients invoking this method on a closed folder must be aware - * that this is a potentially expensive operation. Clients must - * also be prepared to handle a return value of -1 in this case. - * - * @return total number of messages. -1 may be returned - * by certain implementations if this method is - * invoked on a closed folder. - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public abstract int getMessageCount() throws MessagingException; - - /** - * Get the number of new messages in this Folder.

      - * - * This method can be invoked on a closed folder. However, note - * that for some folder implementations, getting the new message - * count can be an expensive operation involving actually opening - * the folder. In such cases, a provider can choose not to support - * this functionality in the closed state, in which case this method - * must return -1.

      - * - * Clients invoking this method on a closed folder must be aware - * that this is a potentially expensive operation. Clients must - * also be prepared to handle a return value of -1 in this case.

      - * - * This implementation returns -1 if this folder is closed. Else - * this implementation gets each Message in the folder using - * getMessage(int) and checks whether its - * RECENT flag is set. The total number of messages - * that have this flag set is returned. - * - * @return number of new messages. -1 may be returned - * by certain implementations if this method is - * invoked on a closed folder. - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public synchronized int getNewMessageCount() - throws MessagingException { - if (!isOpen()) - return -1; - - int newmsgs = 0; - int total = getMessageCount(); - for (int i = 1; i <= total; i++) { - try { - if (getMessage(i).isSet(Flags.Flag.RECENT)) - newmsgs++; - } catch (MessageRemovedException me) { - // This is an expunged message, ignore it. - continue; - } - } - return newmsgs; - } - - /** - * Get the total number of unread messages in this Folder.

      - * - * This method can be invoked on a closed folder. However, note - * that for some folder implementations, getting the unread message - * count can be an expensive operation involving actually opening - * the folder. In such cases, a provider can choose not to support - * this functionality in the closed state, in which case this method - * must return -1.

      - * - * Clients invoking this method on a closed folder must be aware - * that this is a potentially expensive operation. Clients must - * also be prepared to handle a return value of -1 in this case.

      - * - * This implementation returns -1 if this folder is closed. Else - * this implementation gets each Message in the folder using - * getMessage(int) and checks whether its - * SEEN flag is set. The total number of messages - * that do not have this flag set is returned. - * - * @return total number of unread messages. -1 may be returned - * by certain implementations if this method is - * invoked on a closed folder. - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - */ - public synchronized int getUnreadMessageCount() - throws MessagingException { - if (!isOpen()) - return -1; - - int unread = 0; - int total = getMessageCount(); - for (int i = 1; i <= total; i++) { - try { - if (!getMessage(i).isSet(Flags.Flag.SEEN)) - unread++; - } catch (MessageRemovedException me) { - // This is an expunged message, ignore it. - continue; - } - } - return unread; - } - - /** - * Get the number of deleted messages in this Folder.

      - * - * This method can be invoked on a closed folder. However, note - * that for some folder implementations, getting the deleted message - * count can be an expensive operation involving actually opening - * the folder. In such cases, a provider can choose not to support - * this functionality in the closed state, in which case this method - * must return -1.

      - * - * Clients invoking this method on a closed folder must be aware - * that this is a potentially expensive operation. Clients must - * also be prepared to handle a return value of -1 in this case.

      - * - * This implementation returns -1 if this folder is closed. Else - * this implementation gets each Message in the folder using - * getMessage(int) and checks whether its - * DELETED flag is set. The total number of messages - * that have this flag set is returned. - * - * @return number of deleted messages. -1 may be returned - * by certain implementations if this method is - * invoked on a closed folder. - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException - * @since JavaMail 1.3 - */ - public synchronized int getDeletedMessageCount() throws MessagingException { - if (!isOpen()) - return -1; - - int deleted = 0; - int total = getMessageCount(); - for (int i = 1; i <= total; i++) { - try { - if (getMessage(i).isSet(Flags.Flag.DELETED)) - deleted++; - } catch (MessageRemovedException me) { - // This is an expunged message, ignore it. - continue; - } - } - return deleted; - } - - /** - * Get the Message object corresponding to the given message - * number. A Message object's message number is the relative - * position of this Message in its Folder. Messages are numbered - * starting at 1 through the total number of message in the folder. - * Note that the message number for a particular Message can change - * during a session if other messages in the Folder are deleted and - * the Folder is expunged.

      - * - * Message objects are light-weight references to the actual message - * that get filled up on demand. Hence Folder implementations are - * expected to provide light-weight Message objects.

      - * - * Unlike Folder objects, repeated calls to getMessage with the - * same message number will return the same Message object, as - * long as no messages in this folder have been expunged.

      - * - * Since message numbers can change within a session if the folder - * is expunged , clients are advised not to use message numbers as - * references to messages. Use Message objects instead. - * - * @param msgnum the message number - * @return the Message object - * @see #getMessageCount - * @see #fetch - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not opened - * @exception IndexOutOfBoundsException if the message number - * is out of range. - * @exception MessagingException - */ - public abstract Message getMessage(int msgnum) - throws MessagingException; - - /** - * Get the Message objects for message numbers ranging from start - * through end, both start and end inclusive. Note that message - * numbers start at 1, not 0.

      - * - * Message objects are light-weight references to the actual message - * that get filled up on demand. Hence Folder implementations are - * expected to provide light-weight Message objects.

      - * - * This implementation uses getMessage(index) to obtain the required - * Message objects. Note that the returned array must contain - * (end-start+1) Message objects. - * - * @param start the number of the first message - * @param end the number of the last message - * @return the Message objects - * @see #fetch - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not opened. - * @exception IndexOutOfBoundsException if the start or end - * message numbers are out of range. - * @exception MessagingException - */ - public synchronized Message[] getMessages(int start, int end) - throws MessagingException { - Message[] msgs = new Message[end - start +1]; - for (int i = start; i <= end; i++) - msgs[i - start] = getMessage(i); - return msgs; - } - - /** - * Get the Message objects for message numbers specified in - * the array.

      - * - * Message objects are light-weight references to the actual message - * that get filled up on demand. Hence Folder implementations are - * expected to provide light-weight Message objects.

      - * - * This implementation uses getMessage(index) to obtain the required - * Message objects. Note that the returned array must contain - * msgnums.length Message objects - * - * @param msgnums the array of message numbers - * @return the array of Message objects. - * @see #fetch - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not opened. - * @exception IndexOutOfBoundsException if any message number - * in the given array is out of range. - * @exception MessagingException - */ - public synchronized Message[] getMessages(int[] msgnums) - throws MessagingException { - int len = msgnums.length; - Message[] msgs = new Message[len]; - for (int i = 0; i < len; i++) - msgs[i] = getMessage(msgnums[i]); - return msgs; - } - - /** - * Get all Message objects from this Folder. Returns an empty array - * if the folder is empty. - * - * Clients can use Message objects (instead of sequence numbers) - * as references to the messages within a folder; this method supplies - * the Message objects to the client. Folder implementations are - * expected to provide light-weight Message objects, which get - * filled on demand.

      - * - * This implementation invokes getMessageCount() to get - * the current message count and then uses getMessage() - * to get Message objects from 1 till the message count. - * - * @return array of Message objects, empty array if folder - * is empty. - * @see #fetch - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not opened. - * @exception MessagingException - */ - public synchronized Message[] getMessages() throws MessagingException { - if (!isOpen()) // otherwise getMessageCount might return -1 - throw new IllegalStateException("Folder not open"); - int total = getMessageCount(); - Message[] msgs = new Message[total]; - for (int i = 1; i <= total; i++) - msgs[i-1] = getMessage(i); - return msgs; - } - - /** - * Append given Messages to this folder. This method can be - * invoked on a closed Folder. An appropriate MessageCountEvent - * is delivered to any MessageCountListener registered on this - * folder when the messages arrive in the folder.

      - * - * Folder implementations must not abort this operation if a - * Message in the given message array turns out to be an - * expunged Message. - * - * @param msgs array of Messages to be appended - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception MessagingException if the append failed. - */ - public abstract void appendMessages(Message[] msgs) - throws MessagingException; - - /** - * Prefetch the items specified in the FetchProfile for the - * given Messages.

      - * - * Clients use this method to indicate that the specified items are - * needed en-masse for the given message range. Implementations are - * expected to retrieve these items for the given message range in - * a efficient manner. Note that this method is just a hint to the - * implementation to prefetch the desired items.

      - * - * An example is a client filling its header-view window with - * the Subject, From and X-mailer headers for all messages in the - * folder.

      - *

      -     *
      -     *  Message[] msgs = folder.getMessages();
      -     *
      -     *  FetchProfile fp = new FetchProfile();
      -     *  fp.add(FetchProfile.Item.ENVELOPE);
      -     *  fp.add("X-mailer");
      -     *  folder.fetch(msgs, fp);
      -     *  
      -     *  for (int i = 0; i < folder.getMessageCount(); i++) {
      -     *      display(msg[i].getFrom());
      -     *      display(msg[i].getSubject());
      -     *      display(msg[i].getHeader("X-mailer"));
      -     *  }
      -     *
      -     * 

      - * - * The implementation provided here just returns without - * doing anything useful. Providers wanting to provide a real - * implementation for this method should override this method. - * - * @param msgs fetch items for these messages - * @param fp the FetchProfile - * @exception IllegalStateException if this folder is not opened - * @exception MessagingException. - */ - public void fetch(Message[] msgs, FetchProfile fp) - throws MessagingException { - return; - } - - /** - * Set the specified flags on the messages specified in the array. - * This will result in appropriate MessageChangedEvents being - * delivered to any MessageChangedListener registered on this - * Message's containing folder.

      - * - * Note that the specified Message objects must - * belong to this folder. Certain Folder implementations can - * optimize the operation of setting Flags for a group of messages, - * so clients might want to use this method, rather than invoking - * Message.setFlags for each Message.

      - * - * This implementation degenerates to invoking setFlags() - * on each Message object. Specific Folder implementations that can - * optimize this case should do so. - * Also, an implementation must not abort the operation if a Message - * in the array turns out to be an expunged Message. - * - * @param msgs the array of message objects - * @param flag Flags object containing the flags to be set - * @param value set the flags to this boolean value - * @exception IllegalStateException if this folder is not opened - * or if it has been opened READ_ONLY. - * @exception MessagingException - * @see Message#setFlags - * @see javax.mail.event.MessageChangedEvent - */ - public synchronized void setFlags(Message[] msgs, - Flags flag, boolean value) throws MessagingException { - for (int i = 0; i < msgs.length; i++) { - try { - msgs[i].setFlags(flag, value); - } catch (MessageRemovedException me) { - // This message is expunged, skip - } - } - } - - /** - * Set the specified flags on the messages numbered from start - * through end, both start and end inclusive. Note that message - * numbers start at 1, not 0. - * This will result in appropriate MessageChangedEvents being - * delivered to any MessageChangedListener registered on this - * Message's containing folder.

      - * - * Certain Folder implementations can - * optimize the operation of setting Flags for a group of messages, - * so clients might want to use this method, rather than invoking - * Message.setFlags for each Message.

      - * - * The default implementation uses getMessage(int) to - * get each Message object and then invokes - * setFlags on that object to set the flags. - * Specific Folder implementations that can optimize this case should do so. - * Also, an implementation must not abort the operation if a message - * number refers to an expunged message. - * - * @param start the number of the first message - * @param end the number of the last message - * @param flag Flags object containing the flags to be set - * @param value set the flags to this boolean value - * @exception IllegalStateException if this folder is not opened - * or if it has been opened READ_ONLY. - * @exception IndexOutOfBoundsException if the start or end - * message numbers are out of range. - * @exception MessagingException - * @see Message#setFlags - * @see javax.mail.event.MessageChangedEvent - */ - public synchronized void setFlags(int start, int end, - Flags flag, boolean value) throws MessagingException { - for (int i = start; i <= end; i++) { - try { - Message msg = getMessage(i); - msg.setFlags(flag, value); - } catch (MessageRemovedException me) { - // This message is expunged, skip - } - } - } - - /** - * Set the specified flags on the messages whose message numbers - * are in the array. - * This will result in appropriate MessageChangedEvents being - * delivered to any MessageChangedListener registered on this - * Message's containing folder.

      - * - * Certain Folder implementations can - * optimize the operation of setting Flags for a group of messages, - * so clients might want to use this method, rather than invoking - * Message.setFlags for each Message.

      - * - * The default implementation uses getMessage(int) to - * get each Message object and then invokes - * setFlags on that object to set the flags. - * Specific Folder implementations that can optimize this case should do so. - * Also, an implementation must not abort the operation if a message - * number refers to an expunged message. - * - * @param msgnums the array of message numbers - * @param flag Flags object containing the flags to be set - * @param value set the flags to this boolean value - * @exception IllegalStateException if this folder is not opened - * or if it has been opened READ_ONLY. - * @exception IndexOutOfBoundsException if any message number - * in the given array is out of range. - * @exception MessagingException - * @see Message#setFlags - * @see javax.mail.event.MessageChangedEvent - */ - public synchronized void setFlags(int[] msgnums, - Flags flag, boolean value) throws MessagingException { - for (int i = 0; i < msgnums.length; i++) { - try { - Message msg = getMessage(msgnums[i]); - msg.setFlags(flag, value); - } catch (MessageRemovedException me) { - // This message is expunged, skip - } - } - } - - /** - * Copy the specified Messages from this Folder into another - * Folder. This operation appends these Messages to the - * destination Folder. The destination Folder does not have to - * be opened. An appropriate MessageCountEvent - * is delivered to any MessageCountListener registered on the - * destination folder when the messages arrive in the folder.

      - * - * Note that the specified Message objects must - * belong to this folder. Folder implementations might be able - * to optimize this method by doing server-side copies.

      - * - * This implementation just invokes appendMessages() - * on the destination folder to append the given Messages. Specific - * folder implementations that support server-side copies should - * do so, if the destination folder's Store is the same as this - * folder's Store. - * Also, an implementation must not abort the operation if a - * Message in the array turns out to be an expunged Message. - * - * @param msgs the array of message objects - * @param folder the folder to copy the messages to - * @exception FolderNotFoundException if the destination - * folder does not exist. - * @exception IllegalStateException if this folder is not opened. - * @exception MessagingException - * @see #appendMessages - */ - public void copyMessages(Message[] msgs, Folder folder) - throws MessagingException { - if (!folder.exists()) - throw new FolderNotFoundException( - folder.getFullName() + " does not exist", - folder); - - folder.appendMessages(msgs); - } - - /** - * Expunge (permanently remove) messages marked DELETED. Returns an - * array containing the expunged message objects. The - * getMessageNumber method - * on each of these message objects returns that Message's original - * (that is, prior to the expunge) sequence number. A MessageCountEvent - * containing the expunged messages is delivered to any - * MessageCountListeners registered on the folder.

      - * - * Expunge causes the renumbering of Message objects subsequent to - * the expunged messages. Clients that use message numbers as - * references to messages should be aware of this and should be - * prepared to deal with the situation (probably by flushing out - * existing message number caches and reloading them). Because of - * this complexity, it is better for clients to use Message objects - * as references to messages, rather than message numbers. Any - * expunged Messages objects still have to be pruned, but other - * Messages in that folder are not affected by the expunge.

      - * - * After a message is expunged, only the isExpunged and - * getMessageNumber methods are still valid on the - * corresponding Message object; other methods may throw - * MessageRemovedException - * - * @return array of expunged Message objects - * @exception FolderNotFoundException if this folder does not - * exist - * @exception IllegalStateException if this folder is not opened. - * @exception MessagingException - * @see Message#isExpunged - * @see javax.mail.event.MessageCountEvent - */ - public abstract Message[] expunge() throws MessagingException; - - /** - * Search this Folder for messages matching the specified - * search criterion. Returns an array containing the matching - * messages . Returns an empty array if no matches were found.

      - * - * This implementation invokes - * search(term, getMessages()), to apply the search - * over all the messages in this folder. Providers that can implement - * server-side searching might want to override this method to provide - * a more efficient implementation. - * - * @param term the search criterion - * @return array of matching messages - * @exception javax.mail.search.SearchException if the search - * term is too complex for the implementation to handle. - * @exception FolderNotFoundException if this folder does - * not exist. - * @exception IllegalStateException if this folder is not opened. - * @exception MessagingException - * @see javax.mail.search.SearchTerm - */ - public Message[] search(SearchTerm term) throws MessagingException { - return search(term, getMessages()); - } - - /** - * Search the given array of messages for those that match the - * specified search criterion. Returns an array containing the - * matching messages. Returns an empty array if no matches were - * found.

      - * - * Note that the specified Message objects must - * belong to this folder.

      - * - * This implementation iterates through the given array of messages, - * and applies the search criterion on each message by calling - * its match() method with the given term. The - * messages that succeed in the match are returned. Providers - * that can implement server-side searching might want to override - * this method to provide a more efficient implementation. If the - * search term is too complex or contains user-defined terms that - * cannot be executed on the server, providers may elect to either - * throw a SearchException or degenerate to client-side searching by - * calling super.search() to invoke this implementation. - * - * @param term the search criterion - * @param msgs the messages to be searched - * @return array of matching messages - * @exception javax.mail.search.SearchException if the search - * term is too complex for the implementation to handle. - * @exception IllegalStateException if this folder is not opened - * @exception MessagingException - * @see javax.mail.search.SearchTerm - */ - public Message[] search(SearchTerm term, Message[] msgs) - throws MessagingException { - Vector matchedMsgs = new Vector(); - - // Run thru the given messages - for (int i = 0; i < msgs.length; i++) { - try { - if (msgs[i].match(term)) // matched - matchedMsgs.addElement(msgs[i]); // add it - } catch(MessageRemovedException mrex) { } - } - - Message[] m = new Message[matchedMsgs.size()]; - matchedMsgs.copyInto(m); - return m; - } - - /* - * The set of listeners are stored in Vectors appropriate to their - * type. We mark all listener Vectors as "volatile" because, while - * we initialize them inside this folder's synchronization lock, - * they are accessed (checked for null) in the "notify" methods, - * which can't be synchronized due to lock ordering constraints. - * Since the listener fields (the handles on the Vector objects) - * are only ever set, and are never cleared, we believe this is - * safe. The code that dispatches the notifications will either - * see the null and assume there are no listeners or will see the - * Vector and will process the listeners. There's an inherent race - * between adding a listener and notifying the listeners; the lack - * of synchronization during notification does not make the race - * condition significantly worse. If one thread is setting a - * listener at the "same" time an event is being dispatched, the - * dispatch code might not see the listener right away. The - * dispatch code doesn't have to worry about the Vector handle - * being set to null, and thus using an out-of-date set of - * listeners, because we never set the field to null. - */ - - // Vector of connection listeners. - private volatile Vector connectionListeners = null; - - /** - * Add a listener for Connection events on this Folder.

      - * - * The implementation provided here adds this listener - * to an internal list of ConnectionListeners. - * - * @param l the Listener for Connection events - * @see javax.mail.event.ConnectionEvent - */ - public synchronized void - addConnectionListener(ConnectionListener l) { - if (connectionListeners == null) - connectionListeners = new Vector(); - connectionListeners.addElement(l); - } - - /** - * Remove a Connection event listener.

      - * - * The implementation provided here removes this listener - * from the internal list of ConnectionListeners. - * - * @param l the listener - * @see #addConnectionListener - */ - public synchronized void - removeConnectionListener(ConnectionListener l) { - if (connectionListeners != null) - connectionListeners.removeElement(l); - } - - /** - * Notify all ConnectionListeners. Folder implementations are - * expected to use this method to broadcast connection events.

      - * - * The provided implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * ConnectionListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - * - * @param type the ConnectionEvent type - * @see javax.mail.event.ConnectionEvent - */ - protected void notifyConnectionListeners(int type) { - if (connectionListeners != null) { - ConnectionEvent e = new ConnectionEvent(this, type); - queueEvent(e, connectionListeners); - } - - /* Fix for broken JDK1.1.x Garbage collector : - * The 'conservative' GC in JDK1.1.x occasionally fails to - * garbage-collect Threads which are in the wait state. - * This would result in thread (and consequently memory) leaks. - * - * We attempt to fix this by sending a 'terminator' event - * to the queue, after we've sent the CLOSED event. The - * terminator event causes the event-dispatching thread to - * self destruct. - */ - if (type == ConnectionEvent.CLOSED) - terminateQueue(); - } - - // Vector of folder listeners - private volatile Vector folderListeners = null; - - /** - * Add a listener for Folder events on this Folder.

      - * - * The implementation provided here adds this listener - * to an internal list of FolderListeners. - * - * @param l the Listener for Folder events - * @see javax.mail.event.FolderEvent - */ - public synchronized void addFolderListener(FolderListener l) { - if (folderListeners == null) - folderListeners = new Vector(); - folderListeners.addElement(l); - } - - /** - * Remove a Folder event listener.

      - * - * The implementation provided here removes this listener - * from the internal list of FolderListeners. - * - * @param l the listener - * @see #addFolderListener - */ - public synchronized void removeFolderListener(FolderListener l) { - if (folderListeners != null) - folderListeners.removeElement(l); - } - - /** - * Notify all FolderListeners registered on this Folder and - * this folder's Store. Folder implementations are expected - * to use this method to broadcast Folder events.

      - * - * The implementation provided here queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the - * FolderListeners registered on this folder. The implementation - * also invokes notifyFolderListeners on this folder's - * Store to notify any FolderListeners registered on the store. - * - * @param type type of FolderEvent - * @see #notifyFolderRenamedListeners - */ - protected void notifyFolderListeners(int type) { - if (folderListeners != null) { - FolderEvent e = new FolderEvent(this, this, type); - queueEvent(e, folderListeners); - } - store.notifyFolderListeners(type, this); - } - - /** - * Notify all FolderListeners registered on this Folder and - * this folder's Store about the renaming of this folder. - * Folder implementations are expected to use this method to - * broadcast Folder events indicating the renaming of folders.

      - * - * The implementation provided here queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the - * FolderListeners registered on this folder. The implementation - * also invokes notifyFolderRenamedListeners on this - * folder's Store to notify any FolderListeners registered on the store. - * - * @param folder Folder representing the new name. - * @see #notifyFolderListeners - * @since JavaMail 1.1 - */ - protected void notifyFolderRenamedListeners(Folder folder) { - if (folderListeners != null) { - FolderEvent e = new FolderEvent(this, this, folder, - FolderEvent.RENAMED); - queueEvent(e, folderListeners); - } - store.notifyFolderRenamedListeners(this, folder); - } - - // Vector of MessageCount listeners - private volatile Vector messageCountListeners = null; - - /** - * Add a listener for MessageCount events on this Folder.

      - * - * The implementation provided here adds this listener - * to an internal list of MessageCountListeners. - * - * @param l the Listener for MessageCount events - * @see javax.mail.event.MessageCountEvent - */ - public synchronized void addMessageCountListener(MessageCountListener l) { - if (messageCountListeners == null) - messageCountListeners = new Vector(); - messageCountListeners.addElement(l); - } - - /** - * Remove a MessageCount listener.

      - * - * The implementation provided here removes this listener - * from the internal list of MessageCountListeners. - * - * @param l the listener - * @see #addMessageCountListener - */ - public synchronized void - removeMessageCountListener(MessageCountListener l) { - if (messageCountListeners != null) - messageCountListeners.removeElement(l); - } - - /** - * Notify all MessageCountListeners about the addition of messages - * into this folder. Folder implementations are expected to use this - * method to broadcast MessageCount events for indicating arrival of - * new messages.

      - * - * The provided implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * MessageCountListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyMessageAddedListeners(Message[] msgs) { - if (messageCountListeners == null) - return; - - MessageCountEvent e = new MessageCountEvent( - this, - MessageCountEvent.ADDED, - false, - msgs); - - queueEvent(e, messageCountListeners); - } - - /** - * Notify all MessageCountListeners about the removal of messages - * from this Folder. Folder implementations are expected to use this - * method to broadcast MessageCount events indicating removal of - * messages.

      - * - * The provided implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * MessageCountListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyMessageRemovedListeners(boolean removed, - Message[] msgs) { - if (messageCountListeners == null) - return; - - MessageCountEvent e = new MessageCountEvent( - this, - MessageCountEvent.REMOVED, - removed, - msgs); - queueEvent(e, messageCountListeners); - } - - // Vector of MessageChanged listeners. - private volatile Vector messageChangedListeners = null; - - /** - * Add a listener for MessageChanged events on this Folder.

      - * - * The implementation provided here adds this listener - * to an internal list of MessageChangedListeners. - * - * @param l the Listener for MessageChanged events - * @see javax.mail.event.MessageChangedEvent - */ - public synchronized void - addMessageChangedListener(MessageChangedListener l) { - if (messageChangedListeners == null) - messageChangedListeners = new Vector(); - messageChangedListeners.addElement(l); - } - - /** - * Remove a MessageChanged listener.

      - * - * The implementation provided here removes this listener - * from the internal list of MessageChangedListeners. - * - * @param l the listener - * @see #addMessageChangedListener - */ - public synchronized void - removeMessageChangedListener(MessageChangedListener l) { - if (messageChangedListeners != null) - messageChangedListeners.removeElement(l); - } - - /** - * Notify all MessageChangedListeners. Folder implementations are - * expected to use this method to broadcast MessageChanged events.

      - * - * The provided implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to registered - * MessageChangedListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyMessageChangedListeners(int type, Message msg) { - if (messageChangedListeners == null) - return; - - MessageChangedEvent e = new MessageChangedEvent(this, type, msg); - queueEvent(e, messageChangedListeners); - } - - /* - * The queue of events to be delivered. - */ - private EventQueue q; - - /* - * A lock for creating the EventQueue object. Only one thread should - * create an EventQueue for this folder. We can't synchronize on the - * folder's lock because that would violate the locking hierarchy in - * some cases. For details, see the IMAP provider. - */ - private Object qLock = new Object(); - - /* - * Add the event and vector of listeners to the queue to be delivered. - */ - private void queueEvent(MailEvent event, Vector vector) { - // synchronize creation of the event queue - synchronized (qLock) { - if (q == null) - q = new EventQueue(); - } - - /* - * Copy the vector in order to freeze the state of the set - * of EventListeners the event should be delivered to prior - * to delivery. This ensures that any changes made to the - * Vector from a target listener's method during the delivery - * of this event will not take effect until after the event is - * delivered. - */ - Vector v = (Vector)vector.clone(); - q.enqueue(event, v); - } - - static class TerminatorEvent extends MailEvent { - private static final long serialVersionUID = 3765761925441296565L; - - TerminatorEvent() { - super(new Object()); - } - - public void dispatch(Object listener) { - // Kill the event dispatching thread. - Thread.currentThread().interrupt(); - } - } - - // Dispatch the terminator - private void terminateQueue() { - synchronized (qLock) { - if (q != null) { - Vector dummyListeners = new Vector(); - dummyListeners.setSize(1); // need atleast one listener - q.enqueue(new TerminatorEvent(), dummyListeners); - q = null; - } - } - } - - protected void finalize() throws Throwable { - super.finalize(); - terminateQueue(); - } - - /** - * override the default toString(), it will return the String - * from Folder.getFullName() or if that is null, it will use - * the default toString() behavior. - */ - - public String toString() { - String s = getFullName(); - if (s != null) - return s; - else - return super.toString(); - } -} diff --git a/src/main/java/javax/mail/FolderClosedException.java b/src/main/java/javax/mail/FolderClosedException.java deleted file mode 100644 index efa1f90a..00000000 --- a/src/main/java/javax/mail/FolderClosedException.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when a method is invoked on a Messaging object - * and the Folder that owns that object has died due to some reason.

      - * - * Following the exception, the Folder is reset to the "closed" state. - * All messaging objects owned by the Folder should be considered invalid. - * The Folder can be reopened using the "open" method to reestablish the - * lost connection.

      - * - * The getMessage() method returns more detailed information about the - * error that caused this exception.

      - * - * @author John Mani - */ - -public class FolderClosedException extends MessagingException { - transient private Folder folder; - - private static final long serialVersionUID = 1687879213433302315L; - - /** - * Constructs a FolderClosedException. - * - * @param folder The Folder - */ - public FolderClosedException(Folder folder) { - this(folder, null); - } - - /** - * Constructs a FolderClosedException with the specified - * detail message. - * - * @param folder The Folder - * @param message The detailed error message - */ - public FolderClosedException(Folder folder, String message) { - super(message); - this.folder = folder; - } - - /** - * Constructs a FolderClosedException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param folder The Folder - * @param message The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public FolderClosedException(Folder folder, String message, Exception e) { - super(message, e); - this.folder = folder; - } - - /** - * Returns the dead Folder object - */ - public Folder getFolder() { - return folder; - } -} diff --git a/src/main/java/javax/mail/FolderNotFoundException.java b/src/main/java/javax/mail/FolderNotFoundException.java deleted file mode 100644 index bc00eb06..00000000 --- a/src/main/java/javax/mail/FolderNotFoundException.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.lang.*; - -/** - * This exception is thrown by Folder methods, when those - * methods are invoked on a non existent folder. - * - * @author John Mani - */ - -public class FolderNotFoundException extends MessagingException { - transient private Folder folder; - - private static final long serialVersionUID = 472612108891249403L; - - /** - * Constructs a FolderNotFoundException with no detail message. - */ - public FolderNotFoundException() { - super(); - } - - /** - * Constructs a FolderNotFoundException. - * - * @param folder The Folder - * @since JavaMail 1.2 - */ - public FolderNotFoundException(Folder folder) { - super(); - this.folder = folder; - } - - /** - * Constructs a FolderNotFoundException with the specified - * detail message. - * - * @param folder The Folder - * @param s The detailed error message - * @since JavaMail 1.2 - */ - public FolderNotFoundException(Folder folder, String s) { - super(s); - this.folder = folder; - } - - /** - * Constructs a FolderNotFoundException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param folder The Folder - * @param s The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public FolderNotFoundException(Folder folder, String s, Exception e) { - super(s, e); - this.folder = folder; - } - - /** - * Constructs a FolderNotFoundException with the specified detail message - * and the specified folder. - * - * @param s The detail message - * @param folder The Folder - */ - public FolderNotFoundException(String s, Folder folder) { - super(s); - this.folder = folder; - } - - /** - * Returns the offending Folder object. - * - * @return the Folder object. Note that the returned value can be - * null. - */ - public Folder getFolder() { - return folder; - } -} diff --git a/src/main/java/javax/mail/Header.java b/src/main/java/javax/mail/Header.java deleted file mode 100644 index 1236229a..00000000 --- a/src/main/java/javax/mail/Header.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - - -/** - * The Header class stores a name/value pair to represent headers. - * - * @author John Mani - */ - -public class Header { - - /** - * The name of the header. - * - * @since JavaMail 1.4 - */ - protected String name; - - /** - * The value of the header. - * - * @since JavaMail 1.4 - */ - protected String value; - - /** - * Construct a Header object. - * - * @param name name of the header - * @param value value of the header - */ - public Header(String name, String value) { - this.name = name; - this.value = value; - } - - /** - * Returns the name of this header. - * - * @return name of the header - */ - public String getName() { - return name; - } - - /** - * Returns the value of this header. - * - * @return value of the header - */ - public String getValue() { - return value; - } -} diff --git a/src/main/java/javax/mail/IllegalWriteException.java b/src/main/java/javax/mail/IllegalWriteException.java deleted file mode 100644 index ea0b7d3d..00000000 --- a/src/main/java/javax/mail/IllegalWriteException.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - - -/** - * The exception thrown when a write is attempted on a read-only attribute - * of any Messaging object. - * - * @author John Mani - */ - -public class IllegalWriteException extends MessagingException { - - private static final long serialVersionUID = 3974370223328268013L; - - /** - * Constructs an IllegalWriteException with no detail message. - */ - public IllegalWriteException() { - super(); - } - - /** - * Constructs an IllegalWriteException with the specified - * detail message. - * - * @param s The detailed error message - */ - public IllegalWriteException(String s) { - super(s); - } - - /** - * Constructs an IllegalWriteException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param s The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public IllegalWriteException(String s, Exception e) { - super(s, e); - } -} diff --git a/src/main/java/javax/mail/MailSessionDefinition.java b/src/main/java/javax/mail/MailSessionDefinition.java deleted file mode 100644 index 1cbc4d31..00000000 --- a/src/main/java/javax/mail/MailSessionDefinition.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation used by Java EE applications to define a MailSession - * to be registered with JNDI. The MailSession may be configured - * by setting the annotation elements for commonly used Session - * properties. Additional standard and vendor-specific properties may be - * specified using the properties element. - *

      - * The session will be registered under the name specified in the - * name element. It may be defined to be in any valid - * Java EE namespace, and will determine the accessibility of - * the session from other components. - * - * @since JavaMail 1.5 - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface MailSessionDefinition { - - /** - * Description of this mail session. - */ - String description() default ""; - - /** - * JNDI name by which the mail session will be registered. - */ - String name(); - - /** - * Store protocol name. - */ - String storeProtocol() default ""; - - /** - * Transport protocol name. - */ - String transportProtocol() default ""; - - /** - * Host name for the mail server. - */ - String host() default ""; - - /** - * User name to use for authentication. - */ - String user() default ""; - - /** - * Password to use for authentication. - */ - String password() default ""; - - /** - * From address for the user. - */ - String from() default ""; - - /** - * Properties to include in the Session. - * Properties are specified using the format: - * propertyName=propertyValue with one property per array element. - */ - String[] properties() default {}; -} diff --git a/src/main/java/javax/mail/MailSessionDefinitions.java b/src/main/java/javax/mail/MailSessionDefinitions.java deleted file mode 100644 index 77ea3e47..00000000 --- a/src/main/java/javax/mail/MailSessionDefinitions.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.lang.annotation.Target; -import java.lang.annotation.Retention; -import java.lang.annotation.ElementType; -import java.lang.annotation.RetentionPolicy; - -/** - * Declares one or more MailSessionDefinition annotations. - * - * @see MailSessionDefinition - * @since JavaMail 1.5 - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface MailSessionDefinitions { - MailSessionDefinition[] value(); -} diff --git a/src/main/java/javax/mail/Message.java b/src/main/java/javax/mail/Message.java deleted file mode 100644 index 1aeda3e7..00000000 --- a/src/main/java/javax/mail/Message.java +++ /dev/null @@ -1,717 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.Vector; -import java.util.Date; -import java.util.Properties; -import java.io.*; -import javax.mail.search.SearchTerm; - -/** - * This class models an email message. This is an abstract class. - * Subclasses provide actual implementations.

      - * - * Message implements the Part interface. Message contains a set of - * attributes and a "content". Messages within a folder also have a - * set of flags that describe its state within the folder.

      - * - * Message defines some new attributes in addition to those defined - * in the Part interface. These attributes specify meta-data - * for the message - i.e., addressing and descriptive information about - * the message.

      - * - * Message objects are obtained either from a Folder or by constructing - * a new Message object of the appropriate subclass. Messages that have - * been received are normally retrieved from a folder named "INBOX".

      - * - * A Message object obtained from a folder is just a lightweight - * reference to the actual message. The Message is 'lazily' filled - * up (on demand) when each item is requested from the message. Note - * that certain folder implementations may return Message objects that - * are pre-filled with certain user-specified items. - - * To send a message, an appropriate subclass of Message (e.g., - * MimeMessage) is instantiated, the attributes and content are - * filled in, and the message is sent using the Transport.send - * method.

      - * - * @author John Mani - * @author Bill Shannon - * @author Max Spivak - * @see javax.mail.Part - */ - -public abstract class Message implements Part { - - /** - * The number of this message within its folder, or zero if - * the message was not retrieved from a folder. - */ - protected int msgnum = 0; - - /** - * True if this message has been expunged. - */ - protected boolean expunged = false; - - /** - * The containing folder, if this message is obtained from a folder - */ - protected Folder folder = null; - - /** - * The Session object for this Message - */ - protected Session session = null; - - /** - * No-arg version of the constructor. - */ - protected Message() { } - - /** - * Constructor that takes a Folder and a message number. - * Used by Folder implementations. - * - * @param folder containing folder - * @param msgnum this message's sequence number within this folder - */ - protected Message(Folder folder, int msgnum) { - this.folder = folder; - this.msgnum = msgnum; - session = folder.store.session; - } - - /** - * Constructor that takes a Session. Used for client created - * Message objects. - * - * @param session A Session object - */ - protected Message(Session session) { - this.session = session; - } - - /** - * Return the Session used when this message was created. - * - * @return the message's Session - * @since JavaMail 1.5 - */ - public Session getSession() { - return session; - } - - /** - * Returns the "From" attribute. The "From" attribute contains - * the identity of the person(s) who wished this message to - * be sent.

      - * - * In certain implementations, this may be different - * from the entity that actually sent the message.

      - * - * This method returns null if this attribute - * is not present in this message. Returns an empty array if - * this attribute is present, but contains no addresses. - * - * @return array of Address objects - * @exception MessagingException - */ - public abstract Address[] getFrom() throws MessagingException; - - /** - * Set the "From" attribute in this Message. The value of this - * attribute is obtained from the property "mail.user". If this - * property is absent, the system property "user.name" is used. - * - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void setFrom() throws MessagingException; - - /** - * Set the "From" attribute in this Message. - * - * @param address the sender - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void setFrom(Address address) - throws MessagingException; - - /** - * Add these addresses to the existing "From" attribute - * - * @param addresses the senders - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public abstract void addFrom(Address[] addresses) - throws MessagingException; - - /** - * This inner class defines the types of recipients allowed by - * the Message class. The currently defined types are TO, - * CC and BCC. - * - * Note that this class only has a protected constructor, thereby - * restricting new Recipient types to either this class or subclasses. - * This effectively implements an enumeration of the allowed Recipient - * types. - * - * The following code sample shows how to use this class to obtain - * the "TO" recipients from a message. - *

      -     *
      -     * Message msg = folder.getMessages(1);
      -     * Address[] a = m.getRecipients(Message.RecipientType.TO);
      -     *
      -     * 

      - * - * @see javax.mail.Message#getRecipients - * @see javax.mail.Message#setRecipients - * @see javax.mail.Message#addRecipients - */ - public static class RecipientType implements Serializable { - /** - * The "To" (primary) recipients. - */ - public static final RecipientType TO = new RecipientType("To"); - /** - * The "Cc" (carbon copy) recipients. - */ - public static final RecipientType CC = new RecipientType("Cc"); - /** - * The "Bcc" (blind carbon copy) recipients. - */ - public static final RecipientType BCC = new RecipientType("Bcc"); - - /** - * The type of recipient, usually the name of a corresponding - * Internet standard header. - * - * @serial - */ - protected String type; - - private static final long serialVersionUID = -7479791750606340008L; - - /** - * Constructor for use by subclasses. - */ - protected RecipientType(String type) { - this.type = type; - } - - /** - * When deserializing a RecipientType, we need to make sure to - * return only one of the known static final instances defined - * in this class. Subclasses must implement their own - * readResolve method that checks for their known - * instances before calling this super method. - */ - protected Object readResolve() throws ObjectStreamException { - if (type.equals("To")) - return TO; - else if (type.equals("Cc")) - return CC; - else if (type.equals("Bcc")) - return BCC; - else - throw new InvalidObjectException( - "Attempt to resolve unknown RecipientType: " + type); - } - - public String toString() { - return type; - } - } - - /** - * Get all the recipient addresses of the given type.

      - * - * This method returns null if no recipients of - * the given type are present in this message. It may return an - * empty array if the header is present, but contains no addresses. - * - * @param type the recipient type - * @return array of Address objects - * @exception MessagingException - * @see Message.RecipientType#TO - * @see Message.RecipientType#CC - * @see Message.RecipientType#BCC - */ - public abstract Address[] getRecipients(RecipientType type) - throws MessagingException; - - /** - * Get all the recipient addresses for the message. - * The default implementation extracts the TO, CC, and BCC - * recipients using the getRecipients method.

      - * - * This method returns null if none of the recipient - * headers are present in this message. It may Return an empty array - * if any recipient header is present, but contains no addresses. - * - * @return array of Address objects - * @exception MessagingException - * @see Message.RecipientType#TO - * @see Message.RecipientType#CC - * @see Message.RecipientType#BCC - * @see #getRecipients - */ - public Address[] getAllRecipients() throws MessagingException { - Address[] to = getRecipients(RecipientType.TO); - Address[] cc = getRecipients(RecipientType.CC); - Address[] bcc = getRecipients(RecipientType.BCC); - - if (cc == null && bcc == null) - return to; // a common case - - int numRecip = - (to != null ? to.length : 0) + - (cc != null ? cc.length : 0) + - (bcc != null ? bcc.length : 0); - Address[] addresses = new Address[numRecip]; - int pos = 0; - if (to != null) { - System.arraycopy(to, 0, addresses, pos, to.length); - pos += to.length; - } - if (cc != null) { - System.arraycopy(cc, 0, addresses, pos, cc.length); - pos += cc.length; - } - if (bcc != null) { - System.arraycopy(bcc, 0, addresses, pos, bcc.length); - pos += bcc.length; - } - return addresses; - } - - /** - * Set the recipient addresses. All addresses of the specified - * type are replaced by the addresses parameter. - * - * @param type the recipient type - * @param addresses the addresses - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void setRecipients(RecipientType type, Address[] addresses) - throws MessagingException; - - /** - * Set the recipient address. All addresses of the specified - * type are replaced by the address parameter.

      - * - * The default implementation uses the setRecipients method. - * - * @param type the recipient type - * @param address the address - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public void setRecipient(RecipientType type, Address address) - throws MessagingException { - Address[] a = new Address[1]; - a[0] = address; - setRecipients(type, a); - } - - /** - * Add these recipient addresses to the existing ones of the given type. - * - * @param type the recipient type - * @param addresses the addresses - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void addRecipients(RecipientType type, Address[] addresses) - throws MessagingException; - - /** - * Add this recipient address to the existing ones of the given type.

      - * - * The default implementation uses the addRecipients method. - * - * @param type the recipient type - * @param address the address - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public void addRecipient(RecipientType type, Address address) - throws MessagingException { - Address[] a = new Address[1]; - a[0] = address; - addRecipients(type, a); - } - - /** - * Get the addresses to which replies should be directed. - * This will usually be the sender of the message, but - * some messages may direct replies to a different address.

      - * - * The default implementation simply calls the getFrom - * method.

      - * - * This method returns null if the corresponding - * header is not present. Returns an empty array if the header - * is present, but contains no addresses. - * - * @return addresses to which replies should be directed - * @exception MessagingException - * @see #getFrom - */ - public Address[] getReplyTo() throws MessagingException { - return getFrom(); - } - - /** - * Set the addresses to which replies should be directed. - * (Normally only a single address will be specified.) - * Not all message types allow this to be specified separately - * from the sender of the message.

      - * - * The default implementation provided here just throws the - * MethodNotSupportedException. - * - * @param addresses addresses to which replies should be directed - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MethodNotSupportedException if the underlying - * implementation does not support setting this - * attribute - */ - public void setReplyTo(Address[] addresses) throws MessagingException { - throw new MethodNotSupportedException("setReplyTo not supported"); - } - - /** - * Get the subject of this message. - * - * @return the subject - * @exception MessagingException - */ - public abstract String getSubject() throws MessagingException; - - /** - * Set the subject of this message. - * - * @param subject the subject - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void setSubject(String subject) - throws MessagingException; - - /** - * Get the date this message was sent. - * - * @return the date this message was sent - * @exception MessagingException - */ - public abstract Date getSentDate() throws MessagingException; - - /** - * Set the sent date of this message. - * - * @param date the sent date of this message - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - */ - public abstract void setSentDate(Date date) throws MessagingException; - - /** - * Get the date this message was received. - * - * @return the date this message was received - * @exception MessagingException - */ - public abstract Date getReceivedDate() throws MessagingException; - - /** - * Returns a Flags object containing the flags for - * this message.

      - * - * Modifying any of the flags in this returned Flags object will - * not affect the flags of this message. Use setFlags() - * to do that.

      - * - * @return Flags object containing the flags for this message - * @see javax.mail.Flags - * @see #setFlags - * @exception MessagingException - */ - public abstract Flags getFlags() throws MessagingException; - - /** - * Check whether the flag specified in the flag - * argument is set in this message.

      - * - * The default implementation uses getFlags. - * - * @param flag the flag - * @return value of the specified flag for this message - * @see javax.mail.Flags.Flag - * @see javax.mail.Flags.Flag#ANSWERED - * @see javax.mail.Flags.Flag#DELETED - * @see javax.mail.Flags.Flag#DRAFT - * @see javax.mail.Flags.Flag#FLAGGED - * @see javax.mail.Flags.Flag#RECENT - * @see javax.mail.Flags.Flag#SEEN - * @exception MessagingException - */ - public boolean isSet(Flags.Flag flag) throws MessagingException { - return getFlags().contains(flag); - } - - /** - * Set the specified flags on this message to the specified value. - * Note that any flags in this message that are not specified in - * the given Flags object are unaffected.

      - * - * This will result in a MessageChangedEvent being - * delivered to any MessageChangedListener registered on this - * Message's containing folder. - * - * @param flag Flags object containing the flags to be set - * @param set the value to be set - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values. - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @see javax.mail.event.MessageChangedEvent - */ - public abstract void setFlags(Flags flag, boolean set) - throws MessagingException; - - /** - * Set the specified flag on this message to the specified value. - * - * This will result in a MessageChangedEvent being - * delivered to any MessageChangedListener registered on this - * Message's containing folder.

      - * - * The default implementation uses the setFlags method. - * - * @param flag Flags.Flag object containing the flag to be set - * @param set the value to be set - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values. - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @see javax.mail.event.MessageChangedEvent - */ - public void setFlag(Flags.Flag flag, boolean set) - throws MessagingException { - Flags f = new Flags(flag); - setFlags(f, set); - } - - /** - * Get the Message number for this Message. - * A Message object's message number is the relative - * position of this Message in its Folder. Note that the message - * number for a particular Message can change during a session - * if other messages in the Folder are deleted and expunged.

      - * - * Valid message numbers start at 1. Messages that do not belong - * to any folder (like newly composed or derived messages) have 0 - * as their message number. - * - * @return the message number - */ - public int getMessageNumber() { - return msgnum; - } - - /** - * Set the Message number for this Message. This method is - * invoked only by the implementation classes. - */ - protected void setMessageNumber(int msgnum) { - this.msgnum = msgnum; - } - - /** - * Get the folder from which this message was obtained. If - * this is a new message or nested message, this method returns - * null. - * - * @return the containing folder - */ - public Folder getFolder() { - return folder; - } - - /** - * Checks whether this message is expunged. All other methods except - * getMessageNumber() are invalid on an expunged - * Message object.

      - * - * Messages that are expunged due to an explict expunge() - * request on the containing Folder are removed from the Folder - * immediately. Messages that are externally expunged by another source - * are marked "expunged" and return true for the isExpunged() method, - * but they are not removed from the Folder until an explicit - * expunge() is done on the Folder.

      - * - * See the description of expunge() for more details on - * expunge handling. - * - * @see Folder#expunge - */ - public boolean isExpunged() { - return expunged; - } - - /** - * Sets the expunged flag for this Message. This method is to - * be used only by the implementation classes. - * - * @param expunged the expunged flag - */ - protected void setExpunged(boolean expunged) { - this.expunged = expunged; - } - - /** - * Get a new Message suitable for a reply to this message. - * The new Message will have its attributes and headers - * set up appropriately. Note that this new message object - * will be empty, that is, it will not have a "content". - * These will have to be suitably filled in by the client.

      - * - * If replyToAll is set, the new Message will be addressed - * to all recipients of this message. Otherwise, the reply will be - * addressed to only the sender of this message (using the value - * of the getReplyTo method).

      - * - * The "Subject" field is filled in with the original subject - * prefixed with "Re:" (unless it already starts with "Re:").

      - * - * The reply message will use the same session as this message. - * - * @param replyToAll reply should be sent to all recipients - * of this message - * @return the reply Message - * @exception MessagingException - */ - public abstract Message reply(boolean replyToAll) throws MessagingException; - - /** - * Save any changes made to this message into the message-store - * when the containing folder is closed, if the message is contained - * in a folder. (Some implementations may save the changes - * immediately.) Update any header fields to be consistent with the - * changed message contents. If any part of a message's headers or - * contents are changed, saveChanges must be called to ensure that - * those changes are permanent. If saveChanges is not called, any - * such modifications may or may not be saved, depending on the - * message store and folder implementation.

      - * - * Messages obtained from folders opened READ_ONLY should not be - * modified and saveChanges should not be called on such messages. - * - * @exception MessagingException - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values. - */ - public abstract void saveChanges() throws MessagingException; - - /** - * Apply the specified Search criterion to this message. - * - * @param term the Search criterion - * @return true if the Message matches this search - * criterion, false otherwise. - * @exception MessagingException - * @see javax.mail.search.SearchTerm - */ - public boolean match(SearchTerm term) throws MessagingException { - return term.match(this); - } -} diff --git a/src/main/java/javax/mail/MessageAware.java b/src/main/java/javax/mail/MessageAware.java deleted file mode 100644 index 17e02d6c..00000000 --- a/src/main/java/javax/mail/MessageAware.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * An interface optionally implemented by DataSources to - * supply information to a DataContentHandler about the - * message context in which the data content object is operating. - * - * @see javax.mail.MessageContext - * @see javax.activation.DataSource - * @see javax.activation.DataContentHandler - * @since JavaMail 1.1 - */ -public interface MessageAware { - /** - * Return the message context. - */ - public MessageContext getMessageContext(); -} diff --git a/src/main/java/javax/mail/MessageContext.java b/src/main/java/javax/mail/MessageContext.java deleted file mode 100644 index 8abe7f6b..00000000 --- a/src/main/java/javax/mail/MessageContext.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * The context in which a piece of Message content is contained. A - * MessageContext object is returned by the - * getMessageContext method of the - * MessageAware interface. MessageAware is - * typically implemented by DataSources to allow a - * DataContentHandler to pass on information about the - * context in which a data content object is operating. - * - * @see javax.mail.MessageAware - * @see javax.activation.DataSource - * @see javax.activation.DataContentHandler - * @since JavaMail 1.1 - */ -public class MessageContext { - private Part part; - - /** - * Create a MessageContext object describing the context of the given Part. - */ - public MessageContext(Part part) { - this.part = part; - } - - /** - * Return the Part that contains the content. - * - * @return the containing Part, or null if not known - */ - public Part getPart() { - return part; - } - - /** - * Return the Message that contains the content. - * Follows the parent chain up through containing Multipart - * objects until it comes to a Message object, or null. - * - * @return the containing Message, or null if not known - */ - public Message getMessage() { - try { - return getMessage(part); - } catch (MessagingException ex) { - return null; - } - } - - /** - * Return the Message containing an arbitrary Part. - * Follows the parent chain up through containing Multipart - * objects until it comes to a Message object, or null. - * - * @return the containing Message, or null if none - * @see javax.mail.BodyPart#getParent - * @see javax.mail.Multipart#getParent - */ - private static Message getMessage(Part p) throws MessagingException { - while (p != null) { - if (p instanceof Message) - return (Message)p; - BodyPart bp = (BodyPart)p; - Multipart mp = bp.getParent(); - if (mp == null) // MimeBodyPart might not be in a MimeMultipart - return null; - p = mp.getParent(); - } - return null; - } - - /** - * Return the Session we're operating in. - * - * @return the Session, or null if not known - */ - public Session getSession() { - Message msg = getMessage(); - return msg != null ? msg.getSession() : null; - } -} diff --git a/src/main/java/javax/mail/MessageRemovedException.java b/src/main/java/javax/mail/MessageRemovedException.java deleted file mode 100644 index 90f4e816..00000000 --- a/src/main/java/javax/mail/MessageRemovedException.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * The exception thrown when an invalid method is invoked on an expunged - * Message. The only valid methods on an expunged Message are - * isExpunged() and getMessageNumber(). - * - * @see javax.mail.Message#isExpunged() - * @see javax.mail.Message#getMessageNumber() - * @author John Mani - */ - -public class MessageRemovedException extends MessagingException { - - private static final long serialVersionUID = 1951292550679528690L; - - /** - * Constructs a MessageRemovedException with no detail message. - */ - public MessageRemovedException() { - super(); - } - - /** - * Constructs a MessageRemovedException with the specified - * detail message. - * - * @param s The detailed error message - */ - public MessageRemovedException(String s) { - super(s); - } - - /** - * Constructs a MessageRemovedException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param s The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public MessageRemovedException(String s, Exception e) { - super(s, e); - } -} diff --git a/src/main/java/javax/mail/MessagingException.java b/src/main/java/javax/mail/MessagingException.java deleted file mode 100644 index 2055c319..00000000 --- a/src/main/java/javax/mail/MessagingException.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.lang.*; - -/** - * The base class for all exceptions thrown by the Messaging classes - * - * @author John Mani - * @author Bill Shannon - */ - -public class MessagingException extends Exception { - - /** - * The next exception in the chain. - * - * @serial - */ - private Exception next; - - private static final long serialVersionUID = -7569192289819959253L; - - /** - * Constructs a MessagingException with no detail message. - */ - public MessagingException() { - super(); - initCause(null); // prevent anyone else from setting it - } - - /** - * Constructs a MessagingException with the specified detail message. - * - * @param s the detail message - */ - public MessagingException(String s) { - super(s); - initCause(null); // prevent anyone else from setting it - } - - /** - * Constructs a MessagingException with the specified - * Exception and detail message. The specified exception is chained - * to this exception. - * - * @param s the detail message - * @param e the embedded exception - * @see #getNextException - * @see #setNextException - * @see #getCause - */ - public MessagingException(String s, Exception e) { - super(s); - next = e; - initCause(null); // prevent anyone else from setting it - } - - /** - * Get the next exception chained to this one. If the - * next exception is a MessagingException, the chain - * may extend further. - * - * @return next Exception, null if none. - */ - public synchronized Exception getNextException() { - return next; - } - - /** - * Overrides the getCause method of Throwable - * to return the next exception in the chain of nested exceptions. - * - * @return next Exception, null if none. - */ - public synchronized Throwable getCause() { - return next; - } - - /** - * Add an exception to the end of the chain. If the end - * is not a MessagingException, this - * exception cannot be added to the end. - * - * @param ex the new end of the Exception chain - * @return true if this Exception - * was added, false otherwise. - */ - public synchronized boolean setNextException(Exception ex) { - Exception theEnd = this; - while (theEnd instanceof MessagingException && - ((MessagingException)theEnd).next != null) { - theEnd = ((MessagingException)theEnd).next; - } - // If the end is a MessagingException, we can add this - // exception to the chain. - if (theEnd instanceof MessagingException) { - ((MessagingException)theEnd).next = ex; - return true; - } else - return false; - } - - /** - * Override toString method to provide information on - * nested exceptions. - */ - public synchronized String toString() { - String s = super.toString(); - Exception n = next; - if (n == null) - return s; - StringBuffer sb = new StringBuffer(s == null ? "" : s); - while (n != null) { - sb.append(";\n nested exception is:\n\t"); - if (n instanceof MessagingException) { - MessagingException mex = (MessagingException)n; - sb.append(mex.superToString()); - n = mex.next; - } else { - sb.append(n.toString()); - n = null; - } - } - return sb.toString(); - } - - /** - * Return the "toString" information for this exception, - * without any information on nested exceptions. - */ - private final String superToString() { - return super.toString(); - } -} diff --git a/src/main/java/javax/mail/MethodNotSupportedException.java b/src/main/java/javax/mail/MethodNotSupportedException.java deleted file mode 100644 index f23b9371..00000000 --- a/src/main/java/javax/mail/MethodNotSupportedException.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - - -/** - * The exception thrown when a method is not supported by the - * implementation - * - * @author John Mani - */ - -public class MethodNotSupportedException extends MessagingException { - - private static final long serialVersionUID = -3757386618726131322L; - - /** - * Constructs a MethodNotSupportedException with no detail message. - */ - public MethodNotSupportedException() { - super(); - } - - /** - * Constructs a MethodNotSupportedException with the specified - * detail message. - * - * @param s The detailed error message - */ - public MethodNotSupportedException(String s) { - super(s); - } - - /** - * Constructs a MethodNotSupportedException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param s The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public MethodNotSupportedException(String s, Exception e) { - super(s, e); - } -} diff --git a/src/main/java/javax/mail/Multipart.java b/src/main/java/javax/mail/Multipart.java deleted file mode 100644 index 332ea602..00000000 --- a/src/main/java/javax/mail/Multipart.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.Vector; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; -import javax.activation.DataSource; - -/** - * Multipart is a container that holds multiple body parts. Multipart - * provides methods to retrieve and set its subparts.

      - * - * Multipart also acts as the base class for the content object returned - * by most Multipart DataContentHandlers. For example, invoking getContent() - * on a DataHandler whose source is a "multipart/signed" data source may - * return an appropriate subclass of Multipart.

      - * - * Some messaging systems provide different subtypes of Multiparts. For - * example, MIME specifies a set of subtypes that include "alternative", - * "mixed", "related", "parallel", "signed", etc.

      - * - * Multipart is an abstract class. Subclasses provide actual implementations. - * - * @author John Mani - */ - -public abstract class Multipart { - - /** - * Vector of BodyPart objects. - */ - protected Vector parts = new Vector(); // Holds BodyParts - - /** - * This field specifies the content-type of this multipart - * object. It defaults to "multipart/mixed". - */ - protected String contentType = "multipart/mixed"; // Content-Type - - /** - * The Part containing this Multipart, - * if known. - * @since JavaMail 1.1 - */ - protected Part parent; - - /** - * Default constructor. An empty Multipart object is created. - */ - protected Multipart() { } - - /** - * Setup this Multipart object from the given MultipartDataSource.

      - * - * The method adds the MultipartDataSource's BodyPart - * objects into this Multipart. This Multipart's contentType is - * set to that of the MultipartDataSource.

      - * - * This method is typically used in those cases where one - * has a multipart data source that has already been pre-parsed into - * the individual body parts (for example, an IMAP datasource), but - * needs to create an appropriate Multipart subclass that represents - * a specific multipart subtype. - * - * @param mp Multipart datasource - */ - protected synchronized void setMultipartDataSource(MultipartDataSource mp) - throws MessagingException { - contentType = mp.getContentType(); - - int count = mp.getCount(); - for (int i = 0; i < count; i++) - addBodyPart(mp.getBodyPart(i)); - } - - /** - * Return the content-type of this Multipart.

      - * - * This implementation just returns the value of the - * contentType field. - * - * @return content-type - * @see #contentType - */ - public synchronized String getContentType() { - return contentType; - } - - /** - * Return the number of enclosed BodyPart objects.

      - * - * @return number of parts - * @see #parts - */ - public synchronized int getCount() throws MessagingException { - if (parts == null) - return 0; - - return parts.size(); - } - - /** - * Get the specified Part. Parts are numbered starting at 0. - * - * @param index the index of the desired Part - * @return the Part - * @exception IndexOutOfBoundsException if the given index - * is out of range. - * @exception MessagingException - */ - public synchronized BodyPart getBodyPart(int index) - throws MessagingException { - if (parts == null) - throw new IndexOutOfBoundsException("No such BodyPart"); - - return (BodyPart)parts.elementAt(index); - } - - /** - * Remove the specified part from the multipart message. - * Shifts all the parts after the removed part down one. - * - * @param part The part to remove - * @return true if part removed, false otherwise - * @exception MessagingException if no such Part exists - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized boolean removeBodyPart(BodyPart part) - throws MessagingException { - if (parts == null) - throw new MessagingException("No such body part"); - - boolean ret = parts.removeElement(part); - part.setParent(null); - return ret; - } - - /** - * Remove the part at specified location (starting from 0). - * Shifts all the parts after the removed part down one. - * - * @param index Index of the part to remove - * @exception MessagingException - * @exception IndexOutOfBoundsException if the given index - * is out of range. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized void removeBodyPart(int index) - throws MessagingException { - if (parts == null) - throw new IndexOutOfBoundsException("No such BodyPart"); - - BodyPart part = (BodyPart)parts.elementAt(index); - parts.removeElementAt(index); - part.setParent(null); - } - - /** - * Adds a Part to the multipart. The BodyPart is appended to - * the list of existing Parts. - * - * @param part The Part to be appended - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized void addBodyPart(BodyPart part) - throws MessagingException { - if (parts == null) - parts = new Vector(); - - parts.addElement(part); - part.setParent(this); - } - - /** - * Adds a BodyPart at position index. - * If index is not the last one in the list, - * the subsequent parts are shifted up. If index - * is larger than the number of parts present, the - * BodyPart is appended to the end. - * - * @param part The BodyPart to be inserted - * @param index Location where to insert the part - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized void addBodyPart(BodyPart part, int index) - throws MessagingException { - if (parts == null) - parts = new Vector(); - - parts.insertElementAt(part, index); - part.setParent(this); - } - - /** - * Output an appropriately encoded bytestream to the given - * OutputStream. The implementation subclass decides the - * appropriate encoding algorithm to be used. The bytestream - * is typically used for sending. - * - * @exception IOException if an IO related exception occurs - * @exception MessagingException - */ - public abstract void writeTo(OutputStream os) - throws IOException, MessagingException; - - /** - * Return the Part that contains this Multipart - * object, or null if not known. - * @since JavaMail 1.1 - */ - public synchronized Part getParent() { - return parent; - } - - /** - * Set the parent of this Multipart to be the specified - * Part. Normally called by the Message - * or BodyPart setContent(Multipart) method. - * parent may be null if the - * Multipart is being removed from its containing - * Part. - * @since JavaMail 1.1 - */ - public synchronized void setParent(Part parent) { - this.parent = parent; - } -} diff --git a/src/main/java/javax/mail/MultipartDataSource.java b/src/main/java/javax/mail/MultipartDataSource.java deleted file mode 100644 index 6ead6be4..00000000 --- a/src/main/java/javax/mail/MultipartDataSource.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.Vector; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.IOException; -import javax.activation.DataSource; - -/** - * MultipartDataSource is a DataSource that contains body - * parts. This allows "mail aware" DataContentHandlers to - * be implemented more efficiently by being aware of such - * DataSources and using the appropriate methods to access - * BodyParts.

      - * - * Note that the data of a MultipartDataSource is also available as - * an input stream.

      - * - * This interface will typically be implemented by providers that - * preparse multipart bodies, for example an IMAP provider. - * - * @author John Mani - * @see javax.activation.DataSource - */ - -public interface MultipartDataSource extends DataSource { - - /** - * Return the number of enclosed BodyPart objects. - * - * @return number of parts - */ - public int getCount(); - - /** - * Get the specified Part. Parts are numbered starting at 0. - * - * @param index the index of the desired Part - * @return the Part - * @exception IndexOutOfBoundsException if the given index - * is out of range. - * @exception MessagingException - */ - public BodyPart getBodyPart(int index) throws MessagingException; - -} diff --git a/src/main/java/javax/mail/NoSuchProviderException.java b/src/main/java/javax/mail/NoSuchProviderException.java deleted file mode 100644 index b453c58e..00000000 --- a/src/main/java/javax/mail/NoSuchProviderException.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when Session attempts to instantiate a - * Provider that doesn't exist. - * - * @author Max Spivak - */ - -public class NoSuchProviderException extends MessagingException { - - private static final long serialVersionUID = 8058319293154708827L; - - /** - * Constructs a NoSuchProviderException with no detail message. - */ - public NoSuchProviderException() { - super(); - } - - /** - * Constructs a NoSuchProviderException with the specified - * detail message. - * - * @param message The detailed error message - */ - public NoSuchProviderException(String message) { - super(message); - } - - /** - * Constructs a NoSuchProviderException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param message The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public NoSuchProviderException(String message, Exception e) { - super(message, e); - } -} diff --git a/src/main/java/javax/mail/Part.java b/src/main/java/javax/mail/Part.java deleted file mode 100644 index 34b6aa25..00000000 --- a/src/main/java/javax/mail/Part.java +++ /dev/null @@ -1,463 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.*; -import java.util.Enumeration; -import javax.activation.DataHandler; - -/** - * The Part interface is the common base interface for - * Messages and BodyParts.

      - * - * Part consists of a set of attributes and a "Content".

      - * - * Attributes:

      - * - * The JavaMail API defines a set of standard Part attributes that are - * considered to be common to most existing Mail systems. These - * attributes have their own settor and gettor methods. Mail systems - * may support other Part attributes as well, these are represented as - * name-value pairs where both the name and value are Strings.

      - * - * Content:

      - * - * The data type of the "content" is returned by - * the getContentType() method. The MIME typing system - * is used to name data types.

      - * - * The "content" of a Part is available in various formats: - *

        - *
      • As a DataHandler - using the getDataHandler() method. - * The "content" of a Part is also available through a - * javax.activation.DataHandler object. The DataHandler - * object allows clients to discover the operations available on the - * content, and to instantiate the appropriate component to perform - * those operations. - * - *
      • As an input stream - using the getInputStream() method. - * Any mail-specific encodings are decoded before this stream is returned. - * - *
      • As a Java object - using the getContent() method. - * This method returns the "content" as a Java object. - * The returned object is of course dependent on the content - * itself. In particular, a "multipart" Part's content is always a - * Multipart or subclass thereof. That is, getContent() on a - * "multipart" type Part will always return a Multipart (or subclass) object. - *
      - * - * Part provides the writeTo() method that streams - * out its bytestream in mail-safe form suitable for transmission. - * This bytestream is typically an aggregation of the Part attributes - * and its content's bytestream.

      - * - * Message and BodyPart implement the Part interface. Note that in - * MIME parlance, Part models an Entity (RFC 2045, Section 2.4). - * - * @author John Mani - */ - -public interface Part { - - /** - * Return the size of the content of this part in bytes. - * Return -1 if the size cannot be determined.

      - * - * Note that the size may not be an exact measure of the content - * size and may or may not account for any transfer encoding - * of the content. The size is appropriate for display in a - * user interface to give the user a rough idea of the size - * of this part. - * - * @return size of content in bytes - * @exception MessagingException - */ - public int getSize() throws MessagingException; - - /** - * Return the number of lines in the content of this part. - * Return -1 if the number cannot be determined. - * - * Note that this number may not be an exact measure of the - * content length and may or may not account for any transfer - * encoding of the content. - * - * @return number of lines in the content. - * @exception MessagingException - */ - public int getLineCount() throws MessagingException; - - /** - * Returns the Content-Type of the content of this part. - * Returns null if the Content-Type could not be determined.

      - * - * The MIME typing system is used to name Content-types. - * - * @return The ContentType of this part - * @exception MessagingException - * @see javax.activation.DataHandler - */ - public String getContentType() throws MessagingException; - - /** - * Is this Part of the specified MIME type? This method - * compares only the primaryType and - * subType. - * The parameters of the content types are ignored.

      - * - * For example, this method will return true when - * comparing a Part of content type "text/plain" - * with "text/plain; charset=foobar".

      - * - * If the subType of mimeType is the - * special character '*', then the subtype is ignored during the - * comparison. - */ - public boolean isMimeType(String mimeType) throws MessagingException; - - /** - * This part should be presented as an attachment. - * @see #getDisposition - * @see #setDisposition - */ - public static final String ATTACHMENT = "attachment"; - - /** - * This part should be presented inline. - * @see #getDisposition - * @see #setDisposition - */ - public static final String INLINE = "inline"; - - /** - * Return the disposition of this part. The disposition - * describes how the part should be presented to the user. - * (See RFC 2183.) The return value should be considered - * without regard to case. For example:

      - *

      -     * String disp = part.getDisposition();
      -     * if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT))
      -     *	// treat as attachment if not first part
      -     * 
      - * - * @return disposition of this part, or null if unknown - * @exception MessagingException - * @see #ATTACHMENT - * @see #INLINE - * @see #getFileName - */ - public String getDisposition() throws MessagingException; - - /** - * Set the disposition of this part. - * - * @param disposition disposition of this part - * @exception MessagingException - * @exception IllegalWriteException if the underlying implementation - * does not support modification of this header - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - * @see #ATTACHMENT - * @see #INLINE - * @see #setFileName - */ - public void setDisposition(String disposition) throws MessagingException; - - /** - * Return a description String for this part. This typically - * associates some descriptive information with this part. - * Returns null if none is available. - * - * @return description of this part - * @exception MessagingException - */ - public String getDescription() throws MessagingException; - - /** - * Set a description String for this part. This typically - * associates some descriptive information with this part. - * - * @param description description of this part - * @exception MessagingException - * @exception IllegalWriteException if the underlying implementation - * does not support modification of this header - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setDescription(String description) throws MessagingException; - - /** - * Get the filename associated with this part, if possible. - * Useful if this part represents an "attachment" that was - * loaded from a file. The filename will usually be a simple - * name, not including directory components. - * - * @return Filename to associate with this part - */ - public String getFileName() throws MessagingException; - - /** - * Set the filename associated with this part, if possible. - * Useful if this part represents an "attachment" that was - * loaded from a file. The filename will usually be a simple - * name, not including directory components. - * - * @param filename Filename to associate with this part - * @exception IllegalWriteException if the underlying implementation - * does not support modification of this header - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setFileName(String filename) throws MessagingException; - - /** - * Return an input stream for this part's "content". Any - * mail-specific transfer encodings will be decoded before the - * input stream is provided.

      - * - * This is typically a convenience method that just invokes - * the DataHandler's getInputStream() method. - * - * @return an InputStream - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - * @exception MessagingException - * @see #getDataHandler - * @see javax.activation.DataHandler#getInputStream - */ - public InputStream getInputStream() - throws IOException, MessagingException; - - /** - * Return a DataHandler for the content within this part. The - * DataHandler allows clients to operate on as well as retrieve - * the content. - * - * @return DataHandler for the content - * @exception MessagingException - */ - public DataHandler getDataHandler() throws MessagingException; - - /** - * Return the content as a Java object. The type of the returned - * object is of course dependent on the content itself. For example, - * the object returned for "text/plain" content is usually a String - * object. The object returned for a "multipart" content is always a - * Multipart subclass. For content-types that are unknown to the - * DataHandler system, an input stream is returned as the content

      - * - * This is a convenience method that just invokes the DataHandler's - * getContent() method - * - * @return Object - * @exception MessagingException - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - * - * @see javax.activation.DataHandler#getContent - */ - public Object getContent() throws IOException, MessagingException; - - /** - * This method provides the mechanism to set this part's content. - * The DataHandler wraps around the actual content. - * - * @param dh The DataHandler for the content. - * @exception MessagingException - * @exception IllegalWriteException if the underlying implementation - * does not support modification of existing values - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setDataHandler(DataHandler dh) throws MessagingException; - - /** - * A convenience method for setting this part's content. The part - * internally wraps the content in a DataHandler.

      - * - * Note that a DataContentHandler class for the specified type should - * be available to the JavaMail implementation for this to work right. - * i.e., to do setContent(foobar, "application/x-foobar"), - * a DataContentHandler for "application/x-foobar" should be installed. - * Refer to the Java Activation Framework for more information. - * - * @param obj A java object. - * @param type MIME type of this object. - * @exception IllegalWriteException if the underlying implementation - * does not support modification of existing values - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setContent(Object obj, String type) - throws MessagingException; - - /** - * A convenience method that sets the given String as this - * part's content with a MIME type of "text/plain". - * - * @param text The text that is the Message's content. - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setText(String text) throws MessagingException; - - /** - * This method sets the given Multipart object as this message's - * content. - * - * @param mp The multipart object that is the Message's content - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values - * @exception IllegalStateException if this Part is obtained - * from a READ_ONLY folder - */ - public void setContent(Multipart mp) throws MessagingException; - - /** - * Output a bytestream for this Part. This bytestream is - * typically an aggregration of the Part attributes and - * an appropriately encoded bytestream from its 'content'.

      - * - * Classes that implement the Part interface decide on - * the appropriate encoding algorithm to be used.

      - * - * The bytestream is typically used for sending. - * - * @exception IOException if an error occurs writing to the - * stream or if an error is generated - * by the javax.activation layer. - * @exception MessagingException if an error occurs fetching the - * data to be written - * - * @see javax.activation.DataHandler#writeTo - */ - public void writeTo(OutputStream os) throws IOException, MessagingException; - - /** - * Get all the headers for this header name. Returns null - * if no headers for this header name are available. - * - * @param header_name the name of this header - * @return the value fields for all headers with - * this name - * @exception MessagingException - */ - public String[] getHeader(String header_name) - throws MessagingException; - - /** - * Set the value for this header_name. Replaces all existing - * header values with this new value. - * - * @param header_name the name of this header - * @param header_value the value for this header - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void setHeader(String header_name, String header_value) - throws MessagingException; - /** - * Add this value to the existing values for this header_name. - * - * @param header_name the name of this header - * @param header_value the value for this header - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void addHeader(String header_name, String header_value) - throws MessagingException; - /** - * Remove all headers with this name. - * - * @param header_name the name of this header - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void removeHeader(String header_name) - throws MessagingException; - - /** - * Return all the headers from this part as an Enumeration of - * Header objects. - * - * @return enumeration of Header objects - * @exception MessagingException - */ - public Enumeration getAllHeaders() throws MessagingException; - - /** - * Return matching headers from this part as an Enumeration of - * Header objects. - * - * @return enumeration of Header objects - * @exception MessagingException - */ - public Enumeration getMatchingHeaders(String[] header_names) - throws MessagingException; - - /** - * Return non-matching headers from this envelope as an Enumeration - * of Header objects. - * - * @return enumeration of Header objects - * @exception MessagingException - */ - public Enumeration getNonMatchingHeaders(String[] header_names) - throws MessagingException; -} diff --git a/src/main/java/javax/mail/PasswordAuthentication.java b/src/main/java/javax/mail/PasswordAuthentication.java deleted file mode 100644 index 1a358a3f..00000000 --- a/src/main/java/javax/mail/PasswordAuthentication.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - - -/** - * The class PasswordAuthentication is a data holder that is used by - * Authenticator. It is simply a repository for a user name and a password. - * - * @see java.net.PasswordAuthentication - * @see javax.mail.Authenticator - * @see javax.mail.Authenticator#getPasswordAuthentication() - * - * @author Bill Foote - */ - -public final class PasswordAuthentication { - - private final String userName; - private final String password; - - /** - * Initialize a new PasswordAuthentication - * @param userName the user name - * @param password The user's password - */ - public PasswordAuthentication(String userName, String password) { - this.userName = userName; - this.password = password; - } - - /** - * @return the user name - */ - public String getUserName() { - return userName; - } - - /** - * @return the password - */ - public String getPassword() { - return password; - } -} diff --git a/src/main/java/javax/mail/Provider.java b/src/main/java/javax/mail/Provider.java deleted file mode 100644 index d6168a92..00000000 --- a/src/main/java/javax/mail/Provider.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * The Provider is a class that describes a protocol - * implementation. The values typically come from the - * javamail.providers and javamail.default.providers - * resource files. An application may also create and - * register a Provider object to dynamically add support - * for a new provider. - * - * @author Max Spivak - * @author Bill Shannon - */ -public class Provider { - - /** - * This inner class defines the Provider type. - * Currently, STORE and TRANSPORT are the only two provider types - * supported. - */ - - public static class Type { - public static final Type STORE = new Type("STORE"); - public static final Type TRANSPORT = new Type("TRANSPORT"); - - private String type; - - private Type(String type) { - this.type = type; - } - - public String toString() { - return type; - } - } - - private Type type; - private String protocol, className, vendor, version; - - /** - * Create a new provider of the specified type for the specified - * protocol. The specified class implements the provider. - * - * @param type Type.STORE or Type.TRANSPORT - * @param protocol valid protocol for the type - * @param classname class name that implements this protocol - * @param vendor optional string identifying the vendor (may be null) - * @param version optional implementation version string (may be null) - * @since JavaMail 1.4 - */ - public Provider(Type type, String protocol, String classname, - String vendor, String version) { - this.type = type; - this.protocol = protocol; - this.className = classname; - this.vendor = vendor; - this.version = version; - } - - /** Returns the type of this Provider */ - public Type getType() { - return type; - } - - /** Returns the protocol supported by this Provider */ - public String getProtocol() { - return protocol; - } - - /** Returns name of the class that implements the protocol */ - public String getClassName() { - return className; - } - - /** Returns name of vendor associated with this implementation or null */ - public String getVendor() { - return vendor; - } - - /** Returns version of this implementation or null if no version */ - public String getVersion() { - return version; - } - - /** Overrides Object.toString() */ - public String toString() { - String s = "javax.mail.Provider[" + type + "," + - protocol + "," + className; - - if (vendor != null) - s += "," + vendor; - - if (version != null) - s += "," + version; - - s += "]"; - return s; - } -} diff --git a/src/main/java/javax/mail/Quota.java b/src/main/java/javax/mail/Quota.java deleted file mode 100644 index 27a56366..00000000 --- a/src/main/java/javax/mail/Quota.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.Vector; - -/** - * This class represents a set of quotas for a given quota root. - * Each quota root has a set of resources, represented by the - * Quota.Resource class. Each resource has a name - * (for example, "STORAGE"), a current usage, and a usage limit. - * See RFC 2087. - * - * @since JavaMail 1.4 - * @author Bill Shannon - */ - -public class Quota { - - /** - * An individual resource in a quota root. - * - * @since JavaMail 1.4 - */ - public static class Resource { - /** The name of the resource. */ - public String name; - /** The current usage of the resource. */ - public long usage; - /** The usage limit for the resource. */ - public long limit; - - /** - * Construct a Resource object with the given name, - * usage, and limit. - * - * @param name the resource name - * @param usage the current usage of the resource - * @param limit the usage limit for the resource - */ - public Resource(String name, long usage, long limit) { - this.name = name; - this.usage = usage; - this.limit = limit; - } - } - - /** - * The name of the quota root. - */ - public String quotaRoot; - - /** - * The set of resources associated with this quota root. - */ - public Quota.Resource[] resources; - - /** - * Create a Quota object for the named quotaroot with no associated - * resources. - * - * @param quotaRoot the name of the quota root - */ - public Quota(String quotaRoot) { - this.quotaRoot = quotaRoot; - } - - /** - * Set a resource limit for this quota root. - * - * @param name the name of the resource - * @param limit the resource limit - */ - public void setResourceLimit(String name, long limit) { - if (resources == null) { - resources = new Quota.Resource[1]; - resources[0] = new Quota.Resource(name, 0, limit); - return; - } - for (int i = 0; i < resources.length; i++) { - if (resources[i].name.equalsIgnoreCase(name)) { - resources[i].limit = limit; - return; - } - } - Quota.Resource[] ra = new Quota.Resource[resources.length + 1]; - System.arraycopy(resources, 0, ra, 0, resources.length); - ra[ra.length - 1] = new Quota.Resource(name, 0, limit); - resources = ra; - } -} diff --git a/src/main/java/javax/mail/QuotaAwareStore.java b/src/main/java/javax/mail/QuotaAwareStore.java deleted file mode 100644 index 1ff1b23d..00000000 --- a/src/main/java/javax/mail/QuotaAwareStore.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * An interface implemented by Stores that support quotas. - * The {@link #getQuota getQuota} and {@link #setQuota setQuota} methods - * support the quota model defined by the IMAP QUOTA extension. - * Refer to RFC 2087 - * for more information.

      - * - * @since JavaMail 1.4 - */ -public interface QuotaAwareStore { - /** - * Get the quotas for the named folder. - * Quotas are controlled on the basis of a quota root, not - * (necessarily) a folder. The relationship between folders - * and quota roots depends on the server. Some servers - * might implement a single quota root for all folders owned by - * a user. Other servers might implement a separate quota root - * for each folder. A single folder can even have multiple - * quota roots, perhaps controlling quotas for different - * resources. - * - * @param folder the name of the folder - * @return array of Quota objects - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - Quota[] getQuota(String folder) throws MessagingException; - - /** - * Set the quotas for the quota root specified in the quota argument. - * Typically this will be one of the quota roots obtained from the - * getQuota method, but it need not be. - * - * @param quota the quota to set - * @exception MessagingException if the server doesn't support the - * QUOTA extension - */ - void setQuota(Quota quota) throws MessagingException; -} diff --git a/src/main/java/javax/mail/ReadOnlyFolderException.java b/src/main/java/javax/mail/ReadOnlyFolderException.java deleted file mode 100644 index bb87445a..00000000 --- a/src/main/java/javax/mail/ReadOnlyFolderException.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when an attempt is made to open a folder - * read-write access when the folder is marked read-only.

      - * - * The getMessage() method returns more detailed information about the - * error that caused this exception.

      - * - * @author Jim Glennon - */ - -public class ReadOnlyFolderException extends MessagingException { - transient private Folder folder; - - private static final long serialVersionUID = 5711829372799039325L; - - /** - * Constructs a ReadOnlyFolderException with the specified - * folder and no detail message. - * - * @param folder the Folder - * @since JavaMail 1.2 - */ - public ReadOnlyFolderException(Folder folder) { - this(folder, null); - } - - /** - * Constructs a ReadOnlyFolderException with the specified - * detail message. - * - * @param folder The Folder - * @param message The detailed error message - * @since JavaMail 1.2 - */ - public ReadOnlyFolderException(Folder folder, String message) { - super(message); - this.folder = folder; - } - - /** - * Constructs a ReadOnlyFolderException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param folder The Folder - * @param message The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public ReadOnlyFolderException(Folder folder, String message, Exception e) { - super(message, e); - this.folder = folder; - } - - /** - * Returns the dead Folder object. - * @since JavaMail 1.2 - */ - public Folder getFolder() { - return folder; - } -} diff --git a/src/main/java/javax/mail/SendFailedException.java b/src/main/java/javax/mail/SendFailedException.java deleted file mode 100644 index e121a1a5..00000000 --- a/src/main/java/javax/mail/SendFailedException.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when the message cannot be sent.

      - * - * The exception includes those addresses to which the message could not be - * sent as well as the valid addresses to which the message was sent and - * valid addresses to which the message was not sent. - * - * @see javax.mail.Transport#send - * @see javax.mail.Transport#sendMessage - * @see javax.mail.event.TransportEvent - * - * @author John Mani - * @author Max Spivak - */ - -public class SendFailedException extends MessagingException { - transient protected Address[] invalid; - transient protected Address[] validSent; - transient protected Address[] validUnsent; - - private static final long serialVersionUID = -6457531621682372913L; - - /** - * Constructs a SendFailedException with no detail message. - */ - public SendFailedException() { - super(); - } - - /** - * Constructs a SendFailedException with the specified detail message. - * @param s the detail message - */ - public SendFailedException(String s) { - super(s); - } - - /** - * Constructs a SendFailedException with the specified - * Exception and detail message. The specified exception is chained - * to this exception. - * @param s the detail message - * @param e the embedded exception - * @see #getNextException - * @see #setNextException - */ - public SendFailedException(String s, Exception e) { - super(s, e); - } - - - /** - * Constructs a SendFailedException with the specified string - * and the specified address objects. - * - * @param msg the detail message - * @param ex the embedded exception - * @param validSent valid addresses to which message was sent - * @param validUnsent valid addresses to which message was not sent - * @param invalid the invalid addresses - * @see #getNextException - * @see #setNextException - */ - public SendFailedException(String msg, Exception ex, Address[] validSent, - Address[] validUnsent, Address[] invalid) { - super(msg, ex); - this.validSent = validSent; - this.validUnsent = validUnsent; - this.invalid = invalid; - } - - /** - * Return the addresses to which this message was sent succesfully. - * @return Addresses to which the message was sent successfully or null - */ - public Address[] getValidSentAddresses() { - return validSent; - } - - /** - * Return the addresses that are valid but to which this message - * was not sent. - * @return Addresses that are valid but to which the message was - * not sent successfully or null - */ - public Address[] getValidUnsentAddresses() { - return validUnsent; - } - - /** - * Return the addresses to which this message could not be sent. - * - * @return Addresses to which the message sending failed or null; - */ - public Address[] getInvalidAddresses() { - return invalid; - } -} diff --git a/src/main/java/javax/mail/Service.java b/src/main/java/javax/mail/Service.java deleted file mode 100644 index e82ca2a2..00000000 --- a/src/main/java/javax/mail/Service.java +++ /dev/null @@ -1,676 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.*; -import java.net.*; -import java.util.*; -import javax.mail.event.*; - -/** - * An abstract class that contains the functionality - * common to messaging services, such as stores and transports.

      - * A messaging service is created from a Session and is - * named using a URLName. A service must be connected - * before it can be used. Connection events are sent to reflect - * its connection status. - * - * @author Christopher Cotton - * @author Bill Shannon - * @author Kanwar Oberoi - */ - -public abstract class Service { - - /** - * The session from which this service was created. - */ - protected Session session; - - /** - * The URLName of this service. - */ - protected URLName url = null; - - /** - * Debug flag for this service. Set from the session's debug - * flag when this service is created. - */ - protected boolean debug = false; - - private boolean connected = false; - - /* - * connectionListeners is a Vector, initialized here, - * because we depend on it always existing and depend - * on the synchronization that Vector provides. - * (Sychronizing on the Service object itself can cause - * deadlocks when notifying listeners.) - */ - private final Vector connectionListeners = new Vector(); - - /** - * Constructor. - * - * @param session Session object for this service - * @param urlname URLName object to be used for this service - */ - protected Service(Session session, URLName urlname) { - this.session = session; - debug = session.getDebug(); - url = urlname; - - /* - * Initialize the URLName with default values. - * The URLName will be updated when connect is called. - */ - String protocol = null; - String host = null; - int port = -1; - String user = null; - String password = null; - String file = null; - - // get whatever information we can from the URL - // XXX - url should always be non-null here, Session - // passes it into the constructor - if (url != null) { - protocol = url.getProtocol(); - host = url.getHost(); - port = url.getPort(); - user = url.getUsername(); - password = url.getPassword(); - file = url.getFile(); - } - - // try to get protocol-specific default properties - if (protocol != null) { - if (host == null) - host = session.getProperty("mail." + protocol + ".host"); - if (user == null) - user = session.getProperty("mail." + protocol + ".user"); - } - - // try to get mail-wide default properties - if (host == null) - host = session.getProperty("mail.host"); - - if (user == null) - user = session.getProperty("mail.user"); - - // try using the system username - if (user == null) { - try { - user = System.getProperty("user.name"); - } catch (SecurityException sex) { - // XXX - it's not worth creating a MailLogger just for this - //logger.log(Level.CONFIG, "Can't get user.name property", sex); - } - } - - url = new URLName(protocol, host, port, file, user, password); - } - - /** - * A generic connect method that takes no parameters. Subclasses - * can implement the appropriate authentication schemes. Subclasses - * that need additional information might want to use some properties - * or might get it interactively using a popup window.

      - * - * If the connection is successful, an "open" ConnectionEvent - * is delivered to any ConnectionListeners on this service.

      - * - * Most clients should just call this method to connect to the service.

      - * - * It is an error to connect to an already connected service.

      - * - * The implementation provided here simply calls the following - * connect(String, String, String) method with nulls. - * - * @exception AuthenticationFailedException for authentication failures - * @exception MessagingException for other failures - * @exception IllegalStateException if the service is already connected - * - * @see javax.mail.event.ConnectionEvent - */ - public void connect() throws MessagingException { - connect(null, null, null); - } - - /** - * Connect to the specified address. This method provides a simple - * authentication scheme that requires a username and password.

      - * - * If the connection is successful, an "open" ConnectionEvent - * is delivered to any ConnectionListeners on this service.

      - * - * It is an error to connect to an already connected service.

      - * - * The implementation in the Service class will collect defaults - * for the host, user, and password from the session, from the - * URLName for this service, and from the supplied - * parameters and then call the protocolConnect method. - * If the protocolConnect method returns false, - * the user will be prompted for any missing information and the - * protocolConnect method will be called again. The - * subclass should override the protocolConnect method. - * The subclass should also implement the getURLName - * method, or use the implementation in this class.

      - * - * On a successful connection, the setURLName method is - * called with a URLName that includes the information used to make - * the connection, including the password.

      - * - * If the username passed in is null, a default value will be chosen - * as described above. - * - * If the password passed in is null and this is the first successful - * connection to this service, the user name and the password - * collected from the user will be saved as defaults for subsequent - * connection attempts to this same service when using other Service object - * instances (the connection information is typically always saved within - * a particular Service object instance). The password is saved using the - * Session method setPasswordAuthentication. If the - * password passed in is not null, it is not saved, on the assumption - * that the application is managing passwords explicitly. - * - * @param host the host to connect to - * @param user the user name - * @param password this user's password - * @exception AuthenticationFailedException for authentication failures - * @exception MessagingException for other failures - * @exception IllegalStateException if the service is already connected - * @see javax.mail.event.ConnectionEvent - * @see javax.mail.Session#setPasswordAuthentication - */ - public void connect(String host, String user, String password) - throws MessagingException { - connect(host, -1, user, password); - } - - /** - * Connect to the current host using the specified username - * and password. This method is equivalent to calling the - * connect(host, user, password) method with null - * for the host name. - * - * @param user the user name - * @param password this user's password - * @exception AuthenticationFailedException for authentication failures - * @exception MessagingException for other failures - * @exception IllegalStateException if the service is already connected - * @see javax.mail.event.ConnectionEvent - * @see javax.mail.Session#setPasswordAuthentication - * @see #connect(java.lang.String, java.lang.String, java.lang.String) - * @since JavaMail 1.4 - */ - public void connect(String user, String password) throws MessagingException { - connect(null, user, password); - } - - /** - * Similar to connect(host, user, password) except a specific port - * can be specified. - * - * @param host the host to connect to - * @param port the port to connect to (-1 means the default port) - * @param user the user name - * @param password this user's password - * @exception AuthenticationFailedException for authentication failures - * @exception MessagingException for other failures - * @exception IllegalStateException if the service is already connected - * @see #connect(java.lang.String, java.lang.String, java.lang.String) - * @see javax.mail.event.ConnectionEvent - */ - public synchronized void connect(String host, int port, - String user, String password) throws MessagingException { - - // see if the service is already connected - if (isConnected()) - throw new IllegalStateException("already connected"); - - PasswordAuthentication pw; - boolean connected = false; - boolean save = false; - String protocol = null; - String file = null; - - // get whatever information we can from the URL - // XXX - url should always be non-null here, Session - // passes it into the constructor - if (url != null) { - protocol = url.getProtocol(); - if (host == null) - host = url.getHost(); - if (port == -1) - port = url.getPort(); - - if (user == null) { - user = url.getUsername(); - if (password == null) // get password too if we need it - password = url.getPassword(); - } else { - if (password == null && user.equals(url.getUsername())) - // only get the password if it matches the username - password = url.getPassword(); - } - - file = url.getFile(); - } - - // try to get protocol-specific default properties - if (protocol != null) { - if (host == null) - host = session.getProperty("mail." + protocol + ".host"); - if (user == null) - user = session.getProperty("mail." + protocol + ".user"); - } - - // try to get mail-wide default properties - if (host == null) - host = session.getProperty("mail.host"); - - if (user == null) - user = session.getProperty("mail.user"); - - // try using the system username - if (user == null) { - try { - user = System.getProperty("user.name"); - } catch (SecurityException sex) { - // XXX - it's not worth creating a MailLogger just for this - //logger.log(Level.CONFIG, "Can't get user.name property", sex); - } - } - - // if we don't have a password, look for saved authentication info - if (password == null && url != null) { - // canonicalize the URLName - setURLName(new URLName(protocol, host, port, file, user, null)); - pw = session.getPasswordAuthentication(getURLName()); - if (pw != null) { - if (user == null) { - user = pw.getUserName(); - password = pw.getPassword(); - } else if (user.equals(pw.getUserName())) { - password = pw.getPassword(); - } - } else - save = true; - } - - // try connecting, if the protocol needs some missing - // information (user, password) it will not connect. - // if it tries to connect and fails, remember why for later. - AuthenticationFailedException authEx = null; - try { - connected = protocolConnect(host, port, user, password); - } catch (AuthenticationFailedException ex) { - authEx = ex; - } - - // if not connected, ask the user and try again - if (!connected) { - InetAddress addr; - try { - addr = InetAddress.getByName(host); - } catch (UnknownHostException e) { - addr = null; - } - pw = session.requestPasswordAuthentication( - addr, port, - protocol, - null, user); - if (pw != null) { - user = pw.getUserName(); - password = pw.getPassword(); - - // have the service connect again - connected = protocolConnect(host, port, user, password); - } - } - - // if we're not connected by now, we give up - if (!connected) { - if (authEx != null) - throw authEx; - else if (user == null) - throw new AuthenticationFailedException( - "failed to connect, no user name specified?"); - else if (password == null) - throw new AuthenticationFailedException( - "failed to connect, no password specified?"); - else - throw new AuthenticationFailedException("failed to connect"); - } - - setURLName(new URLName(protocol, host, port, file, user, password)); - - if (save) - session.setPasswordAuthentication(getURLName(), - new PasswordAuthentication(user, password)); - - // set our connected state - setConnected(true); - - // finally, deliver the connection event - notifyConnectionListeners(ConnectionEvent.OPENED); - } - - - /** - * The service implementation should override this method to - * perform the actual protocol-specific connection attempt. - * The default implementation of the connect method - * calls this method as needed.

      - * - * The protocolConnect method should return - * false if a user name or password is required - * for authentication but the corresponding parameter is null; - * the connect method will prompt the user when - * needed to supply missing information. This method may - * also return false if authentication fails for - * the supplied user name or password. Alternatively, this method - * may throw an AuthenticationFailedException when authentication - * fails. This exception may include a String message with more - * detail about the failure.

      - * - * The protocolConnect method should throw an - * exception to report failures not related to authentication, - * such as an invalid host name or port number, loss of a - * connection during the authentication process, unavailability - * of the server, etc. - * - * @param host the name of the host to connect to - * @param port the port to use (-1 means use default port) - * @param user the name of the user to login as - * @param password the user's password - * @return true if connection successful, false if authentication failed - * @exception AuthenticationFailedException for authentication failures - * @exception MessagingException for non-authentication failures - */ - protected boolean protocolConnect(String host, int port, String user, - String password) throws MessagingException { - return false; - } - - /** - * Is this service currently connected?

      - * - * This implementation uses a private boolean field to - * store the connection state. This method returns the value - * of that field.

      - * - * Subclasses may want to override this method to verify that any - * connection to the message store is still alive. - * - * @return true if the service is connected, false if it is not connected - */ - public synchronized boolean isConnected() { - return connected; - } - - /** - * Set the connection state of this service. The connection state - * will automatically be set by the service implementation during the - * connect and close methods. - * Subclasses will need to call this method to set the state - * if the service was automatically disconnected.

      - * - * The implementation in this class merely sets the private field - * returned by the isConnected method. - * - * @param connected true if the service is connected, - * false if it is not connected - */ - protected synchronized void setConnected(boolean connected) { - this.connected = connected; - } - - /** - * Close this service and terminate its connection. A close - * ConnectionEvent is delivered to any ConnectionListeners. Any - * Messaging components (Folders, Messages, etc.) belonging to this - * service are invalid after this service is closed. Note that the service - * is closed even if this method terminates abnormally by throwing - * a MessagingException.

      - * - * This implementation uses setConnected(false) to set - * this service's connected state to false. It will then - * send a close ConnectionEvent to any registered ConnectionListeners. - * Subclasses overriding this method to do implementation specific - * cleanup should call this method as a last step to insure event - * notification, probably by including a call to super.close() - * in a finally clause. - * - * @see javax.mail.event.ConnectionEvent - * @throws MessagingException for errors while closing - */ - public synchronized void close() throws MessagingException { - setConnected(false); - notifyConnectionListeners(ConnectionEvent.CLOSED); - } - - /** - * Return a URLName representing this service. The returned URLName - * does not include the password field.

      - * - * Subclasses should only override this method if their - * URLName does not follow the standard format.

      - * - * The implementation in the Service class returns (usually a copy of) - * the url field with the password and file information - * stripped out. - * - * @return the URLName representing this service - * @see URLName - */ - public synchronized URLName getURLName() { - if (url != null && (url.getPassword() != null || url.getFile() != null)) - return new URLName(url.getProtocol(), url.getHost(), - url.getPort(), null /* no file */, - url.getUsername(), null /* no password */); - else - return url; - } - - /** - * Set the URLName representing this service. - * Normally used to update the url field - * after a service has successfully connected.

      - * - * Subclasses should only override this method if their - * URL does not follow the standard format. In particular, - * subclasses should override this method if their URL - * does not require all the possible fields supported by - * URLName; a new URLName should - * be constructed with any unneeded fields removed.

      - * - * The implementation in the Service class simply sets the - * url field. - * - * @see URLName - */ - protected synchronized void setURLName(URLName url) { - this.url = url; - } - - /** - * Add a listener for Connection events on this service.

      - * - * The default implementation provided here adds this listener - * to an internal list of ConnectionListeners. - * - * @param l the Listener for Connection events - * @see javax.mail.event.ConnectionEvent - */ - public void addConnectionListener(ConnectionListener l) { - connectionListeners.addElement(l); - } - - /** - * Remove a Connection event listener.

      - * - * The default implementation provided here removes this listener - * from the internal list of ConnectionListeners. - * - * @param l the listener - * @see #addConnectionListener - */ - public void removeConnectionListener(ConnectionListener l) { - connectionListeners.removeElement(l); - } - - /** - * Notify all ConnectionListeners. Service implementations are - * expected to use this method to broadcast connection events.

      - * - * The provided default implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * ConnectionListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyConnectionListeners(int type) { - /* - * Don't bother queuing an event if there's no listeners. - * Yes, listeners could be removed after checking, which - * just makes this an expensive no-op. - */ - if (connectionListeners.size() > 0) { - ConnectionEvent e = new ConnectionEvent(this, type); - queueEvent(e, connectionListeners); - } - - /* Fix for broken JDK1.1.x Garbage collector : - * The 'conservative' GC in JDK1.1.x occasionally fails to - * garbage-collect Threads which are in the wait state. - * This would result in thread (and consequently memory) leaks. - * - * We attempt to fix this by sending a 'terminator' event - * to the queue, after we've sent the CLOSED event. The - * terminator event causes the event-dispatching thread to - * self destruct. - */ - if (type == ConnectionEvent.CLOSED) - terminateQueue(); - } - - /** - * Return getURLName.toString() if this service has a URLName, - * otherwise it will return the default toString. - */ - public String toString() { - URLName url = getURLName(); - if (url != null) - return url.toString(); - else - return super.toString(); - } - - /* - * The queue of events to be delivered. - */ - private EventQueue q; - - /* - * A lock for creating the EventQueue object. Only one thread should - * create an EventQueue for this service. We can't synchronize on the - * service's lock because that might violate the locking hierarchy in - * some cases. - */ - private Object qLock = new Object(); - - /** - * Add the event and vector of listeners to the queue to be delivered. - */ - protected void queueEvent(MailEvent event, Vector vector) { - // synchronize creation of the event queue - synchronized (qLock) { - if (q == null) - q = new EventQueue(); - } - - /* - * Copy the vector in order to freeze the state of the set - * of EventListeners the event should be delivered to prior - * to delivery. This ensures that any changes made to the - * Vector from a target listener's method during the delivery - * of this event will not take effect until after the event is - * delivered. - */ - Vector v = (Vector)vector.clone(); - q.enqueue(event, v); - } - - static class TerminatorEvent extends MailEvent { - private static final long serialVersionUID = 5542172141759168416L; - - TerminatorEvent() { - super(new Object()); - } - - public void dispatch(Object listener) { - // Kill the event dispatching thread. - Thread.currentThread().interrupt(); - } - } - - // Dispatch the terminator - private void terminateQueue() { - synchronized (qLock) { - if (q != null) { - Vector dummyListeners = new Vector(); - dummyListeners.setSize(1); // need atleast one listener - q.enqueue(new TerminatorEvent(), dummyListeners); - q = null; - } - } - } - - /** - * Stop the event dispatcher thread so the queue can be garbage collected. - */ - protected void finalize() throws Throwable { - super.finalize(); - terminateQueue(); - } -} diff --git a/src/main/java/javax/mail/Session.java b/src/main/java/javax/mail/Session.java deleted file mode 100644 index e1b08cee..00000000 --- a/src/main/java/javax/mail/Session.java +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.lang.reflect.*; -import java.io.*; -import java.net.*; -import java.security.*; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Properties; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.logging.Level; - -import javax.activation.*; - -import com.sun.mail.util.LineInputStream; -import com.sun.mail.util.MailLogger; - -/** - * The Session class represents a mail session and is not subclassed. - * It collects together properties and defaults used by the mail API's. - * A single default session can be shared by multiple applications on the - * desktop. Unshared sessions can also be created.

      - * - * The Session class provides access to the protocol providers that - * implement the Store, Transport, and related - * classes. The protocol providers are configured using the following files: - *

        - *
      • javamail.providers and - * javamail.default.providers
      • - *
      • javamail.address.map and - * javamail.default.address.map
      • - *
      - *

      - * Each javamail.X resource file is searched for using - * three methods in the following order: - *

        - *
      1. java.home/lib/javamail.X
      2. - *
      3. META-INF/javamail.X
      4. - *
      5. META-INF/javamail.default.X
      6. - *
      - *

      - * The first method allows the user to include their own version of the - * resource file by placing it in the lib directory where the - * java.home property points. The second method allows an - * application that uses the JavaMail APIs to include their own resource - * files in their application's or jar file's META-INF - * directory. The javamail.default.X default files - * are part of the JavaMail mail.jar file.

      - * - * File location depends upon how the ClassLoader method - * getResource is implemented. Usually, the - * getResource method searches through CLASSPATH until it - * finds the requested file and then stops. JDK 1.1 has a limitation that - * the number of files of each name that will be found in the CLASSPATH is - * limited to one. However, this only affects method two, above; method - * one is loaded from a specific location (if allowed by the - * SecurityManager) and method three uses a different name to ensure that - * the default resource file is always loaded successfully. J2SE 1.2 and - * later are not limited to one file of a given name.

      - * - * The ordering of entries in the resource files matters. If multiple - * entries exist, the first entries take precedence over the later - * entries. For example, the first IMAP provider found will be set as the - * default IMAP implementation until explicitly changed by the - * application. The user- or system-supplied resource files augment, they - * do not override, the default files included with the JavaMail APIs. - * This means that all entries in all files loaded will be available.

      - * - * javamail.providers and - * javamail.default.providers

      - * - * These resource files specify the stores and transports that are - * available on the system, allowing an application to "discover" what - * store and transport implementations are available. The protocol - * implementations are listed one per line. The file format defines four - * attributes that describe a protocol implementation. Each attribute is - * an "="-separated name-value pair with the name in lowercase. Each - * name-value pair is semi-colon (";") separated. The following names - * are defined.

      - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
      - * Attribute Names in Providers Files - *
      NameDescription
      protocolName assigned to protocol. - * For example, smtp for Transport.
      typeValid entries are store and transport.
      classClass name that implements this protocol.
      vendorOptional string identifying the vendor.
      versionOptional string identifying the version.

      - * - * Here's an example of META-INF/javamail.default.providers - * file contents: - *

      - * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc.;
      - * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc.;
      - * 

      - * - * javamail.address.map and - * javamail.default.address.map

      - * - * These resource files map transport address types to the transport - * protocol. The getType method of - * javax.mail.Address returns the address type. The - * javamail.address.map file maps the transport type to the - * protocol. The file format is a series of name-value pairs. Each key - * name should correspond to an address type that is currently installed - * on the system; there should also be an entry for each - * javax.mail.Address implementation that is present if it is - * to be used. For example, the - * javax.mail.internet.InternetAddress method - * getType returns "rfc822". Each referenced protocol should - * be installed on the system. For the case of news, below, - * the client should install a Transport provider supporting the nntp - * protocol.

      - * - * Here are the typical contents of a javamail.address.map file: - *

      - * rfc822=smtp
      - * news=nntp
      - * 
      - * - * @author John Mani - * @author Bill Shannon - * @author Max Spivak - */ - -public final class Session { - - private final Properties props; - private final Authenticator authenticator; - private final Hashtable authTable = new Hashtable(); - private boolean debug = false; - private PrintStream out; // debug output stream - private MailLogger logger; - private final Vector providers = new Vector(); - private final Hashtable providersByProtocol = new Hashtable(); - private final Hashtable providersByClassName = new Hashtable(); - private final Properties addressMap = new Properties(); - private final String version = "1.5"; - // maps type to protocol - // The default session. - private static Session defaultSession = null; - - // Constructor is not public - private Session(Properties props, Authenticator authenticator) { - this.props = props; - this.authenticator = authenticator; - - if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue()) - debug = true; - - initLogger(); - logger.log(Level.CONFIG, "JavaMail version {0}", version); - - // get the Class associated with the Authenticator - Class cl; - if (authenticator != null) - cl = authenticator.getClass(); - else - cl = this.getClass(); - // load the resources - loadProviders(cl); - loadAddressMap(cl); - } - - private final void initLogger() { - logger = new MailLogger(this.getClass(), "DEBUG", debug, getDebugOut()); - } - - /** - * Get a new Session object. - * - * @param props Properties object that hold relevant properties.
      - * It is expected that the client supplies values - * for the properties listed in Appendix A of the - * JavaMail spec (particularly mail.store.protocol, - * mail.transport.protocol, mail.host, mail.user, - * and mail.from) as the defaults are unlikely to - * work in all cases. - * @param authenticator Authenticator object used to call back to - * the application when a user name and password is - * needed. - * @return a new Session object - * @see javax.mail.Authenticator - */ - public static Session getInstance(Properties props, - Authenticator authenticator) { - return new Session(props, authenticator); - } - - /** - * Get a new Session object. - * - * @param props Properties object that hold relevant properties.
      - * It is expected that the client supplies values - * for the properties listed in Appendix A of the - * JavaMail spec (particularly mail.store.protocol, - * mail.transport.protocol, mail.host, mail.user, - * and mail.from) as the defaults are unlikely to - * work in all cases. - * @return a new Session object - * @since JavaMail 1.2 - */ - public static Session getInstance(Properties props) { - return new Session(props, null); - } - - /** - * Get the default Session object. If a default has not yet been - * setup, a new Session object is created and installed as the - * default.

      - * - * Since the default session is potentially available to all - * code executing in the same Java virtual machine, and the session - * can contain security sensitive information such as user names - * and passwords, access to the default session is restricted. - * The Authenticator object, which must be created by the caller, - * is used indirectly to check access permission. The Authenticator - * object passed in when the session is created is compared with - * the Authenticator object passed in to subsequent requests to - * get the default session. If both objects are the same, or are - * from the same ClassLoader, the request is allowed. Otherwise, - * it is denied.

      - * - * Note that if the Authenticator object used to create the session - * is null, anyone can get the default session by passing in null.

      - * - * Note also that the Properties object is used only the first time - * this method is called, when a new Session object is created. - * Subsequent calls return the Session object that was created by the - * first call, and ignore the passed Properties object. Use the - * getInstance method to get a new Session object every - * time the method is called.

      - * - * In JDK 1.2, additional security Permission objects may be used to - * control access to the default session. - * - * @param props Properties object. Used only if a new Session - * object is created.
      - * It is expected that the client supplies values - * for the properties listed in Appendix A of the - * JavaMail spec (particularly mail.store.protocol, - * mail.transport.protocol, mail.host, mail.user, - * and mail.from) as the defaults are unlikely to - * work in all cases. - * @param authenticator Authenticator object. Used only if a - * new Session object is created. Otherwise, - * it must match the Authenticator used to create - * the Session. - * @return the default Session object - */ - public static synchronized Session getDefaultInstance(Properties props, - Authenticator authenticator) { - if (defaultSession == null) - defaultSession = new Session(props, authenticator); - else { - // have to check whether caller is allowed to see default session - if (defaultSession.authenticator == authenticator) - ; // either same object or both null, either way OK - else if (defaultSession.authenticator != null && - authenticator != null && - defaultSession.authenticator.getClass().getClassLoader() == - authenticator.getClass().getClassLoader()) - ; // both objects came from the same class loader, OK - else - // anything else is not allowed - throw new SecurityException("Access to default session denied"); - } - - return defaultSession; - } - - /** - * Get the default Session object. If a default has not yet been - * setup, a new Session object is created and installed as the - * default.

      - * - * Note that a default session created with no Authenticator is - * available to all code executing in the same Java virtual - * machine, and the session can contain security sensitive - * information such as user names and passwords. - * - * @param props Properties object. Used only if a new Session - * object is created.
      - * It is expected that the client supplies values - * for the properties listed in Appendix A of the - * JavaMail spec (particularly mail.store.protocol, - * mail.transport.protocol, mail.host, mail.user, - * and mail.from) as the defaults are unlikely to - * work in all cases. - * @return the default Session object - * @since JavaMail 1.2 - */ - public static Session getDefaultInstance(Properties props) { - return getDefaultInstance(props, null); - } - - /** - * Set the debug setting for this Session. - *

      - * Since the debug setting can be turned on only after the Session - * has been created, to turn on debugging in the Session - * constructor, set the property mail.debug in the - * Properties object passed in to the constructor to true. The - * value of the mail.debug property is used to - * initialize the per-Session debugging flag. Subsequent calls to - * the setDebug method manipulate the per-Session - * debugging flag and have no affect on the mail.debug - * property. - * - * @param debug Debug setting - */ - public synchronized void setDebug(boolean debug) { - this.debug = debug; - initLogger(); - logger.log(Level.CONFIG, "setDebug: JavaMail version {0}", - version); - } - - /** - * Get the debug setting for this Session. - * - * @return current debug setting - */ - public synchronized boolean getDebug() { - return debug; - } - - /** - * Set the stream to be used for debugging output for this session. - * If out is null, System.out will be used. - * Note that debugging output that occurs before any session is created, - * as a result of setting the mail.debug system property, - * will always be sent to System.out. - * - * @param out the PrintStream to use for debugging output - * @since JavaMail 1.3 - */ - public synchronized void setDebugOut(PrintStream out) { - this.out = out; - initLogger(); - } - - /** - * Returns the stream to be used for debugging output. If no stream - * has been set, System.out is returned. - * - * @return the PrintStream to use for debugging output - * @since JavaMail 1.3 - */ - public synchronized PrintStream getDebugOut() { - if (out == null) - return System.out; - else - return out; - } - - /** - * This method returns an array of all the implementations installed - * via the javamail.[default.]providers files that can - * be loaded using the ClassLoader available to this application. - * - * @return Array of configured providers - */ - public synchronized Provider[] getProviders() { - Provider[] _providers = new Provider[providers.size()]; - providers.copyInto(_providers); - return _providers; - } - - /** - * Returns the default Provider for the protocol - * specified. Checks mail.<protocol>.class property - * first and if it exists, returns the Provider - * associated with this implementation. If it doesn't exist, - * returns the Provider that appeared first in the - * configuration files. If an implementation for the protocol - * isn't found, throws NoSuchProviderException - * - * @param protocol Configured protocol (i.e. smtp, imap, etc) - * @return Currently configured Provider for the specified protocol - * @exception NoSuchProviderException If a provider for the given - * protocol is not found. - */ - public synchronized Provider getProvider(String protocol) - throws NoSuchProviderException { - - if (protocol == null || protocol.length() <= 0) { - throw new NoSuchProviderException("Invalid protocol: null"); - } - - Provider _provider = null; - - // check if the mail..class property exists - String _className = props.getProperty("mail."+protocol+".class"); - if (_className != null) { - if (logger.isLoggable(Level.FINE)) { - logger.fine("mail."+protocol+ - ".class property exists and points to " + - _className); - } - _provider = (Provider)providersByClassName.get(_className); - } - - if (_provider != null) { - return _provider; - } else { - // returning currently default protocol in providersByProtocol - _provider = (Provider)providersByProtocol.get(protocol); - } - - if (_provider == null) { - throw new NoSuchProviderException("No provider for " + protocol); - } else { - if (logger.isLoggable(Level.FINE)) { - logger.fine("getProvider() returning " + _provider.toString()); - } - return _provider; - } - } - - /** - * Set the passed Provider to be the default implementation - * for the protocol in Provider.protocol overriding any previous values. - * - * @param provider Currently configured Provider which will be - * set as the default for the protocol - * @exception NoSuchProviderException If the provider passed in - * is invalid. - */ - public synchronized void setProvider(Provider provider) - throws NoSuchProviderException { - if (provider == null) { - throw new NoSuchProviderException("Can't set null provider"); - } - providersByProtocol.put(provider.getProtocol(), provider); - props.put("mail." + provider.getProtocol() + ".class", - provider.getClassName()); - } - - - /** - * Get a Store object that implements this user's desired Store - * protocol. The mail.store.protocol property specifies the - * desired protocol. If an appropriate Store object is not obtained, - * NoSuchProviderException is thrown - * - * @return a Store object - * @exception NoSuchProviderException If a provider for the given - * protocol is not found. - */ - public Store getStore() throws NoSuchProviderException { - return getStore(getProperty("mail.store.protocol")); - } - - /** - * Get a Store object that implements the specified protocol. If an - * appropriate Store object cannot be obtained, - * NoSuchProviderException is thrown. - * - * @param protocol - * @return a Store object - * @exception NoSuchProviderException If a provider for the given - * protocol is not found. - */ - public Store getStore(String protocol) throws NoSuchProviderException { - return getStore(new URLName(protocol, null, -1, null, null, null)); - } - - - /** - * Get a Store object for the given URLName. If the requested Store - * object cannot be obtained, NoSuchProviderException is thrown. - * - * The "scheme" part of the URL string (Refer RFC 1738) is used - * to locate the Store protocol.

      - * - * @param url URLName that represents the desired Store - * @return a closed Store object - * @see #getFolder(URLName) - * @see javax.mail.URLName - * @exception NoSuchProviderException If a provider for the given - * URLName is not found. - */ - public Store getStore(URLName url) throws NoSuchProviderException { - String protocol = url.getProtocol(); - Provider p = getProvider(protocol); - return getStore(p, url); - } - - /** - * Get an instance of the store specified by Provider. Instantiates - * the store and returns it. - * - * @param provider Store Provider that will be instantiated - * @return Instantiated Store - * @exception NoSuchProviderException If a provider for the given - * Provider is not found. - */ - public Store getStore(Provider provider) throws NoSuchProviderException { - return getStore(provider, null); - } - - - /** - * Get an instance of the store specified by Provider. If the URLName - * is not null, uses it, otherwise creates a new one. Instantiates - * the store and returns it. This is a private method used by - * getStore(Provider) and getStore(URLName) - * - * @param provider Store Provider that will be instantiated - * @param url URLName used to instantiate the Store - * @return Instantiated Store - * @exception NoSuchProviderException If a provider for the given - * Provider/URLName is not found. - */ - private Store getStore(Provider provider, URLName url) - throws NoSuchProviderException { - - // make sure we have the correct type of provider - if (provider == null || provider.getType() != Provider.Type.STORE ) { - throw new NoSuchProviderException("invalid provider"); - } - - try { - return (Store) getService(provider, url); - } catch (ClassCastException cce) { - throw new NoSuchProviderException("incorrect class"); - } - } - - /** - * Get a closed Folder object for the given URLName. If the requested - * Folder object cannot be obtained, null is returned.

      - * - * The "scheme" part of the URL string (Refer RFC 1738) is used - * to locate the Store protocol. The rest of the URL string (that is, - * the "schemepart", as per RFC 1738) is used by that Store - * in a protocol dependent manner to locate and instantiate the - * appropriate Folder object.

      - * - * Note that RFC 1738 also specifies the syntax for the - * "schemepart" for IP-based protocols (IMAP4, POP3, etc.). - * Providers of IP-based mail Stores should implement that - * syntax for referring to Folders.

      - * - * @param url URLName that represents the desired folder - * @return Folder - * @see #getStore(URLName) - * @see javax.mail.URLName - * @exception NoSuchProviderException If a provider for the given - * URLName is not found. - * @exception MessagingException if the Folder could not be - * located or created. - */ - public Folder getFolder(URLName url) - throws MessagingException { - // First get the Store - Store store = getStore(url); - store.connect(); - return store.getFolder(url); - } - - /** - * Get a Transport object that implements this user's desired - * Transport protcol. The mail.transport.protocol property - * specifies the desired protocol. If an appropriate Transport - * object cannot be obtained, MessagingException is thrown. - * - * @return a Transport object - * @exception NoSuchProviderException If the provider is not found. - */ - public Transport getTransport() throws NoSuchProviderException { - return getTransport(getProperty("mail.transport.protocol")); - } - - /** - * Get a Transport object that implements the specified protocol. - * If an appropriate Transport object cannot be obtained, null is - * returned. - * - * @return a Transport object - * @exception NoSuchProviderException If provider for the given - * protocol is not found. - */ - public Transport getTransport(String protocol) - throws NoSuchProviderException { - return getTransport(new URLName(protocol, null, -1, null, null, null)); - } - - - /** - * Get a Transport object for the given URLName. If the requested - * Transport object cannot be obtained, NoSuchProviderException is thrown. - * - * The "scheme" part of the URL string (Refer RFC 1738) is used - * to locate the Transport protocol.

      - * - * @param url URLName that represents the desired Transport - * @return a closed Transport object - * @see javax.mail.URLName - * @exception NoSuchProviderException If a provider for the given - * URLName is not found. - */ - public Transport getTransport(URLName url) throws NoSuchProviderException { - String protocol = url.getProtocol(); - Provider p = getProvider(protocol); - return getTransport(p, url); - } - - /** - * Get an instance of the transport specified in the Provider. Instantiates - * the transport and returns it. - * - * @param provider Transport Provider that will be instantiated - * @return Instantiated Transport - * @exception NoSuchProviderException If provider for the given - * provider is not found. - */ - public Transport getTransport(Provider provider) - throws NoSuchProviderException { - return getTransport(provider, null); - } - - /** - * Get a Transport object that can transport a Message of the - * specified address type. - * - * @param address - * @return A Transport object - * @see javax.mail.Address - * @exception NoSuchProviderException If provider for the - * Address type is not found - */ - public Transport getTransport(Address address) - throws NoSuchProviderException { - - String transportProtocol; - transportProtocol = - getProperty("mail.transport.protocol." + address.getType()); - if (transportProtocol != null) - return getTransport(transportProtocol); - transportProtocol = (String)addressMap.get(address.getType()); - if (transportProtocol != null) - return getTransport(transportProtocol); - throw new NoSuchProviderException("No provider for Address type: "+ - address.getType()); - } - - /** - * Get a Transport object using the given provider and urlname. - * - * @param provider the provider to use - * @param url urlname to use (can be null) - * @return A Transport object - * @exception NoSuchProviderException If no provider or the provider - * was the wrong class. - */ - - private Transport getTransport(Provider provider, URLName url) - throws NoSuchProviderException { - // make sure we have the correct type of provider - if (provider == null || provider.getType() != Provider.Type.TRANSPORT) { - throw new NoSuchProviderException("invalid provider"); - } - - try { - return (Transport) getService(provider, url); - } catch (ClassCastException cce) { - throw new NoSuchProviderException("incorrect class"); - } - } - - /** - * Get a Service object. Needs a provider object, but will - * create a URLName if needed. It attempts to instantiate - * the correct class. - * - * @param provider which provider to use - * @param url which URLName to use (can be null) - * @exception NoSuchProviderException thrown when the class cannot be - * found or when it does not have the correct constructor - * (Session, URLName), or if it is not derived from - * Service. - */ - private Object getService(Provider provider, URLName url) - throws NoSuchProviderException { - // need a provider and url - if (provider == null) { - throw new NoSuchProviderException("null"); - } - - // create a url if needed - if (url == null) { - url = new URLName(provider.getProtocol(), null, -1, - null, null, null); - } - - Object service = null; - - // get the ClassLoader associated with the Authenticator - ClassLoader cl; - if (authenticator != null) - cl = authenticator.getClass().getClassLoader(); - else - cl = this.getClass().getClassLoader(); - - // now load the class - Class serviceClass = null; - try { - // First try the "application's" class loader. - ClassLoader ccl = getContextClassLoader(); - if (ccl != null) - try { - serviceClass = - Class.forName(provider.getClassName(), false, ccl); - } catch (ClassNotFoundException ex) { - // ignore it - } - if (serviceClass == null) - serviceClass = - Class.forName(provider.getClassName(), false, cl); - } catch (Exception ex1) { - // That didn't work, now try the "system" class loader. - // (Need both of these because JDK 1.1 class loaders - // may not delegate to their parent class loader.) - try { - serviceClass = Class.forName(provider.getClassName()); - } catch (Exception ex) { - // Nothing worked, give up. - logger.log(Level.FINE, "Exception loading provider", ex); - throw new NoSuchProviderException(provider.getProtocol()); - } - } - - // construct an instance of the class - try { - Class[] c = {javax.mail.Session.class, javax.mail.URLName.class}; - Constructor cons = serviceClass.getConstructor(c); - - Object[] o = {this, url}; - service = cons.newInstance(o); - - } catch (Exception ex) { - logger.log(Level.FINE, "Exception loading provider", ex); - throw new NoSuchProviderException(provider.getProtocol()); - } - - return service; - } - - /** - * Save a PasswordAuthentication for this (store or transport) URLName. - * If pw is null the entry corresponding to the URLName is removed. - *

      - * This is normally used only by the store or transport implementations - * to allow authentication information to be shared among multiple - * uses of a session. - */ - public void setPasswordAuthentication(URLName url, - PasswordAuthentication pw) { - if (pw == null) - authTable.remove(url); - else - authTable.put(url, pw); - } - - /** - * Return any saved PasswordAuthentication for this (store or transport) - * URLName. Normally used only by store or transport implementations. - * - * @return the PasswordAuthentication corresponding to the URLName - */ - public PasswordAuthentication getPasswordAuthentication(URLName url) { - return (PasswordAuthentication)authTable.get(url); - } - - /** - * Call back to the application to get the needed user name and password. - * The application should put up a dialog something like: - *

      -     * Connecting to <protocol> mail service on host <addr>, port <port>.
      -     * <prompt>
      -     *
      -     * User Name: <defaultUserName>
      -     * Password:
      -     * 
      - * - * @param addr InetAddress of the host. may be null. - * @param protocol protocol scheme (e.g. imap, pop3, etc.) - * @param prompt any additional String to show as part of - * the prompt; may be null. - * @param defaultUserName the default username. may be null. - * @return the authentication which was collected by the authenticator; - * may be null. - */ - public PasswordAuthentication requestPasswordAuthentication( - InetAddress addr, int port, - String protocol, String prompt, String defaultUserName) { - - if (authenticator != null) { - return authenticator.requestPasswordAuthentication( - addr, port, protocol, prompt, defaultUserName); - } else { - return null; - } - } - - /** - * Returns the Properties object associated with this Session - * - * @return Properties object - */ - public Properties getProperties() { - return props; - } - - /** - * Returns the value of the specified property. Returns null - * if this property does not exist. - * - * @return String that is the property value - */ - public String getProperty(String name) { - return props.getProperty(name); - } - - /** - * Load the protocol providers config files. - */ - private void loadProviders(Class cl) { - StreamLoader loader = new StreamLoader() { - public void load(InputStream is) throws IOException { - loadProvidersFromStream(is); - } - }; - - // load system-wide javamail.providers from the /lib dir - try { - String res = System.getProperty("java.home") + - File.separator + "lib" + - File.separator + "javamail.providers"; - loadFile(res, loader); - } catch (SecurityException sex) { - logger.log(Level.CONFIG, "can't get java.home", sex); - } - - // load the META-INF/javamail.providers file supplied by an application - loadAllResources("META-INF/javamail.providers", cl, loader); - - // load default META-INF/javamail.default.providers from mail.jar file - loadResource("/META-INF/javamail.default.providers", cl, loader); - - if (providers.size() == 0) { - logger.config("failed to load any providers, using defaults"); - // failed to load any providers, initialize with our defaults - addProvider(new Provider(Provider.Type.STORE, - "imap", "com.sun.mail.imap.IMAPStore", - "Sun Microsystems, Inc.", version)); - addProvider(new Provider(Provider.Type.STORE, - "imaps", "com.sun.mail.imap.IMAPSSLStore", - "Sun Microsystems, Inc.", version)); - addProvider(new Provider(Provider.Type.STORE, - "pop3", "com.sun.mail.pop3.POP3Store", - "Sun Microsystems, Inc.", version)); - addProvider(new Provider(Provider.Type.STORE, - "pop3s", "com.sun.mail.pop3.POP3SSLStore", - "Sun Microsystems, Inc.", version)); - addProvider(new Provider(Provider.Type.TRANSPORT, - "smtp", "com.sun.mail.smtp.SMTPTransport", - "Sun Microsystems, Inc.", version)); - addProvider(new Provider(Provider.Type.TRANSPORT, - "smtps", "com.sun.mail.smtp.SMTPSSLTransport", - "Sun Microsystems, Inc.", version)); - } - - if (logger.isLoggable(Level.CONFIG)) { - // dump the output of the tables for debugging - logger.config("Tables of loaded providers"); - logger.config("Providers Listed By Class Name: " + - providersByClassName.toString()); - logger.config("Providers Listed By Protocol: " + - providersByProtocol.toString()); - } - } - - private void loadProvidersFromStream(InputStream is) - throws IOException { - if (is != null) { - LineInputStream lis = new LineInputStream(is); - String currLine; - - // load and process one line at a time using LineInputStream - while ((currLine = lis.readLine()) != null) { - - if (currLine.startsWith("#")) - continue; - Provider.Type type = null; - String protocol = null, className = null; - String vendor = null, version = null; - - // separate line into key-value tuples - StringTokenizer tuples = new StringTokenizer(currLine,";"); - while (tuples.hasMoreTokens()) { - String currTuple = tuples.nextToken().trim(); - - // set the value of each attribute based on its key - int sep = currTuple.indexOf("="); - if (currTuple.startsWith("protocol=")) { - protocol = currTuple.substring(sep+1); - } else if (currTuple.startsWith("type=")) { - String strType = currTuple.substring(sep+1); - if (strType.equalsIgnoreCase("store")) { - type = Provider.Type.STORE; - } else if (strType.equalsIgnoreCase("transport")) { - type = Provider.Type.TRANSPORT; - } - } else if (currTuple.startsWith("class=")) { - className = currTuple.substring(sep+1); - } else if (currTuple.startsWith("vendor=")) { - vendor = currTuple.substring(sep+1); - } else if (currTuple.startsWith("version=")) { - version = currTuple.substring(sep+1); - } - } - - // check if a valid Provider; else, continue - if (type == null || protocol == null || className == null - || protocol.length() <= 0 || className.length() <= 0) { - - logger.log(Level.CONFIG, "Bad provider entry: {0}", - currLine); - continue; - } - Provider provider = new Provider(type, protocol, className, - vendor, version); - - // add the newly-created Provider to the lookup tables - addProvider(provider); - } - } - } - - /** - * Add a provider to the session. - * - * @param provider the provider to add - * @since JavaMail 1.4 - */ - public synchronized void addProvider(Provider provider) { - providers.addElement(provider); - providersByClassName.put(provider.getClassName(), provider); - if (!providersByProtocol.containsKey(provider.getProtocol())) - providersByProtocol.put(provider.getProtocol(), provider); - } - - // load maps in reverse order of preference so that the preferred - // map is loaded last since its entries will override the previous ones - private void loadAddressMap(Class cl) { - StreamLoader loader = new StreamLoader() { - public void load(InputStream is) throws IOException { - addressMap.load(is); - } - }; - - // load default META-INF/javamail.default.address.map from mail.jar - loadResource("/META-INF/javamail.default.address.map", cl, loader); - - // load the META-INF/javamail.address.map file supplied by an app - loadAllResources("META-INF/javamail.address.map", cl, loader); - - // load system-wide javamail.address.map from the /lib dir - try { - String res = System.getProperty("java.home") + - File.separator + "lib" + - File.separator + "javamail.address.map"; - loadFile(res, loader); - } catch (SecurityException sex) { - logger.log(Level.CONFIG, "can't get java.home", sex); - } - - if (addressMap.isEmpty()) { - logger.config("failed to load address map, using defaults"); - addressMap.put("rfc822", "smtp"); - } - } - - /** - * Set the default transport protocol to use for addresses of - * the specified type. Normally the default is set by the - * javamail.default.address.map or - * javamail.address.map files or resources. - * - * @param addresstype type of address - * @param protocol name of protocol - * @see #getTransport(Address) - * @since JavaMail 1.4 - */ - public synchronized void setProtocolForAddress(String addresstype, - String protocol) { - if (protocol == null) - addressMap.remove(addresstype); - else - addressMap.put(addresstype, protocol); - } - - /** - * Load from the named file. - */ - private void loadFile(String name, StreamLoader loader) { - InputStream clis = null; - try { - clis = new BufferedInputStream(new FileInputStream(name)); - loader.load(clis); - logger.log(Level.CONFIG, "successfully loaded file: {0}", name); - } catch (FileNotFoundException fex) { - // ignore it - } catch (IOException e) { - if (logger.isLoggable(Level.CONFIG)) - logger.log(Level.CONFIG, "not loading file: " + name, e); - } catch (SecurityException sex) { - if (logger.isLoggable(Level.CONFIG)) - logger.log(Level.CONFIG, "not loading file: " + name, sex); - } finally { - try { - if (clis != null) - clis.close(); - } catch (IOException ex) { } // ignore it - } - } - - /** - * Load from the named resource. - */ - private void loadResource(String name, Class cl, StreamLoader loader) { - InputStream clis = null; - try { - clis = getResourceAsStream(cl, name); - if (clis != null) { - loader.load(clis); - logger.log(Level.CONFIG, "successfully loaded resource: {0}", - name); - } else { - /* - logger.log(Level.CONFIG, "not loading resource: {0}", name); - */ - } - } catch (IOException e) { - logger.log(Level.CONFIG, "Exception loading resource", e); - } catch (SecurityException sex) { - logger.log(Level.CONFIG, "Exception loading resource", sex); - } finally { - try { - if (clis != null) - clis.close(); - } catch (IOException ex) { } // ignore it - } - } - - /** - * Load all of the named resource. - */ - private void loadAllResources(String name, Class cl, StreamLoader loader) { - boolean anyLoaded = false; - try { - URL[] urls; - ClassLoader cld = null; - // First try the "application's" class loader. - cld = getContextClassLoader(); - if (cld == null) - cld = cl.getClassLoader(); - if (cld != null) - urls = getResources(cld, name); - else - urls = getSystemResources(name); - if (urls != null) { - for (int i = 0; i < urls.length; i++) { - URL url = urls[i]; - InputStream clis = null; - logger.log(Level.CONFIG, "URL {0}", url); - try { - clis = openStream(url); - if (clis != null) { - loader.load(clis); - anyLoaded = true; - logger.log(Level.CONFIG, - "successfully loaded resource: {0}", url); - } else { - logger.log(Level.CONFIG, - "not loading resource: {0}", url); - } - } catch (FileNotFoundException fex) { - // ignore it - } catch (IOException ioex) { - logger.log(Level.CONFIG, "Exception loading resource", - ioex); - } catch (SecurityException sex) { - logger.log(Level.CONFIG, "Exception loading resource", - sex); - } finally { - try { - if (clis != null) - clis.close(); - } catch (IOException cex) { } - } - } - } - } catch (Exception ex) { - logger.log(Level.CONFIG, "Exception loading resource", ex); - } - - // if failed to load anything, fall back to old technique, just in case - if (!anyLoaded) { - /* - logger.config("!anyLoaded"); - */ - loadResource("/" + name, cl, loader); - } - } - - /* - * Following are security related methods that work on JDK 1.2 or newer. - */ - - private static ClassLoader getContextClassLoader() { - return (ClassLoader) - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } catch (SecurityException ex) { } - return cl; - } - }); - } - - private static InputStream getResourceAsStream(final Class c, - final String name) throws IOException { - try { - return (InputStream) - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws IOException { - return c.getResourceAsStream(name); - } - }); - } catch (PrivilegedActionException e) { - throw (IOException)e.getException(); - } - } - - private static URL[] getResources(final ClassLoader cl, final String name) { - return (URL[]) - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - URL[] ret = null; - try { - Vector v = new Vector(); - Enumeration e = cl.getResources(name); - while (e != null && e.hasMoreElements()) { - URL url = (URL)e.nextElement(); - if (url != null) - v.addElement(url); - } - if (v.size() > 0) { - ret = new URL[v.size()]; - v.copyInto(ret); - } - } catch (IOException ioex) { - } catch (SecurityException ex) { } - return ret; - } - }); - } - - private static URL[] getSystemResources(final String name) { - return (URL[]) - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - URL[] ret = null; - try { - Vector v = new Vector(); - Enumeration e = ClassLoader.getSystemResources(name); - while (e != null && e.hasMoreElements()) { - URL url = (URL)e.nextElement(); - if (url != null) - v.addElement(url); - } - if (v.size() > 0) { - ret = new URL[v.size()]; - v.copyInto(ret); - } - } catch (IOException ioex) { - } catch (SecurityException ex) { } - return ret; - } - }); - } - - private static InputStream openStream(final URL url) throws IOException { - try { - return (InputStream) - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws IOException { - return url.openStream(); - } - }); - } catch (PrivilegedActionException e) { - throw (IOException)e.getException(); - } - } -} - -/** - * Support interface to generalize - * code that loads resources from stream. - */ -interface StreamLoader { - public void load(InputStream is) throws IOException; -} diff --git a/src/main/java/javax/mail/Store.java b/src/main/java/javax/mail/Store.java deleted file mode 100644 index 1b210e8a..00000000 --- a/src/main/java/javax/mail/Store.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.*; -import java.net.*; -import java.util.*; -import javax.mail.event.*; - -/** - * An abstract class that models a message store and its - * access protocol, for storing and retrieving messages. - * Subclasses provide actual implementations.

      - * - * Note that Store extends the Service - * class, which provides many common methods for naming stores, - * connecting to stores, and listening to connection events. - * - * @author John Mani - * @author Bill Shannon - * - * @see javax.mail.Service - * @see javax.mail.event.ConnectionEvent - * @see javax.mail.event.StoreEvent - */ - -public abstract class Store extends Service { - - /** - * Constructor. - * - * @param session Session object for this Store. - * @param urlname URLName object to be used for this Store - */ - protected Store(Session session, URLName urlname) { - super(session, urlname); - } - - /** - * Returns a Folder object that represents the 'root' of - * the default namespace presented to the user by the Store. - * - * @return the root Folder - * @exception IllegalStateException if this Store is not connected. - */ - public abstract Folder getDefaultFolder() throws MessagingException; - - /** - * Return the Folder object corresponding to the given name. Note - * that a Folder object is returned even if the named folder does - * not physically exist on the Store. The exists() - * method on the folder object indicates whether this folder really - * exists.

      - * - * Folder objects are not cached by the Store, so invoking this - * method on the same name multiple times will return that many - * distinct Folder objects. - * - * @param name The name of the Folder. In some Stores, name can - * be an absolute path if it starts with the - * hierarchy delimiter. Else it is interpreted - * relative to the 'root' of this namespace. - * @return Folder object - * @exception IllegalStateException if this Store is not connected. - * @see Folder#exists - * @see Folder#create - */ - public abstract Folder getFolder(String name) - throws MessagingException; - - /** - * Return a closed Folder object, corresponding to the given - * URLName. The store specified in the given URLName should - * refer to this Store object.

      - * - * Implementations of this method may obtain the name of the - * actual folder using the getFile() method on - * URLName, and use that name to create the folder. - * - * @param url URLName that denotes a folder - * @see URLName - * @exception IllegalStateException if this Store is not connected. - * @return Folder object - */ - public abstract Folder getFolder(URLName url) - throws MessagingException; - - /** - * Return a set of folders representing the personal namespaces - * for the current user. A personal namespace is a set of names that - * is considered within the personal scope of the authenticated user. - * Typically, only the authenticated user has access to mail folders - * in their personal namespace. If an INBOX exists for a user, it - * must appear within the user's personal namespace. In the - * typical case, there should be only one personal namespace for each - * user in each Store.

      - * - * This implementation returns an array with a single entry containing - * the return value of the getDefaultFolder method. - * Subclasses should override this method to return appropriate information. - * - * @exception IllegalStateException if this Store is not connected. - * @return array of Folder objects - * @since JavaMail 1.2 - */ - public Folder[] getPersonalNamespaces() throws MessagingException { - return new Folder[] { getDefaultFolder() }; - } - - /** - * Return a set of folders representing the namespaces for - * user. The namespaces returned represent the - * personal namespaces for the user. To access mail folders in the - * other user's namespace, the currently authenticated user must be - * explicitly granted access rights. For example, it is common for - * a manager to grant to their secretary access rights to their - * mail folders.

      - * - * This implementation returns an empty array. Subclasses should - * override this method to return appropriate information. - * - * @exception IllegalStateException if this Store is not connected. - * @return array of Folder objects - * @since JavaMail 1.2 - */ - public Folder[] getUserNamespaces(String user) - throws MessagingException { - return new Folder[0]; - } - - /** - * Return a set of folders representing the shared namespaces. - * A shared namespace is a namespace that consists of mail folders - * that are intended to be shared amongst users and do not exist - * within a user's personal namespace.

      - * - * This implementation returns an empty array. Subclasses should - * override this method to return appropriate information. - * - * @exception IllegalStateException if this Store is not connected. - * @return array of Folder objects - * @since JavaMail 1.2 - */ - public Folder[] getSharedNamespaces() throws MessagingException { - return new Folder[0]; - } - - // Vector of Store listeners - private volatile Vector storeListeners = null; - - /** - * Add a listener for StoreEvents on this Store.

      - * - * The default implementation provided here adds this listener - * to an internal list of StoreListeners. - * - * @param l the Listener for Store events - * @see javax.mail.event.StoreEvent - */ - public synchronized void addStoreListener(StoreListener l) { - if (storeListeners == null) - storeListeners = new Vector(); - storeListeners.addElement(l); - } - - /** - * Remove a listener for Store events.

      - * - * The default implementation provided here removes this listener - * from the internal list of StoreListeners. - * - * @param l the listener - * @see #addStoreListener - */ - public synchronized void removeStoreListener(StoreListener l) { - if (storeListeners != null) - storeListeners.removeElement(l); - } - - /** - * Notify all StoreListeners. Store implementations are - * expected to use this method to broadcast StoreEvents.

      - * - * The provided default implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * StoreListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyStoreListeners(int type, String message) { - if (storeListeners == null) - return; - - StoreEvent e = new StoreEvent(this, type, message); - queueEvent(e, storeListeners); - } - - // Vector of folder listeners - private volatile Vector folderListeners = null; - - /** - * Add a listener for Folder events on any Folder object - * obtained from this Store. FolderEvents are delivered to - * FolderListeners on the affected Folder as well as to - * FolderListeners on the containing Store.

      - * - * The default implementation provided here adds this listener - * to an internal list of FolderListeners. - * - * @param l the Listener for Folder events - * @see javax.mail.event.FolderEvent - */ - public synchronized void addFolderListener(FolderListener l) { - if (folderListeners == null) - folderListeners = new Vector(); - folderListeners.addElement(l); - } - - /** - * Remove a listener for Folder events.

      - * - * The default implementation provided here removes this listener - * from the internal list of FolderListeners. - * - * @param l the listener - * @see #addFolderListener - */ - public synchronized void removeFolderListener(FolderListener l) { - if (folderListeners != null) - folderListeners.removeElement(l); - } - - /** - * Notify all FolderListeners. Store implementations are - * expected to use this method to broadcast Folder events.

      - * - * The provided default implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * FolderListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - * - * @param type type of FolderEvent - * @param folder affected Folder - * @see #notifyFolderRenamedListeners - */ - protected void notifyFolderListeners(int type, Folder folder) { - if (folderListeners == null) - return; - - FolderEvent e = new FolderEvent(this, folder, type); - queueEvent(e, folderListeners); - } - - /** - * Notify all FolderListeners about the renaming of a folder. - * Store implementations are expected to use this method to broadcast - * Folder events indicating the renaming of folders.

      - * - * The provided default implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * FolderListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - * - * @param oldF the folder being renamed - * @param newF the folder representing the new name. - * @since JavaMail 1.1 - */ - protected void notifyFolderRenamedListeners(Folder oldF, Folder newF) { - if (folderListeners == null) - return; - - FolderEvent e = new FolderEvent(this, oldF, newF,FolderEvent.RENAMED); - queueEvent(e, folderListeners); - } -} diff --git a/src/main/java/javax/mail/StoreClosedException.java b/src/main/java/javax/mail/StoreClosedException.java deleted file mode 100644 index c8c6dcbc..00000000 --- a/src/main/java/javax/mail/StoreClosedException.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -/** - * This exception is thrown when a method is invoked on a Messaging object - * and the Store that owns that object has died due to some reason. - * This exception should be treated as a fatal error; in particular any - * messaging object belonging to that Store must be considered invalid.

      - * - * The connect method may be invoked on the dead Store object to - * revive it.

      - * - * The getMessage() method returns more detailed information about the - * error that caused this exception.

      - * - * @author John Mani - */ - -public class StoreClosedException extends MessagingException { - transient private Store store; - - private static final long serialVersionUID = -3145392336120082655L; - - /** - * Constructs a StoreClosedException with no detail message. - * - * @param store The dead Store object - */ - public StoreClosedException(Store store) { - this(store, null); - } - - /** - * Constructs a StoreClosedException with the specified - * detail message. - * - * @param store The dead Store object - * @param message The detailed error message - */ - public StoreClosedException(Store store, String message) { - super(message); - this.store = store; - } - - /** - * Constructs a StoreClosedException with the specified - * detail message and embedded exception. The exception is chained - * to this exception. - * - * @param store The dead Store object - * @param message The detailed error message - * @param e The embedded exception - * @since JavaMail 1.5 - */ - public StoreClosedException(Store store, String message, Exception e) { - super(message, e); - this.store = store; - } - - /** - * Returns the dead Store object - */ - public Store getStore() { - return store; - } -} diff --git a/src/main/java/javax/mail/Transport.java b/src/main/java/javax/mail/Transport.java deleted file mode 100644 index f0645883..00000000 --- a/src/main/java/javax/mail/Transport.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.io.IOException; -import java.net.*; -import java.util.Vector; -import java.util.Hashtable; -import java.util.Enumeration; -import javax.mail.event.*; - -/** - * An abstract class that models a message transport. - * Subclasses provide actual implementations.

      - * - * Note that Transport extends the Service - * class, which provides many common methods for naming transports, - * connecting to transports, and listening to connection events. - * - * @author John Mani - * @author Max Spivak - * @author Bill Shannon - * - * @see javax.mail.Service - * @see javax.mail.event.ConnectionEvent - * @see javax.mail.event.TransportEvent - */ - -public abstract class Transport extends Service { - - /** - * Constructor. - * - * @param session Session object for this Transport. - * @param urlname URLName object to be used for this Transport - */ - public Transport(Session session, URLName urlname) { - super(session, urlname); - } - - /** - * Send a message. The message will be sent to all recipient - * addresses specified in the message (as returned from the - * Message method getAllRecipients), - * using message transports appropriate to each address. The - * send method calls the saveChanges - * method on the message before sending it.

      - * - * If any of the recipient addresses is detected to be invalid by - * the Transport during message submission, a SendFailedException - * is thrown. Clients can get more detail about the failure by examining - * the exception. Whether or not the message is still sent successfully - * to any valid addresses depends on the Transport implementation. See - * SendFailedException for more details. Note also that success does - * not imply that the message was delivered to the ultimate recipient, - * as failures may occur in later stages of delivery. Once a Transport - * accepts a message for delivery to a recipient, failures that occur later - * should be reported to the user via another mechanism, such as - * returning the undeliverable message.

      - * - * In typical usage, a SendFailedException reflects an error detected - * by the server. The details of the SendFailedException will usually - * contain the error message from the server (such as an SMTP error - * message). An address may be detected as invalid for a variety of - * reasons - the address may not exist, the address may have invalid - * syntax, the address may have exceeded its quota, etc.

      - * - * Note that send is a static method that creates and - * manages its own connection. Any connection associated with any - * Transport instance used to invoke this method is ignored and not - * used. This method should only be invoked using the form - * Transport.send(msg);, and should never be invoked - * using an instance variable. - * - * @param msg the message to send - * @exception SendFailedException if the message could not - * be sent to some or any of the recipients. - * @exception MessagingException - * @see Message#saveChanges - * @see Message#getAllRecipients - * @see #send(Message, Address[]) - * @see javax.mail.SendFailedException - */ - public static void send(Message msg) throws MessagingException { - msg.saveChanges(); // do this first - send0(msg, msg.getAllRecipients(), null, null); - } - - /** - * Send the message to the specified addresses, ignoring any - * recipients specified in the message itself. The - * send method calls the saveChanges - * method on the message before sending it.

      - * - * @param msg the message to send - * @param addresses the addresses to which to send the message - * @exception SendFailedException if the message could not - * be sent to some or any of the recipients. - * @exception MessagingException - * @see Message#saveChanges - * @see #send(Message) - * @see javax.mail.SendFailedException - */ - public static void send(Message msg, Address[] addresses) - throws MessagingException { - - msg.saveChanges(); - send0(msg, addresses, null, null); - } - - /** - * Send a message. The message will be sent to all recipient - * addresses specified in the message (as returned from the - * Message method getAllRecipients). - * The send method calls the saveChanges - * method on the message before sending it.

      - * - * Use the specified user name and password to authenticate to - * the mail server. - * - * @param msg the message to send - * @param user the user name - * @param password this user's password - * @exception SendFailedException if the message could not - * be sent to some or any of the recipients. - * @exception MessagingException - * @see Message#saveChanges - * @see #send(Message) - * @see javax.mail.SendFailedException - * @since JavaMail 1.5 - */ - public static void send(Message msg, - String user, String password) throws MessagingException { - - msg.saveChanges(); - send0(msg, msg.getAllRecipients(), user, password); - } - - /** - * Send the message to the specified addresses, ignoring any - * recipients specified in the message itself. The - * send method calls the saveChanges - * method on the message before sending it.

      - * - * Use the specified user name and password to authenticate to - * the mail server. - * - * @param msg the message to send - * @param addresses the addresses to which to send the message - * @param user the user name - * @param password this user's password - * @exception SendFailedException if the message could not - * be sent to some or any of the recipients. - * @exception MessagingException - * @see Message#saveChanges - * @see #send(Message) - * @see javax.mail.SendFailedException - * @since JavaMail 1.5 - */ - public static void send(Message msg, Address[] addresses, - String user, String password) throws MessagingException { - - msg.saveChanges(); - send0(msg, addresses, user, password); - } - - // send, but without the saveChanges - private static void send0(Message msg, Address[] addresses, - String user, String password) throws MessagingException { - - if (addresses == null || addresses.length == 0) - throw new SendFailedException("No recipient addresses"); - - /* - * protocols is a hashtable containing the addresses - * indexed by address type - */ - Hashtable protocols = new Hashtable(); - - // Vectors of addresses - Vector invalid = new Vector(); - Vector validSent = new Vector(); - Vector validUnsent = new Vector(); - - for (int i = 0; i < addresses.length; i++) { - // is this address type already in the hashtable? - if (protocols.containsKey(addresses[i].getType())) { - Vector v = (Vector)protocols.get(addresses[i].getType()); - v.addElement(addresses[i]); - } else { - // need to add a new protocol - Vector w = new Vector(); - w.addElement(addresses[i]); - protocols.put(addresses[i].getType(), w); - } - } - - int dsize = protocols.size(); - if (dsize == 0) - throw new SendFailedException("No recipient addresses"); - - Session s = (msg.session != null) ? msg.session : - Session.getDefaultInstance(System.getProperties(), null); - Transport transport; - - /* - * Optimize the case of a single protocol. - */ - if (dsize == 1) { - transport = s.getTransport(addresses[0]); - try { - if (user != null) - transport.connect(user, password); - else - transport.connect(); - transport.sendMessage(msg, addresses); - } finally { - transport.close(); - } - return; - } - - /* - * More than one protocol. Have to do them one at a time - * and collect addresses and chain exceptions. - */ - MessagingException chainedEx = null; - boolean sendFailed = false; - - Enumeration e = protocols.elements(); - while (e.hasMoreElements()) { - Vector v = (Vector)e.nextElement(); - Address[] protaddresses = new Address[v.size()]; - v.copyInto(protaddresses); - - // Get a Transport that can handle this address type. - if ((transport = s.getTransport(protaddresses[0])) == null) { - // Could not find an appropriate Transport .. - // Mark these addresses invalid. - for (int j = 0; j < protaddresses.length; j++) - invalid.addElement(protaddresses[j]); - continue; - } - try { - transport.connect(); - transport.sendMessage(msg, protaddresses); - } catch (SendFailedException sex) { - sendFailed = true; - // chain the exception we're catching to any previous ones - if (chainedEx == null) - chainedEx = sex; - else - chainedEx.setNextException(sex); - - // retrieve invalid addresses - Address[] a = sex.getInvalidAddresses(); - if (a != null) - for (int j = 0; j < a.length; j++) - invalid.addElement(a[j]); - - // retrieve validSent addresses - a = sex.getValidSentAddresses(); - if (a != null) - for (int k = 0; k < a.length; k++) - validSent.addElement(a[k]); - - // retrieve validUnsent addresses - Address[] c = sex.getValidUnsentAddresses(); - if (c != null) - for (int l = 0; l < c.length; l++) - validUnsent.addElement(c[l]); - } catch (MessagingException mex) { - sendFailed = true; - // chain the exception we're catching to any previous ones - if (chainedEx == null) - chainedEx = mex; - else - chainedEx.setNextException(mex); - } finally { - transport.close(); - } - } - - // done with all protocols. throw exception if something failed - if (sendFailed || invalid.size() != 0 || validUnsent.size() != 0) { - Address[] a = null, b = null, c = null; - - // copy address vectors into arrays - if (validSent.size() > 0) { - a = new Address[validSent.size()]; - validSent.copyInto(a); - } - if (validUnsent.size() > 0) { - b = new Address[validUnsent.size()]; - validUnsent.copyInto(b); - } - if (invalid.size() > 0) { - c = new Address[invalid.size()]; - invalid.copyInto(c); - } - throw new SendFailedException("Sending failed", chainedEx, - a, b, c); - } - } - - /** - * Send the Message to the specified list of addresses. An appropriate - * TransportEvent indicating the delivery status is delivered to any - * TransportListener registered on this Transport. Also, if any of - * the addresses is invalid, a SendFailedException is thrown. - * Whether or not the message is still sent succesfully to - * any valid addresses depends on the Transport implementation.

      - * - * Unlike the static send method, the sendMessage - * method does not call the saveChanges method on - * the message; the caller should do so. - * - * @param msg The Message to be sent - * @param addresses array of addresses to send this message to - * @see javax.mail.event.TransportEvent - * @exception SendFailedException if the send failed because of - * invalid addresses. - * @exception MessagingException if the connection is dead or not in the - * connected state - */ - public abstract void sendMessage(Message msg, Address[] addresses) - throws MessagingException; - - // Vector of Transport listeners - private volatile Vector transportListeners = null; - - /** - * Add a listener for Transport events.

      - * - * The default implementation provided here adds this listener - * to an internal list of TransportListeners. - * - * @param l the Listener for Transport events - * @see javax.mail.event.TransportEvent - */ - public synchronized void addTransportListener(TransportListener l) { - if (transportListeners == null) - transportListeners = new Vector(); - transportListeners.addElement(l); - } - - /** - * Remove a listener for Transport events.

      - * - * The default implementation provided here removes this listener - * from the internal list of TransportListeners. - * - * @param l the listener - * @see #addTransportListener - */ - public synchronized void removeTransportListener(TransportListener l) { - if (transportListeners != null) - transportListeners.removeElement(l); - } - - /** - * Notify all TransportListeners. Transport implementations are - * expected to use this method to broadcast TransportEvents.

      - * - * The provided default implementation queues the event into - * an internal event queue. An event dispatcher thread dequeues - * events from the queue and dispatches them to the registered - * TransportListeners. Note that the event dispatching occurs - * in a separate thread, thus avoiding potential deadlock problems. - */ - protected void notifyTransportListeners(int type, Address[] validSent, - Address[] validUnsent, - Address[] invalid, Message msg) { - if (transportListeners == null) - return; - - TransportEvent e = new TransportEvent(this, type, validSent, - validUnsent, invalid, msg); - queueEvent(e, transportListeners); - } -} diff --git a/src/main/java/javax/mail/UIDFolder.java b/src/main/java/javax/mail/UIDFolder.java deleted file mode 100644 index 24138d29..00000000 --- a/src/main/java/javax/mail/UIDFolder.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.util.NoSuchElementException; - -/** - * The UIDFolder interface is implemented by Folders - * that can support the "disconnected" mode of operation, by providing - * unique-ids for messages in the folder. This interface is based on - * the IMAP model for supporting disconnected operation.

      - * - * A Unique identifier (UID) is a positive long value, assigned to - * each message in a specific folder. Unique identifiers are assigned - * in a strictly ascending fashion in the mailbox. - * That is, as each message is added to the mailbox it is assigned a - * higher UID than the message(s) which were added previously. Unique - * identifiers persist across sessions. This permits a client to - * resynchronize its state from a previous session with the server.

      - * - * Associated with every mailbox is a unique identifier validity value. - * If unique identifiers from an earlier session fail to persist to - * this session, the unique identifier validity value - * must be greater than the one used in the earlier - * session.

      - * - * Refer to RFC 2060 - * for more information. - * - * All the Folder objects returned by the default IMAP provider implement - * the UIDFolder interface. Use it as follows:

      - *

      - *
      - * 	Folder f = store.getFolder("whatever");
      - *	UIDFolder uf = (UIDFolder)f;
      - *	long uid = uf.getUID(msg);
      - *
      - * 

      - * - * @author John Mani - */ - -public interface UIDFolder { - - /** - * A fetch profile item for fetching UIDs. - * This inner class extends the FetchProfile.Item - * class to add new FetchProfile item types, specific to UIDFolders. - * The only item currently defined here is the UID item. - * - * @see FetchProfile - */ - public static class FetchProfileItem extends FetchProfile.Item { - protected FetchProfileItem(String name) { - super(name); - } - - /** - * UID is a fetch profile item that can be included in a - * FetchProfile during a fetch request to a Folder. - * This item indicates that the UIDs for messages in the specified - * range are desired to be prefetched.

      - * - * An example of how a client uses this is below:

      - *

      -	 *
      -	 * 	FetchProfile fp = new FetchProfile();
      -	 *	fp.add(UIDFolder.FetchProfileItem.UID);
      -	 *	folder.fetch(msgs, fp);
      -	 *
      -	 * 

      - */ - public static final FetchProfileItem UID = - new FetchProfileItem("UID"); - } - - /** - * This is a special value that can be used as the end - * parameter in getMessagesByUID(start, end), to denote the - * UID of the last message in the folder. - * - * @see #getMessagesByUID - */ - public final static long LASTUID = -1; - - /** - * Returns the UIDValidity value associated with this folder.

      - * - * Clients typically compare this value against a UIDValidity - * value saved from a previous session to insure that any cached - * UIDs are not stale. - * - * @return UIDValidity - */ - public long getUIDValidity() throws MessagingException; - - /** - * Get the Message corresponding to the given UID. If no such - * message exists, null is returned. - * - * @param uid UID for the desired message - * @return the Message object. null is returned - * if no message corresponding to this UID is obtained. - * @exception MessagingException - */ - public Message getMessageByUID(long uid) throws MessagingException; - - /** - * Get the Messages specified by the given range. The special - * value LASTUID can be used for the end parameter - * to indicate the UID of the last message in the folder.

      - * - * Note that end need not be greater than start; - * the order of the range doesn't matter. - * Note also that, unless the folder is empty, use of LASTUID ensures - * that at least one message will be returned - the last message in the - * folder. - * - * @param start start UID - * @param end end UID - * @return array of Message objects - * @exception MessagingException - * @see #LASTUID - */ - public Message[] getMessagesByUID(long start, long end) - throws MessagingException; - - /** - * Get the Messages specified by the given array of UIDs. If any UID is - * invalid, null is returned for that entry.

      - * - * Note that the returned array will be of the same size as the specified - * array of UIDs, and null entries may be present in the - * array to indicate invalid UIDs. - * - * @param uids array of UIDs - * @return array of Message objects - * @exception MessagingException - */ - public Message[] getMessagesByUID(long[] uids) - throws MessagingException; - - /** - * Get the UID for the specified message. Note that the message - * must belong to this folder. Otherwise - * java.util.NoSuchElementException is thrown. - * - * @param message Message from this folder - * @return UID for this message - * @exception NoSuchElementException if the given Message - * is not in this Folder. - */ - public long getUID(Message message) throws MessagingException; -} diff --git a/src/main/java/javax/mail/URLName.java b/src/main/java/javax/mail/URLName.java deleted file mode 100644 index ba045d63..00000000 --- a/src/main/java/javax/mail/URLName.java +++ /dev/null @@ -1,770 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail; - -import java.net.*; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStreamWriter; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.BitSet; -import java.util.Locale; - - -/** - * The name of a URL. This class represents a URL name and also - * provides the basic parsing functionality to parse most internet - * standard URL schemes.

      - * - * Note that this class differs from java.net.URL - * in that this class just represents the name of a URL, it does - * not model the connection to a URL. - * - * @author Christopher Cotton - * @author Bill Shannon - */ - -public class URLName { - - /** - * The full version of the URL - */ - protected String fullURL; - - /** - * The protocol to use (ftp, http, nntp, imap, pop3 ... etc.) . - */ - private String protocol; - - /** - * The username to use when connecting - */ - private String username; - - /** - * The password to use when connecting. - */ - private String password; - - /** - * The host name to which to connect. - */ - private String host; - - /** - * The host's IP address, used in equals and hashCode. - * Computed on demand. - */ - private InetAddress hostAddress; - private boolean hostAddressKnown = false; - - /** - * The protocol port to connect to. - */ - private int port = -1; - - /** - * The specified file name on that host. - */ - private String file; - - /** - * # reference. - */ - private String ref; - - /** - * Our hash code. - */ - private int hashCode = 0; - - /** - * A way to turn off encoding, just in case... - */ - private static boolean doEncode = true; - - static { - try { - doEncode = !Boolean.getBoolean("mail.URLName.dontencode"); - } catch (Exception ex) { - // ignore any errors - } - } - - /** - * Creates a URLName object from the specified protocol, - * host, port number, file, username, and password. Specifying a port - * number of -1 indicates that the URL should use the default port for - * the protocol. - */ - public URLName( - String protocol, - String host, - int port, - String file, - String username, - String password - ) - { - this.protocol = protocol; - this.host = host; - this.port = port; - int refStart; - if (file != null && (refStart = file.indexOf('#')) != -1) { - this.file = file.substring(0, refStart); - this.ref = file.substring(refStart + 1); - } else { - this.file = file; - this.ref = null; - } - this.username = doEncode ? encode(username) : username; - this.password = doEncode ? encode(password) : password; - } - - /** - * Construct a URLName from a java.net.URL object. - */ - public URLName(URL url) { - this(url.toString()); - } - - /** - * Construct a URLName from the string. Parses out all the possible - * information (protocol, host, port, file, username, password). - */ - public URLName(String url) { - parseString(url); - } - - /** - * Constructs a string representation of this URLName. - */ - public String toString() { - if (fullURL == null) { - // add the "protocol:" - StringBuffer tempURL = new StringBuffer(); - if (protocol != null) { - tempURL.append(protocol); - tempURL.append(":"); - } - - if (username != null || host != null) { - // add the "//" - tempURL.append("//"); - - // add the user:password@ - // XXX - can you just have a password? without a username? - if (username != null) { - tempURL.append(username); - - if (password != null){ - tempURL.append(":"); - tempURL.append(password); - } - - tempURL.append("@"); - } - - // add host - if (host != null) { - tempURL.append(host); - } - - // add port (if needed) - if (port != -1) { - tempURL.append(":"); - tempURL.append(Integer.toString(port)); - } - if (file != null) - tempURL.append("/"); - } - - // add the file - if (file != null) { - tempURL.append(file); - } - - // add the ref - if (ref != null) { - tempURL.append("#"); - tempURL.append(ref); - } - - // create the fullURL now - fullURL = tempURL.toString(); - } - - return fullURL; - } - - /** - * Method which does all of the work of parsing the string. - */ - protected void parseString(String url) { - // initialize everything in case called from subclass - // (URLName really should be a final class) - protocol = file = ref = host = username = password = null; - port = -1; - - int len = url.length(); - - // find the protocol - // XXX - should check for only legal characters before the colon - // (legal: a-z, A-Z, 0-9, "+", ".", "-") - int protocolEnd = url.indexOf(':'); - if (protocolEnd != -1) - protocol = url.substring(0, protocolEnd); - - // is this an Internet standard URL that contains a host name? - if (url.regionMatches(protocolEnd + 1, "//", 0, 2)) { - // find where the file starts - String fullhost = null; - int fileStart = url.indexOf('/', protocolEnd + 3); - if (fileStart != -1) { - fullhost = url.substring(protocolEnd + 3, fileStart); - if (fileStart + 1 < len) - file = url.substring(fileStart + 1); - else - file = ""; - } else - fullhost = url.substring(protocolEnd + 3); - - // examine the fullhost, for username password etc. - int i = fullhost.indexOf('@'); - if (i != -1) { - String fulluserpass = fullhost.substring(0, i); - fullhost = fullhost.substring(i + 1); - - // get user and password - int passindex = fulluserpass.indexOf(':'); - if (passindex != -1) { - username = fulluserpass.substring(0, passindex); - password = fulluserpass.substring(passindex + 1); - } else { - username = fulluserpass; - } - } - - // get the port (if there) - int portindex; - if (fullhost.length() > 0 && fullhost.charAt(0) == '[') { - // an IPv6 address? - portindex = fullhost.indexOf(':', fullhost.indexOf(']')); - } else { - portindex = fullhost.indexOf(':'); - } - if (portindex != -1) { - String portstring = fullhost.substring(portindex + 1); - if (portstring.length() > 0) { - try { - port = Integer.parseInt(portstring); - } catch (NumberFormatException nfex) { - port = -1; - } - } - - host = fullhost.substring(0, portindex); - } else { - host = fullhost; - } - } else { - if (protocolEnd + 1 < len) - file = url.substring(protocolEnd + 1); - } - - // extract the reference from the file name, if any - int refStart; - if (file != null && (refStart = file.indexOf('#')) != -1) { - ref = file.substring(refStart + 1); - file = file.substring(0, refStart); - } - } - - /** - * Returns the port number of this URLName. - * Returns -1 if the port is not set. - */ - public int getPort() { - return port; - } - - /** - * Returns the protocol of this URLName. - * Returns null if this URLName has no protocol. - */ - public String getProtocol() { - return protocol; - } - - /** - * Returns the file name of this URLName. - * Returns null if this URLName has no file name. - */ - public String getFile() { - return file; - } - - /** - * Returns the reference of this URLName. - * Returns null if this URLName has no reference. - */ - public String getRef() { - return ref; - } - - /** - * Returns the host of this URLName. - * Returns null if this URLName has no host. - */ - public String getHost() { - return host; - } - - /** - * Returns the user name of this URLName. - * Returns null if this URLName has no user name. - */ - public String getUsername() { - return doEncode ? decode(username) : username; - } - - /** - * Returns the password of this URLName. - * Returns null if this URLName has no password. - */ - public String getPassword() { - return doEncode ? decode(password) : password; - } - - /** - * Constructs a URL from the URLName. - */ - public URL getURL() throws MalformedURLException { - return new URL(getProtocol(), getHost(), getPort(), getFile()); - } - - /** - * Compares two URLNames. The result is true if and only if the - * argument is not null and is a URLName object that represents the - * same URLName as this object. Two URLName objects are equal if - * they have the same protocol and the same host, - * the same port number on the host, the same username, - * and the same file on the host. The fields (host, username, - * file) are also considered the same if they are both - * null.

      - * - * Hosts are considered equal if the names are equal (case independent) - * or if host name lookups for them both succeed and they both reference - * the same IP address.

      - * - * Note that URLName has no knowledge of default port numbers for - * particular protocols, so "imap://host" and "imap://host:143" - * would not compare as equal.

      - * - * Note also that the password field is not included in the comparison, - * nor is any reference field appended to the filename. - */ - public boolean equals(Object obj) { - if (!(obj instanceof URLName)) - return false; - URLName u2 = (URLName)obj; - - // compare protocols - if (u2.protocol == null || !u2.protocol.equals(protocol)) - return false; - - // compare hosts - InetAddress a1 = getHostAddress(), a2 = u2.getHostAddress(); - // if we have internet address for both, and they're not the same, fail - if (a1 != null && a2 != null) { - if (!a1.equals(a2)) - return false; - // else, if we have host names for both, and they're not the same, fail - } else if (host != null && u2.host != null) { - if (!host.equalsIgnoreCase(u2.host)) - return false; - // else, if not both null - } else if (host != u2.host) { - return false; - } - // at this point, hosts match - - // compare usernames - if (!(username == u2.username || - (username != null && username.equals(u2.username)))) - return false; - - // Forget about password since it doesn't - // really denote a different store. - - // compare files - String f1 = file == null ? "" : file; - String f2 = u2.file == null ? "" : u2.file; - - if (!f1.equals(f2)) - return false; - - // compare ports - if (port != u2.port) - return false; - - // all comparisons succeeded, they're equal - return true; - } - - /** - * Compute the hash code for this URLName. - */ - public int hashCode() { - if (hashCode != 0) - return hashCode; - if (protocol != null) - hashCode += protocol.hashCode(); - InetAddress addr = getHostAddress(); - if (addr != null) - hashCode += addr.hashCode(); - else if (host != null) - hashCode += host.toLowerCase(Locale.ENGLISH).hashCode(); - if (username != null) - hashCode += username.hashCode(); - if (file != null) - hashCode += file.hashCode(); - hashCode += port; - return hashCode; - } - - /** - * Get the IP address of our host. Look up the - * name the first time and remember that we've done - * so, whether the lookup fails or not. - */ - private synchronized InetAddress getHostAddress() { - if (hostAddressKnown) - return hostAddress; - if (host == null) - return null; - try { - hostAddress = InetAddress.getByName(host); - } catch (UnknownHostException ex) { - hostAddress = null; - } - hostAddressKnown = true; - return hostAddress; - } - - /** - * The class contains a utility method for converting a - * String into a MIME format called - * "x-www-form-urlencoded" format. - *

      - * To convert a String, each character is examined in turn: - *

        - *
      • The ASCII characters 'a' through 'z', - * 'A' through 'Z', '0' - * through '9', and ".", "-", - * "*", "_" remain the same. - *
      • The space character ' ' is converted into a - * plus sign '+'. - *
      • All other characters are converted into the 3-character string - * "%xy", where xy is the two-digit - * hexadecimal representation of the lower 8-bits of the character. - *
      - * - * @author Herb Jellinek - * @since JDK1.0 - */ - static BitSet dontNeedEncoding; - static final int caseDiff = ('a' - 'A'); - - /* The list of characters that are not encoded have been determined by - referencing O'Reilly's "HTML: The Definitive Guide" (page 164). */ - - static { - dontNeedEncoding = new BitSet(256); - int i; - for (i = 'a'; i <= 'z'; i++) { - dontNeedEncoding.set(i); - } - for (i = 'A'; i <= 'Z'; i++) { - dontNeedEncoding.set(i); - } - for (i = '0'; i <= '9'; i++) { - dontNeedEncoding.set(i); - } - /* encoding a space to a + is done in the encode() method */ - dontNeedEncoding.set(' '); - dontNeedEncoding.set('-'); - dontNeedEncoding.set('_'); - dontNeedEncoding.set('.'); - dontNeedEncoding.set('*'); - } - - /** - * Translates a string into x-www-form-urlencoded format. - * - * @param s String to be translated. - * @return the translated String. - */ - static String encode(String s) { - if (s == null) - return null; - // the common case is no encoding is needed - for (int i = 0; i < s.length(); i++) { - int c = (int)s.charAt(i); - if (c == ' ' || !dontNeedEncoding.get(c)) - return _encode(s); - } - return s; - } - - private static String _encode(String s) { - int maxBytesPerChar = 10; - StringBuffer out = new StringBuffer(s.length()); - ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); - OutputStreamWriter writer = new OutputStreamWriter(buf); - - for (int i = 0; i < s.length(); i++) { - int c = (int)s.charAt(i); - if (dontNeedEncoding.get(c)) { - if (c == ' ') { - c = '+'; - } - out.append((char)c); - } else { - // convert to external encoding before hex conversion - try { - writer.write(c); - writer.flush(); - } catch(IOException e) { - buf.reset(); - continue; - } - byte[] ba = buf.toByteArray(); - for (int j = 0; j < ba.length; j++) { - out.append('%'); - char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); - // converting to use uppercase letter as part of - // the hex value if ch is a letter. - if (Character.isLetter(ch)) { - ch -= caseDiff; - } - out.append(ch); - ch = Character.forDigit(ba[j] & 0xF, 16); - if (Character.isLetter(ch)) { - ch -= caseDiff; - } - out.append(ch); - } - buf.reset(); - } - } - - return out.toString(); - } - - - /** - * The class contains a utility method for converting from - * a MIME format called "x-www-form-urlencoded" - * to a String - *

      - * To convert to a String, each character is examined in turn: - *

        - *
      • The ASCII characters 'a' through 'z', - * 'A' through 'Z', and '0' - * through '9' remain the same. - *
      • The plus sign '+'is converted into a - * space character ' '. - *
      • The remaining characters are represented by 3-character - * strings which begin with the percent sign, - * "%xy", where xy is the two-digit - * hexadecimal representation of the lower 8-bits of the character. - *
      - * - * @author Mark Chamness - * @author Michael McCloskey - * @since 1.2 - */ - - /** - * Decodes a "x-www-form-urlencoded" - * to a String. - * @param s the String to decode - * @return the newly decoded String - */ - static String decode(String s) { - if (s == null) - return null; - if (indexOfAny(s, "+%") == -1) - return s; // the common case - - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - switch (c) { - case '+': - sb.append(' '); - break; - case '%': - try { - sb.append((char)Integer.parseInt( - s.substring(i+1,i+3),16)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - "Illegal URL encoded value: " + - s.substring(i,i+3)); - } - i += 2; - break; - default: - sb.append(c); - break; - } - } - // Undo conversion to external encoding - String result = sb.toString(); - try { - byte[] inputBytes = result.getBytes("8859_1"); - result = new String(inputBytes); - } catch (UnsupportedEncodingException e) { - // The system should always have 8859_1 - } - return result; - } - - /** - * Return the first index of any of the characters in "any" in "s", - * or -1 if none are found. - * - * This should be a method on String. - */ - private static int indexOfAny(String s, String any) { - return indexOfAny(s, any, 0); - } - - private static int indexOfAny(String s, String any, int start) { - try { - int len = s.length(); - for (int i = start; i < len; i++) { - if (any.indexOf(s.charAt(i)) >= 0) - return i; - } - return -1; - } catch (StringIndexOutOfBoundsException e) { - return -1; - } - } - - /* - // Do not remove, this is needed when testing new URL cases - public static void main(String[] argv) { - String [] testURLNames = { - "protocol://userid:password@host:119/file", - "http://funny/folder/file.html", - "http://funny/folder/file.html#ref", - "http://funny/folder/file.html#", - "http://funny/#ref", - "imap://jmr:secret@labyrinth//var/mail/jmr", - "nntp://fred@labyrinth:143/save/it/now.mbox", - "imap://jmr@labyrinth/INBOX", - "imap://labryrinth", - "imap://labryrinth/", - "file:", - "file:INBOX", - "file:/home/shannon/mail/foo", - "/tmp/foo", - "//host/tmp/foo", - ":/tmp/foo", - "/really/weird:/tmp/foo#bar", - "" - }; - - URLName url = - new URLName("protocol", "host", 119, "file", "userid", "password"); - System.out.println("Test URL: " + url.toString()); - if (argv.length == 0) { - for (int i = 0; i < testURLNames.length; i++) { - print(testURLNames[i]); - System.out.println(); - } - } else { - for (int i = 0; i < argv.length; i++) { - print(argv[i]); - System.out.println(); - } - if (argv.length == 2) { - URLName u1 = new URLName(argv[0]); - URLName u2 = new URLName(argv[1]); - System.out.println("URL1 hash code: " + u1.hashCode()); - System.out.println("URL2 hash code: " + u2.hashCode()); - if (u1.equals(u2)) - System.out.println("success, equal"); - else - System.out.println("fail, not equal"); - if (u2.equals(u1)) - System.out.println("success, equal"); - else - System.out.println("fail, not equal"); - if (u1.hashCode() == u2.hashCode()) - System.out.println("success, hashCodes equal"); - else - System.out.println("fail, hashCodes not equal"); - } - } - } - - private static void print(String name) { - URLName url = new URLName(name); - System.out.println("Original URL: " + name); - System.out.println("The fullUrl : " + url.toString()); - if (!name.equals(url.toString())) - System.out.println(" : NOT EQUAL!"); - System.out.println("The protocol is: " + url.getProtocol()); - System.out.println("The host is: " + url.getHost()); - System.out.println("The port is: " + url.getPort()); - System.out.println("The user is: " + url.getUsername()); - System.out.println("The password is: " + url.getPassword()); - System.out.println("The file is: " + url.getFile()); - System.out.println("The ref is: " + url.getRef()); - } - */ -} diff --git a/src/main/java/javax/mail/event/ConnectionAdapter.java b/src/main/java/javax/mail/event/ConnectionAdapter.java deleted file mode 100644 index bba2da92..00000000 --- a/src/main/java/javax/mail/event/ConnectionAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -/** - * The adapter which receives connection events. - * The methods in this class are empty; this class is provided as a - * convenience for easily creating listeners by extending this class - * and overriding only the methods of interest. - * - * @author John Mani - */ -public abstract class ConnectionAdapter implements ConnectionListener { - public void opened(ConnectionEvent e) {} - public void disconnected(ConnectionEvent e) {} - public void closed(ConnectionEvent e) {} -} diff --git a/src/main/java/javax/mail/event/ConnectionEvent.java b/src/main/java/javax/mail/event/ConnectionEvent.java deleted file mode 100644 index 1a6cd02a..00000000 --- a/src/main/java/javax/mail/event/ConnectionEvent.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class models Connection events. - * - * @author John Mani - */ - -public class ConnectionEvent extends MailEvent { - - /** A connection was opened. */ - public static final int OPENED = 1; - /** A connection was disconnected (not currently used). */ - public static final int DISCONNECTED = 2; - /** A connection was closed. */ - public static final int CLOSED = 3; - - /** - * The event type. - * - * @serial - */ - protected int type; - - private static final long serialVersionUID = -1855480171284792957L; - - /** - * Constructor - * @param source The source object - */ - public ConnectionEvent(Object source, int type) { - super(source); - this.type = type; - } - - /** - * Return the type of this event - * @return type - */ - public int getType() { - return type; - } - - /** - * Invokes the appropriate ConnectionListener method - */ - public void dispatch(Object listener) { - if (type == OPENED) - ((ConnectionListener)listener).opened(this); - else if (type == DISCONNECTED) - ((ConnectionListener)listener).disconnected(this); - else if (type == CLOSED) - ((ConnectionListener)listener).closed(this); - } -} diff --git a/src/main/java/javax/mail/event/ConnectionListener.java b/src/main/java/javax/mail/event/ConnectionListener.java deleted file mode 100644 index 1e7fa4e7..00000000 --- a/src/main/java/javax/mail/event/ConnectionListener.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for Connection events. - * - * @author John Mani - */ - -public interface ConnectionListener extends java.util.EventListener { - - /** - * Invoked when a Store/Folder/Transport is opened. - */ - public void opened(ConnectionEvent e); - - /** - * Invoked when a Store is disconnected. Note that a folder - * cannot be disconnected, so a folder will not fire this event - */ - public void disconnected(ConnectionEvent e); - - /** - * Invoked when a Store/Folder/Transport is closed. - */ - public void closed(ConnectionEvent e); -} diff --git a/src/main/java/javax/mail/event/FolderAdapter.java b/src/main/java/javax/mail/event/FolderAdapter.java deleted file mode 100644 index ae9cbda2..00000000 --- a/src/main/java/javax/mail/event/FolderAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -/** - * The adapter which receives Folder events. - * The methods in this class are empty; this class is provided as a - * convenience for easily creating listeners by extending this class - * and overriding only the methods of interest. - * - * @author John Mani - */ -public abstract class FolderAdapter implements FolderListener { - public void folderCreated(FolderEvent e) {} - public void folderRenamed(FolderEvent e) {} - public void folderDeleted(FolderEvent e) {} -} diff --git a/src/main/java/javax/mail/event/FolderEvent.java b/src/main/java/javax/mail/event/FolderEvent.java deleted file mode 100644 index 1d48fa15..00000000 --- a/src/main/java/javax/mail/event/FolderEvent.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class models Folder existence events. FolderEvents are - * delivered to FolderListeners registered on the affected Folder as - * well as the containing Store.

      - * - * Service providers vary widely in their ability to notify clients of - * these events. At a minimum, service providers must notify listeners - * registered on the same Store or Folder object on which the operation - * occurs. Service providers may also notify listeners when changes - * are made through operations on other objects in the same virtual - * machine, or by other clients in the same or other hosts. Such - * notifications are not required and are typically not supported - * by mail protocols (including IMAP). - * - * @author John Mani - * @author Bill Shannon - */ - -public class FolderEvent extends MailEvent { - - /** The folder was created. */ - public static final int CREATED = 1; - /** The folder was deleted. */ - public static final int DELETED = 2; - /** The folder was renamed. */ - public static final int RENAMED = 3; - - /** - * The event type. - * - * @serial - */ - protected int type; - - /** - * The folder the event occurred on. - */ - transient protected Folder folder; - - /** - * The folder that represents the new name, in case of a RENAMED event. - * - * @since JavaMail 1.1 - */ - transient protected Folder newFolder; - - private static final long serialVersionUID = 5278131310563694307L; - - /** - * Constructor.

      - * - * @param source The source of the event - * @param folder The affected folder - * @param type The event type - */ - public FolderEvent(Object source, Folder folder, int type) { - this(source, folder, folder, type); - } - - /** - * Constructor. Use for RENAMED events. - * - * @param source The source of the event - * @param oldFolder The folder that is renamed - * @param newFolder The folder that represents the new name - * @param type The event type - * @since JavaMail 1.1 - */ - public FolderEvent(Object source, Folder oldFolder, - Folder newFolder, int type) { - super(source); - this.folder = oldFolder; - this.newFolder = newFolder; - this.type = type; - } - - /** - * Return the type of this event. - * - * @return type - */ - public int getType() { - return type; - } - - /** - * Return the affected folder. - * - * @return the affected folder - * @see #getNewFolder - */ - public Folder getFolder() { - return folder; - } - - /** - * If this event indicates that a folder is renamed, (i.e, the event type - * is RENAMED), then this method returns the Folder object representing the - * new name.

      - * - * The getFolder() method returns the folder that is renamed. - * - * @return Folder representing the new name. - * @see #getFolder - * @since JavaMail 1.1 - */ - public Folder getNewFolder() { - return newFolder; - } - - /** - * Invokes the appropriate FolderListener method - */ - public void dispatch(Object listener) { - if (type == CREATED) - ((FolderListener)listener).folderCreated(this); - else if (type == DELETED) - ((FolderListener)listener).folderDeleted(this); - else if (type == RENAMED) - ((FolderListener)listener).folderRenamed(this); - } -} diff --git a/src/main/java/javax/mail/event/FolderListener.java b/src/main/java/javax/mail/event/FolderListener.java deleted file mode 100644 index 118520e1..00000000 --- a/src/main/java/javax/mail/event/FolderListener.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for Folder events. - * - * @author John Mani - */ - -public interface FolderListener extends java.util.EventListener { - /** - * Invoked when a Folder is created. - */ - public void folderCreated(FolderEvent e); - - /** - * Invoked when a folder is deleted. - */ - public void folderDeleted(FolderEvent e); - - /** - * Invoked when a folder is renamed. - */ - public void folderRenamed(FolderEvent e); -} diff --git a/src/main/java/javax/mail/event/MailEvent.java b/src/main/java/javax/mail/event/MailEvent.java deleted file mode 100644 index 0681a653..00000000 --- a/src/main/java/javax/mail/event/MailEvent.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.EventObject; - -/** - * Common base class for mail events, defining the dispatch method. - * - * @author Bill Shannon - */ - -public abstract class MailEvent extends EventObject { - private static final long serialVersionUID = 1846275636325456631L; - - public MailEvent(Object source) { - super(source); - } - - /** - * This method invokes the appropriate method on a listener for - * this event. Subclasses provide the implementation. - */ - public abstract void dispatch(Object listener); -} diff --git a/src/main/java/javax/mail/event/MessageChangedEvent.java b/src/main/java/javax/mail/event/MessageChangedEvent.java deleted file mode 100644 index bdfff176..00000000 --- a/src/main/java/javax/mail/event/MessageChangedEvent.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class models Message change events. - * - * @author John Mani - */ - -public class MessageChangedEvent extends MailEvent { - - /** The message's flags changed. */ - public static final int FLAGS_CHANGED = 1; - /** The message's envelope (headers, but not body) changed. */ - public static final int ENVELOPE_CHANGED = 2; - - /** - * The event type. - * - * @serial - */ - protected int type; - - /** - * The message that changed. - */ - transient protected Message msg; - - private static final long serialVersionUID = -4974972972105535108L; - - /** - * Constructor. - * @param source The folder that owns the message - * @param type The change type - * @param msg The changed message - */ - public MessageChangedEvent(Object source, int type, Message msg) { - super(source); - this.msg = msg; - this.type = type; - } - - /** - * Return the type of this event. - * @return type - */ - public int getMessageChangeType() { - return type; - } - - /** - * Return the changed Message. - * @return the message - */ - public Message getMessage() { - return msg; - } - - /** - * Invokes the appropriate MessageChangedListener method. - */ - public void dispatch(Object listener) { - ((MessageChangedListener)listener).messageChanged(this); - } -} diff --git a/src/main/java/javax/mail/event/MessageChangedListener.java b/src/main/java/javax/mail/event/MessageChangedListener.java deleted file mode 100644 index 43cf6a42..00000000 --- a/src/main/java/javax/mail/event/MessageChangedListener.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for MessageChanged events - * - * @author John Mani - */ - -public interface MessageChangedListener extends java.util.EventListener { - /** - * Invoked when a message is changed. The change-type specifies - * what changed. - * @see MessageChangedEvent#FLAGS_CHANGED - * @see MessageChangedEvent#ENVELOPE_CHANGED - */ - public void messageChanged(MessageChangedEvent e); -} diff --git a/src/main/java/javax/mail/event/MessageCountAdapter.java b/src/main/java/javax/mail/event/MessageCountAdapter.java deleted file mode 100644 index 2795b133..00000000 --- a/src/main/java/javax/mail/event/MessageCountAdapter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -/** - * The adapter which receives MessageCount events. - * The methods in this class are empty; this class is provided as a - * convenience for easily creating listeners by extending this class - * and overriding only the methods of interest. - * - * @author John Mani - */ -public abstract class MessageCountAdapter implements MessageCountListener { - public void messagesAdded(MessageCountEvent e) {} - public void messagesRemoved(MessageCountEvent e) {} -} diff --git a/src/main/java/javax/mail/event/MessageCountEvent.java b/src/main/java/javax/mail/event/MessageCountEvent.java deleted file mode 100644 index 7c1b825a..00000000 --- a/src/main/java/javax/mail/event/MessageCountEvent.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class notifies changes in the number of messages in a folder.

      - * - * Note that some folder types may only deliver MessageCountEvents at - * certain times or after certain operations. IMAP in particular will - * only notify the client of MessageCountEvents when a client issues a - * new command. - * Refer to RFC 2060 - * http://www.ietf.org/rfc/rfc2060.txt for details. - * A client may want "poll" the folder by occasionally calling the - * getMessageCount or isConnected methods - * to solicit any such notifications. - * - * @author John Mani - */ - -public class MessageCountEvent extends MailEvent { - - /** The messages were added to their folder */ - public static final int ADDED = 1; - /** The messages were removed from their folder */ - public static final int REMOVED = 2; - - /** - * The event type. - * - * @serial - */ - protected int type; - - /** - * If true, this event is the result of an explicit - * expunge by this client, and the messages in this - * folder have been renumbered to account for this. - * If false, this event is the result of an expunge - * by external sources. - * - * @serial - */ - protected boolean removed; - - /** - * The messages. - */ - transient protected Message[] msgs; - - private static final long serialVersionUID = -7447022340837897369L; - - /** - * Constructor. - * @param folder The containing folder - * @param type The event type - * @param removed If true, this event is the result of an explicit - * expunge by this client, and the messages in this - * folder have been renumbered to account for this. - * If false, this event is the result of an expunge - * by external sources. - * - * @param msgs The messages added/removed - */ - public MessageCountEvent(Folder folder, int type, - boolean removed, Message[] msgs) { - super(folder); - this.type = type; - this.removed = removed; - this.msgs = msgs; - } - - /** - * Return the type of this event. - * @return type - */ - public int getType() { - return type; - } - - /** - * Indicates whether this event is the result of an explicit - * expunge by this client, or due to an expunge from external - * sources. If true, this event is due to an - * explicit expunge and hence all remaining messages in this - * folder have been renumbered. If false, this event - * is due to an external expunge.

      - * - * Note that this method is valid only if the type of this event - * is REMOVED - */ - public boolean isRemoved() { - return removed; - } - - /** - * Return the array of messages added or removed. - * @return array of messages - */ - public Message[] getMessages() { - return msgs; - } - - /** - * Invokes the appropriate MessageCountListener method. - */ - public void dispatch(Object listener) { - if (type == ADDED) - ((MessageCountListener)listener).messagesAdded(this); - else // REMOVED - ((MessageCountListener)listener).messagesRemoved(this); - } -} diff --git a/src/main/java/javax/mail/event/MessageCountListener.java b/src/main/java/javax/mail/event/MessageCountListener.java deleted file mode 100644 index 1b40268a..00000000 --- a/src/main/java/javax/mail/event/MessageCountListener.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for MessageCount events. - * - * @author John Mani - */ - -public interface MessageCountListener extends java.util.EventListener { - /** - * Invoked when messages are added into a folder. - */ - public void messagesAdded(MessageCountEvent e); - - /** - * Invoked when messages are removed (expunged) from a folder. - */ - public void messagesRemoved(MessageCountEvent e); -} diff --git a/src/main/java/javax/mail/event/StoreEvent.java b/src/main/java/javax/mail/event/StoreEvent.java deleted file mode 100644 index a4f2190d..00000000 --- a/src/main/java/javax/mail/event/StoreEvent.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class models notifications from the Store connection. These - * notifications can be ALERTS or NOTICES. ALERTS must be presented - * to the user in a fashion that calls the user's attention to the - * message. - * - * @author John Mani - */ - -public class StoreEvent extends MailEvent { - - /** - * Indicates that this message is an ALERT. - */ - public static final int ALERT = 1; - - /** - * Indicates that this message is a NOTICE. - */ - public static final int NOTICE = 2; - - /** - * The event type. - * - * @serial - */ - protected int type; - - /** - * The message text to be presented to the user. - * - * @serial - */ - protected String message; - - private static final long serialVersionUID = 1938704919992515330L; - - /** - * Constructor. - * @param store The source Store - */ - public StoreEvent(Store store, int type, String message) { - super(store); - this.type = type; - this.message = message; - } - - /** - * Return the type of this event. - * - * @return type - * @see #ALERT - * @see #NOTICE - */ - public int getMessageType() { - return type; - } - - /** - * Get the message from the Store. - * - * @return message from the Store - */ - public String getMessage() { - return message; - } - - /** - * Invokes the appropriate StoreListener method. - */ - public void dispatch(Object listener) { - ((StoreListener)listener).notification(this); - } -} diff --git a/src/main/java/javax/mail/event/StoreListener.java b/src/main/java/javax/mail/event/StoreListener.java deleted file mode 100644 index e76356ce..00000000 --- a/src/main/java/javax/mail/event/StoreListener.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for Store Notifications. - * - * @author John Mani - */ - -public interface StoreListener extends java.util.EventListener { - - /** - * Invoked when the Store generates a notification event. - * - * @see StoreEvent#ALERT - * @see StoreEvent#NOTICE - */ - public void notification(StoreEvent e); -} diff --git a/src/main/java/javax/mail/event/TransportAdapter.java b/src/main/java/javax/mail/event/TransportAdapter.java deleted file mode 100644 index 155fc715..00000000 --- a/src/main/java/javax/mail/event/TransportAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -/** - * The adapter which receives Transport events. - * The methods in this class are empty; this class is provided as a - * convenience for easily creating listeners by extending this class - * and overriding only the methods of interest. - * - * @author John Mani - */ -public abstract class TransportAdapter implements TransportListener { - public void messageDelivered(TransportEvent e) {} - public void messageNotDelivered(TransportEvent e) {} - public void messagePartiallyDelivered(TransportEvent e) {} -} diff --git a/src/main/java/javax/mail/event/TransportEvent.java b/src/main/java/javax/mail/event/TransportEvent.java deleted file mode 100644 index a8b90bcc..00000000 --- a/src/main/java/javax/mail/event/TransportEvent.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; -import javax.mail.*; - -/** - * This class models Transport events. - * - * @author John Mani - * @author Max Spivak - * - * @see javax.mail.Transport - * @see javax.mail.event.TransportListener - */ - -public class TransportEvent extends MailEvent { - - /** - * Message has been successfully delivered to all recipients by the - * transport firing this event. validSent[] contains all the addresses - * this transport sent to successfully. validUnsent[] and invalid[] - * should be null, - */ - public static final int MESSAGE_DELIVERED = 1; - - /** - * Message was not sent for some reason. validSent[] should be null. - * validUnsent[] may have addresses that are valid (but the message - * wasn't sent to them). invalid[] should likely contain invalid addresses. - */ - public static final int MESSAGE_NOT_DELIVERED = 2; - - /** - * Message was successfully sent to some recipients but not to all. - * validSent[] holds addresses of recipients to whom the message was sent. - * validUnsent[] holds valid addresses to which the message was not sent. - * invalid[] holds invalid addresses, if any. - */ - public static final int MESSAGE_PARTIALLY_DELIVERED = 3; - - - /** - * The event type. - * - * @serial - */ - protected int type; - - transient protected Address[] validSent; - transient protected Address[] validUnsent; - transient protected Address[] invalid; - transient protected Message msg; - - private static final long serialVersionUID = -4729852364684273073L; - - /** - * Constructor. - * @param transport The Transport object - */ - public TransportEvent(Transport transport, int type, Address[] validSent, - Address[] validUnsent, Address[] invalid, - Message msg) { - super(transport); - this.type = type; - this.validSent = validSent; - this.validUnsent = validUnsent; - this.invalid = invalid; - this.msg = msg; - } - - /** - * Return the type of this event. - * @return type - */ - public int getType() { - return type; - } - - /** - * Return the addresses to which this message was sent succesfully. - * @return Addresses to which the message was sent successfully or null - */ - public Address[] getValidSentAddresses() { - return validSent; - } - - /** - * Return the addresses that are valid but to which this message - * was not sent. - * @return Addresses that are valid but to which the message was - * not sent successfully or null - */ - public Address[] getValidUnsentAddresses() { - return validUnsent; - } - - /** - * Return the addresses to which this message could not be sent. - * @return Addresses to which the message sending failed or null - */ - public Address[] getInvalidAddresses() { - return invalid; - } - - /** - * Get the Message object associated with this Transport Event. - * - * @return the Message object - * @since JavaMail 1.2 - */ - public Message getMessage() { - return msg; - } - - /** - * Invokes the appropriate TransportListener method. - */ - public void dispatch(Object listener) { - if (type == MESSAGE_DELIVERED) - ((TransportListener)listener).messageDelivered(this); - else if (type == MESSAGE_NOT_DELIVERED) - ((TransportListener)listener).messageNotDelivered(this); - else // MESSAGE_PARTIALLY_DELIVERED - ((TransportListener)listener).messagePartiallyDelivered(this); - } -} diff --git a/src/main/java/javax/mail/event/TransportListener.java b/src/main/java/javax/mail/event/TransportListener.java deleted file mode 100644 index 2b90b073..00000000 --- a/src/main/java/javax/mail/event/TransportListener.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.event; - -import java.util.*; - -/** - * This is the Listener interface for Transport events - * - * @author John Mani - * @author Max Spivak - * - * @see javax.mail.Transport - * @see javax.mail.event.TransportEvent - */ - -public interface TransportListener extends java.util.EventListener { - - /** - * Invoked when a Message is succesfully delivered. - * @param e TransportEvent - */ - public void messageDelivered(TransportEvent e); - - /** - * Invoked when a Message is not delivered. - * @param e TransportEvent - * @see TransportEvent - */ - public void messageNotDelivered(TransportEvent e); - - /** - * Invoked when a Message is partially delivered. - * @param e TransportEvent - * @see TransportEvent - */ - public void messagePartiallyDelivered(TransportEvent e); -} diff --git a/src/main/java/javax/mail/event/package.html b/src/main/java/javax/mail/event/package.html deleted file mode 100644 index 05dfb074..00000000 --- a/src/main/java/javax/mail/event/package.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - -Listeners and events for the JavaMail API. -This package defines listener classes and event classes used by the classes -defined in the javax.mail package. - - - diff --git a/src/main/java/javax/mail/internet/AddressException.java b/src/main/java/javax/mail/internet/AddressException.java deleted file mode 100644 index a8c03545..00000000 --- a/src/main/java/javax/mail/internet/AddressException.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -/** - * The exception thrown when a wrongly formatted address is encountered. - * - * @author Bill Shannon - * @author Max Spivak - */ - -public class AddressException extends ParseException { - /** - * The string being parsed. - * - * @serial - */ - protected String ref = null; - - /** - * The index in the string where the error occurred, or -1 if not known. - * - * @serial - */ - protected int pos = -1; - - private static final long serialVersionUID = 9134583443539323120L; - - /** - * Constructs an AddressException with no detail message. - */ - public AddressException() { - super(); - } - - /** - * Constructs an AddressException with the specified detail message. - * @param s the detail message - */ - public AddressException(String s) { - super(s); - } - - /** - * Constructs an AddressException with the specified detail message - * and reference info. - * - * @param s the detail message - */ - - public AddressException(String s, String ref) { - super(s); - this.ref = ref; - } - /** - * Constructs an AddressException with the specified detail message - * and reference info. - * - * @param s the detail message - */ - public AddressException(String s, String ref, int pos) { - super(s); - this.ref = ref; - this.pos = pos; - } - - /** - * Get the string that was being parsed when the error was detected - * (null if not relevant). - */ - public String getRef() { - return ref; - } - - /** - * Get the position with the reference string where the error was - * detected (-1 if not relevant). - */ - public int getPos() { - return pos; - } - - public String toString() { - String s = super.toString(); - if (ref == null) - return s; - s += " in string ``" + ref + "''"; - if (pos < 0) - return s; - return s + " at position " + pos; - } -} diff --git a/src/main/java/javax/mail/internet/ContentDisposition.java b/src/main/java/javax/mail/internet/ContentDisposition.java deleted file mode 100644 index cb5e7006..00000000 --- a/src/main/java/javax/mail/internet/ContentDisposition.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import java.util.*; -import java.io.*; - -/** - * This class represents a MIME ContentDisposition value. It provides - * methods to parse a ContentDisposition string into individual components - * and to generate a MIME style ContentDisposition string. - * - * @author John Mani - */ - -public class ContentDisposition { - - private String disposition; // disposition - private ParameterList list; // parameter list - - /** - * No-arg Constructor. - */ - public ContentDisposition() { } - - /** - * Constructor. - * - * @param disposition disposition - * @param list ParameterList - * @since JavaMail 1.2 - */ - public ContentDisposition(String disposition, ParameterList list) { - this.disposition = disposition; - this.list = list; - } - - /** - * Constructor that takes a ContentDisposition string. The String - * is parsed into its constituents: dispostion and parameters. - * A ParseException is thrown if the parse fails. - * - * @param s the ContentDisposition string. - * @exception ParseException if the parse fails. - * @since JavaMail 1.2 - */ - public ContentDisposition(String s) throws ParseException { - HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); - HeaderTokenizer.Token tk; - - // First "disposition" .. - tk = h.next(); - if (tk.getType() != HeaderTokenizer.Token.ATOM) - throw new ParseException("Expected disposition, got " + - tk.getValue()); - disposition = tk.getValue(); - - // Then parameters .. - String rem = h.getRemainder(); - if (rem != null) - list = new ParameterList(rem); - } - - /** - * Return the disposition value. - * @return the disposition - * @since JavaMail 1.2 - */ - public String getDisposition() { - return disposition; - } - - /** - * Return the specified parameter value. Returns null - * if this parameter is absent. - * @return parameter value - * @since JavaMail 1.2 - */ - public String getParameter(String name) { - if (list == null) - return null; - - return list.get(name); - } - - /** - * Return a ParameterList object that holds all the available - * parameters. Returns null if no parameters are available. - * - * @return ParameterList - * @since JavaMail 1.2 - */ - public ParameterList getParameterList() { - return list; - } - - /** - * Set the disposition. Replaces the existing disposition. - * @param disposition the disposition - * @since JavaMail 1.2 - */ - public void setDisposition(String disposition) { - this.disposition = disposition; - } - - /** - * Set the specified parameter. If this parameter already exists, - * it is replaced by this new value. - * - * @param name parameter name - * @param value parameter value - * @since JavaMail 1.2 - */ - public void setParameter(String name, String value) { - if (list == null) - list = new ParameterList(); - - list.set(name, value); - } - - /** - * Set a new ParameterList. - * @param list ParameterList - * @since JavaMail 1.2 - */ - public void setParameterList(ParameterList list) { - this.list = list; - } - - /** - * Retrieve a RFC2045 style string representation of - * this ContentDisposition. Returns an empty string if - * the conversion failed. - * - * @return RFC2045 style string - * @since JavaMail 1.2 - */ - public String toString() { - if (disposition == null) - return ""; - - if (list == null) - return disposition; - - StringBuffer sb = new StringBuffer(disposition); - - // append the parameter list - // use the length of the string buffer + the length of - // the header name formatted as follows "Content-Disposition: " - sb.append(list.toString(sb.length() + 21)); - return sb.toString(); - } -} diff --git a/src/main/java/javax/mail/internet/ContentType.java b/src/main/java/javax/mail/internet/ContentType.java deleted file mode 100644 index 8d8adc59..00000000 --- a/src/main/java/javax/mail/internet/ContentType.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import java.util.*; -import java.io.*; - -/** - * This class represents a MIME Content-Type value. It provides - * methods to parse a Content-Type string into individual components - * and to generate a MIME style Content-Type string. - * - * @author John Mani - */ - -public class ContentType { - - private String primaryType; // primary type - private String subType; // subtype - private ParameterList list; // parameter list - - /** - * No-arg Constructor. - */ - public ContentType() { } - - /** - * Constructor. - * - * @param primaryType primary type - * @param subType subType - * @param list ParameterList - */ - public ContentType(String primaryType, String subType, - ParameterList list) { - this.primaryType = primaryType; - this.subType = subType; - this.list = list; - } - - /** - * Constructor that takes a Content-Type string. The String - * is parsed into its constituents: primaryType, subType - * and parameters. A ParseException is thrown if the parse fails. - * - * @param s the Content-Type string. - * @exception ParseException if the parse fails. - */ - public ContentType(String s) throws ParseException { - HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); - HeaderTokenizer.Token tk; - - // First "type" .. - tk = h.next(); - if (tk.getType() != HeaderTokenizer.Token.ATOM) - throw new ParseException("Expected MIME type, got " + - tk.getValue()); - primaryType = tk.getValue(); - - // The '/' separator .. - tk = h.next(); - if ((char)tk.getType() != '/') - throw new ParseException("Expected '/', got " + tk.getValue()); - - // Then "subType" .. - tk = h.next(); - if (tk.getType() != HeaderTokenizer.Token.ATOM) - throw new ParseException("Expected MIME subtype, got " + - tk.getValue()); - subType = tk.getValue(); - - // Finally parameters .. - String rem = h.getRemainder(); - if (rem != null) - list = new ParameterList(rem); - } - - /** - * Return the primary type. - * @return the primary type - */ - public String getPrimaryType() { - return primaryType; - } - - /** - * Return the subType. - * @return the subType - */ - public String getSubType() { - return subType; - } - - /** - * Return the MIME type string, without the parameters. - * The returned value is basically the concatenation of - * the primaryType, the '/' character and the secondaryType. - * - * @return the type - */ - public String getBaseType() { - return primaryType + '/' + subType; - } - - /** - * Return the specified parameter value. Returns null - * if this parameter is absent. - * @return parameter value - */ - public String getParameter(String name) { - if (list == null) - return null; - - return list.get(name); - } - - /** - * Return a ParameterList object that holds all the available - * parameters. Returns null if no parameters are available. - * - * @return ParameterList - */ - public ParameterList getParameterList() { - return list; - } - - /** - * Set the primary type. Overrides existing primary type. - * @param primaryType primary type - */ - public void setPrimaryType(String primaryType) { - this.primaryType = primaryType; - } - - /** - * Set the subType. Replaces the existing subType. - * @param subType the subType - */ - public void setSubType(String subType) { - this.subType = subType; - } - - /** - * Set the specified parameter. If this parameter already exists, - * it is replaced by this new value. - * - * @param name parameter name - * @param value parameter value - */ - public void setParameter(String name, String value) { - if (list == null) - list = new ParameterList(); - - list.set(name, value); - } - - /** - * Set a new ParameterList. - * @param list ParameterList - */ - public void setParameterList(ParameterList list) { - this.list = list; - } - - /** - * Retrieve a RFC2045 style string representation of - * this Content-Type. Returns an empty string if - * the conversion failed. - * - * @return RFC2045 style string - */ - public String toString() { - if (primaryType == null || subType == null) // need both - return ""; - - StringBuffer sb = new StringBuffer(); - sb.append(primaryType).append('/').append(subType); - if (list != null) - // append the parameter list - // use the length of the string buffer + the length of - // the header name formatted as follows "Content-Type: " - sb.append(list.toString(sb.length() + 14)); - - return sb.toString(); - } - - /** - * Match with the specified ContentType object. This method - * compares only the primaryType and - * subType . The parameters of both operands - * are ignored.

      - * - * For example, this method will return true when - * comparing the ContentTypes for "text/plain" - * and "text/plain; charset=foobar". - * - * If the subType of either operand is the special - * character '*', then the subtype is ignored during the match. - * For example, this method will return true when - * comparing the ContentTypes for "text/plain" - * and "text/*" - * - * @param cType ContentType to compare this against - */ - public boolean match(ContentType cType) { - // Match primaryType - if (!primaryType.equalsIgnoreCase(cType.getPrimaryType())) - return false; - - String sType = cType.getSubType(); - - // If either one of the subTypes is wildcarded, return true - if ((subType.charAt(0) == '*') || (sType.charAt(0) == '*')) - return true; - - // Match subType - if (!subType.equalsIgnoreCase(sType)) - return false; - - return true; - } - - /** - * Match with the specified content-type string. This method - * compares only the primaryType and - * subType . - * The parameters of both operands are ignored.

      - * - * For example, this method will return true when - * comparing the ContentType for "text/plain" - * with "text/plain; charset=foobar". - * - * If the subType of either operand is the special - * character '*', then the subtype is ignored during the match. - * For example, this method will return true when - * comparing the ContentType for "text/plain" - * with "text/*" - */ - public boolean match(String s) { - try { - return match(new ContentType(s)); - } catch (ParseException pex) { - return false; - } - } -} diff --git a/src/main/java/javax/mail/internet/HeaderTokenizer.java b/src/main/java/javax/mail/internet/HeaderTokenizer.java deleted file mode 100644 index 3d956540..00000000 --- a/src/main/java/javax/mail/internet/HeaderTokenizer.java +++ /dev/null @@ -1,489 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.util.*; - -/** - * This class tokenizes RFC822 and MIME headers into the basic - * symbols specified by RFC822 and MIME.

      - * - * This class handles folded headers (ie headers with embedded - * CRLF SPACE sequences). The folds are removed in the returned - * tokens. - * - * @author John Mani - * @author Bill Shannon - */ - -public class HeaderTokenizer { - - /** - * The Token class represents tokens returned by the - * HeaderTokenizer. - */ - public static class Token { - - private int type; - private String value; - - /** - * Token type indicating an ATOM. - */ - public static final int ATOM = -1; - - /** - * Token type indicating a quoted string. The value - * field contains the string without the quotes. - */ - public static final int QUOTEDSTRING = -2; - - /** - * Token type indicating a comment. The value field - * contains the comment string without the comment - * start and end symbols. - */ - public static final int COMMENT = -3; - - /** - * Token type indicating end of input. - */ - public static final int EOF = -4; - - /** - * Constructor. - * @param type Token type - * @param value Token value - */ - public Token(int type, String value) { - this.type = type; - this.value = value; - } - - /** - * Return the type of the token. If the token represents a - * delimiter or a control character, the type is that character - * itself, converted to an integer. Otherwise, it's value is - * one of the following: - *

        - *
      • ATOM A sequence of ASCII characters - * delimited by either SPACE, CTL, "(", <"> or the - * specified SPECIALS - *
      • QUOTEDSTRING A sequence of ASCII characters - * within quotes - *
      • COMMENT A sequence of ASCII characters - * within "(" and ")". - *
      • EOF End of header - *
      - */ - public int getType() { - return type; - } - - /** - * Returns the value of the token just read. When the current - * token is a quoted string, this field contains the body of the - * string, without the quotes. When the current token is a comment, - * this field contains the body of the comment. - * - * @return token value - */ - public String getValue() { - return value; - } - } - - private String string; // the string to be tokenized - private boolean skipComments; // should comments be skipped ? - private String delimiters; // delimiter string - private int currentPos; // current parse position - private int maxPos; // string length - private int nextPos; // track start of next Token for next() - private int peekPos; // track start of next Token for peek() - - /** - * RFC822 specials - */ - public final static String RFC822 = "()<>@,;:\\\"\t .[]"; - - /** - * MIME specials - */ - public final static String MIME = "()<>@,;:\\\"\t []/?="; - - // The EOF Token - private final static Token EOFToken = new Token(Token.EOF, null); - - /** - * Constructor that takes a rfc822 style header. - * - * @param header The rfc822 header to be tokenized - * @param delimiters Set of delimiter characters - * to be used to delimit ATOMS. These - * are usually RFC822 or - * MIME - * @param skipComments If true, comments are skipped and - * not returned as tokens - */ - public HeaderTokenizer(String header, String delimiters, - boolean skipComments) { - string = (header == null) ? "" : header; // paranoia ?! - this.skipComments = skipComments; - this.delimiters = delimiters; - currentPos = nextPos = peekPos = 0; - maxPos = string.length(); - } - - /** - * Constructor. Comments are ignored and not returned as tokens - * - * @param header The header that is tokenized - * @param delimiters The delimiters to be used - */ - public HeaderTokenizer(String header, String delimiters) { - this(header, delimiters, true); - } - - /** - * Constructor. The RFC822 defined delimiters - RFC822 - are - * used to delimit ATOMS. Also comments are skipped and not - * returned as tokens - */ - public HeaderTokenizer(String header) { - this(header, RFC822); - } - - /** - * Parses the next token from this String.

      - * - * Clients sit in a loop calling next() to parse successive - * tokens until an EOF Token is returned. - * - * @return the next Token - * @exception ParseException if the parse fails - */ - public Token next() throws ParseException { - return next('\0', false); - } - - /** - * Parses the next token from this String. - * If endOfAtom is not NUL, the token extends until the - * endOfAtom character is seen, or to the end of the header. - * This method is useful when parsing headers that don't - * obey the MIME specification, e.g., by failing to quote - * parameter values that contain spaces. - * - * @param endOfAtom if not NUL, character marking end of token - * @return the next Token - * @exception ParseException if the parse fails - * @since JavaMail 1.5 - */ - public Token next(char endOfAtom) throws ParseException { - return next(endOfAtom, false); - } - - /** - * Parses the next token from this String. - * endOfAtom is handled as above. If keepEscapes is true, - * any backslash escapes are preserved in the returned string. - * This method is useful when parsing headers that don't - * obey the MIME specification, e.g., by failing to escape - * backslashes in the filename parameter. - * - * @param endOfAtom if not NUL, character marking end of token - * @param keepEscapes keep all backslashes in returned string? - * @return the next Token - * @exception ParseException if the parse fails - * @since JavaMail 1.5 - */ - public Token next(char endOfAtom, boolean keepEscapes) - throws ParseException { - Token tk; - - currentPos = nextPos; // setup currentPos - tk = getNext(endOfAtom, keepEscapes); - nextPos = peekPos = currentPos; // update currentPos and peekPos - return tk; - } - - /** - * Peek at the next token, without actually removing the token - * from the parse stream. Invoking this method multiple times - * will return successive tokens, until next() is - * called.

      - * - * @return the next Token - * @exception ParseException if the parse fails - */ - public Token peek() throws ParseException { - Token tk; - - currentPos = peekPos; // setup currentPos - tk = getNext('\0', false); - peekPos = currentPos; // update peekPos - return tk; - } - - /** - * Return the rest of the Header. - * - * @return String rest of header. null is returned if we are - * already at end of header - */ - public String getRemainder() { - return string.substring(nextPos); - } - - /* - * Return the next token starting from 'currentPos'. After the - * parse, 'currentPos' is updated to point to the start of the - * next token. - */ - private Token getNext(char endOfAtom, boolean keepEscapes) - throws ParseException { - // If we're already at end of string, return EOF - if (currentPos >= maxPos) - return EOFToken; - - // Skip white-space, position currentPos beyond the space - if (skipWhiteSpace() == Token.EOF) - return EOFToken; - - char c; - int start; - boolean filter = false; - - c = string.charAt(currentPos); - - // Check or Skip comments and position currentPos - // beyond the comment - while (c == '(') { - // Parsing comment .. - int nesting; - for (start = ++currentPos, nesting = 1; - nesting > 0 && currentPos < maxPos; - currentPos++) { - c = string.charAt(currentPos); - if (c == '\\') { // Escape sequence - currentPos++; // skip the escaped character - filter = true; - } else if (c == '\r') - filter = true; - else if (c == '(') - nesting++; - else if (c == ')') - nesting--; - } - if (nesting != 0) - throw new ParseException("Unbalanced comments"); - - if (!skipComments) { - // Return the comment, if we are asked to. - // Note that the comment start & end markers are ignored. - String s; - if (filter) // need to go thru the token again. - s = filterToken(string, start, currentPos-1, keepEscapes); - else - s = string.substring(start,currentPos-1); - - return new Token(Token.COMMENT, s); - } - - // Skip any whitespace after the comment. - if (skipWhiteSpace() == Token.EOF) - return EOFToken; - c = string.charAt(currentPos); - } - - // Check for quoted-string and position currentPos - // beyond the terminating quote - if (c == '"') { - currentPos++; // skip initial quote - return collectString('"', keepEscapes); - } - - // Check for SPECIAL or CTL - if (c < 040 || c >= 0177 || delimiters.indexOf(c) >= 0) { - if (endOfAtom > 0 && c != endOfAtom) { - // not expecting a special character here, - // pretend it's a quoted string - return collectString(endOfAtom, keepEscapes); - } - currentPos++; // re-position currentPos - char ch[] = new char[1]; - ch[0] = c; - return new Token((int)c, new String(ch)); - } - - // Check for ATOM - for (start = currentPos; currentPos < maxPos; currentPos++) { - c = string.charAt(currentPos); - // ATOM is delimited by either SPACE, CTL, "(", <"> - // or the specified SPECIALS - if (c < 040 || c >= 0177 || c == '(' || c == ' ' || - c == '"' || delimiters.indexOf(c) >= 0) { - if (endOfAtom > 0 && c != endOfAtom) { - // not the expected atom after all; - // back up and pretend it's a quoted string - currentPos = start; - return collectString(endOfAtom, keepEscapes); - } - break; - } - } - return new Token(Token.ATOM, string.substring(start, currentPos)); - } - - private Token collectString(char eos, boolean keepEscapes) - throws ParseException { - int start; - boolean filter = false; - for (start = currentPos; currentPos < maxPos; currentPos++) { - char c = string.charAt(currentPos); - if (c == '\\') { // Escape sequence - currentPos++; - filter = true; - } else if (c == '\r') - filter = true; - else if (c == eos) { - currentPos++; - String s; - - if (filter) - s = filterToken(string, start, currentPos-1, keepEscapes); - else - s = string.substring(start, currentPos-1); - - if (c != '"') { // not a real quoted string - s = trimWhiteSpace(s); - currentPos--; // back up before the eos char - } - - return new Token(Token.QUOTEDSTRING, s); - } - } - - // ran off the end of the string - - // if we're looking for a matching quote, that's an error - if (eos == '"') - throw new ParseException("Unbalanced quoted string"); - - // otherwise, just return whatever's left - String s; - if (filter) - s = filterToken(string, start, currentPos, keepEscapes); - else - s = string.substring(start, currentPos); - s = trimWhiteSpace(s); - return new Token(Token.QUOTEDSTRING, s); - } - - // Skip SPACE, HT, CR and NL - private int skipWhiteSpace() { - char c; - for (; currentPos < maxPos; currentPos++) - if (((c = string.charAt(currentPos)) != ' ') && - (c != '\t') && (c != '\r') && (c != '\n')) - return currentPos; - return Token.EOF; - } - - // Trim SPACE, HT, CR and NL from end of string - private static String trimWhiteSpace(String s) { - char c; - int i; - for (i = s.length() - 1; i >= 0; i--) { - if (((c = s.charAt(i)) != ' ') && - (c != '\t') && (c != '\r') && (c != '\n')) - break; - } - if (i <= 0) - return ""; - else - return s.substring(0, i + 1); - } - - /* Process escape sequences and embedded LWSPs from a comment or - * quoted string. - */ - private static String filterToken(String s, int start, int end, - boolean keepEscapes) { - StringBuffer sb = new StringBuffer(); - char c; - boolean gotEscape = false; - boolean gotCR = false; - - for (int i = start; i < end; i++) { - c = s.charAt(i); - if (c == '\n' && gotCR) { - // This LF is part of an unescaped - // CRLF sequence (i.e, LWSP). Skip it. - gotCR = false; - continue; - } - - gotCR = false; - if (!gotEscape) { - // Previous character was NOT '\' - if (c == '\\') // skip this character - gotEscape = true; - else if (c == '\r') // skip this character - gotCR = true; - else // append this character - sb.append(c); - } else { - // Previous character was '\'. So no need to - // bother with any special processing, just - // append this character. If keepEscapes is - // set, keep the backslash. IE6 fails to escape - // backslashes in quoted strings in HTTP headers, - // e.g., in the filename parameter. - if (keepEscapes) - sb.append('\\'); - sb.append(c); - gotEscape = false; - } - } - return sb.toString(); - } -} diff --git a/src/main/java/javax/mail/internet/InternetAddress.java b/src/main/java/javax/mail/internet/InternetAddress.java deleted file mode 100644 index 706144b8..00000000 --- a/src/main/java/javax/mail/internet/InternetAddress.java +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.List; -import java.util.ArrayList; -import java.util.StringTokenizer; -import java.util.Locale; -import javax.mail.*; -import com.sun.mail.util.PropUtil; - -/** - * This class represents an Internet email address using the syntax - * of RFC822. - * Typical address syntax is of the form "user@host.domain" or - * "Personal Name <user@host.domain>". - * - * @author Bill Shannon - * @author John Mani - */ - -public class InternetAddress extends Address implements Cloneable { - - protected String address; // email address - - /** - * The personal name. - */ - protected String personal; - - /** - * The RFC 2047 encoded version of the personal name.

      - * - * This field and the personal field track each - * other, so if a subclass sets one of these fields directly, it - * should set the other to null, so that it is - * suitably recomputed. - */ - protected String encodedPersonal; - - private static final long serialVersionUID = -7507595530758302903L; - - private static final boolean ignoreBogusGroupName = - PropUtil.getBooleanSystemProperty( - "mail.mime.address.ignorebogusgroupname", true); - - /** - * Default constructor. - */ - public InternetAddress() { } - - /** - * Constructor.

      - * - * Parse the given string and create an InternetAddress. - * See the parse method for details of the parsing. - * The address is parsed using "strict" parsing. - * This constructor does not perform the additional - * syntax checks that the - * InternetAddress(String address, boolean strict) - * constructor does when strict is true. - * This constructor is equivalent to - * InternetAddress(address, false). - * - * @param address the address in RFC822 format - * @exception AddressException if the parse failed - */ - public InternetAddress(String address) throws AddressException { - // use our address parsing utility routine to parse the string - InternetAddress a[] = parse(address, true); - // if we got back anything other than a single address, it's an error - if (a.length != 1) - throw new AddressException("Illegal address", address); - - /* - * Now copy the contents of the single address we parsed - * into the current object, which will be returned from the - * constructor. - * XXX - this sure is a round-about way of getting this done. - */ - this.address = a[0].address; - this.personal = a[0].personal; - this.encodedPersonal = a[0].encodedPersonal; - } - - /** - * Parse the given string and create an InternetAddress. - * If strict is false, the detailed syntax of the - * address isn't checked. - * - * @param address the address in RFC822 format - * @param strict enforce RFC822 syntax - * @exception AddressException if the parse failed - * @since JavaMail 1.3 - */ - public InternetAddress(String address, boolean strict) - throws AddressException { - this(address); - if (strict) { - if (isGroup()) - getGroup(true); // throw away the result - else - checkAddress(this.address, true, true); - } - } - - /** - * Construct an InternetAddress given the address and personal name. - * The address is assumed to be a syntactically valid RFC822 address. - * - * @param address the address in RFC822 format - * @param personal the personal name - */ - public InternetAddress(String address, String personal) - throws UnsupportedEncodingException { - this(address, personal, null); - } - - /** - * Construct an InternetAddress given the address and personal name. - * The address is assumed to be a syntactically valid RFC822 address. - * - * @param address the address in RFC822 format - * @param personal the personal name - * @param charset the MIME charset for the name - */ - public InternetAddress(String address, String personal, String charset) - throws UnsupportedEncodingException { - this.address = address; - setPersonal(personal, charset); - } - - /** - * Return a copy of this InternetAddress object. - * @since JavaMail 1.2 - */ - public Object clone() { - InternetAddress a = null; - try { - a = (InternetAddress)super.clone(); - } catch (CloneNotSupportedException e) {} // Won't happen - return a; - } - - /** - * Return the type of this address. The type of an InternetAddress - * is "rfc822". - */ - public String getType() { - return "rfc822"; - } - - /** - * Set the email address. - * - * @param address email address - */ - public void setAddress(String address) { - this.address = address; - } - - /** - * Set the personal name. If the name contains non US-ASCII - * characters, then the name will be encoded using the specified - * charset as per RFC 2047. If the name contains only US-ASCII - * characters, no encoding is done and the name is used as is.

      - * - * @param name personal name - * @param charset MIME charset to be used to encode the name as - * per RFC 2047 - * @see #setPersonal(String) - * @exception UnsupportedEncodingException if the charset encoding - * fails. - */ - public void setPersonal(String name, String charset) - throws UnsupportedEncodingException { - personal = name; - if (name != null) - encodedPersonal = MimeUtility.encodeWord(name, charset, null); - else - encodedPersonal = null; - } - - /** - * Set the personal name. If the name contains non US-ASCII - * characters, then the name will be encoded using the platform's - * default charset. If the name contains only US-ASCII characters, - * no encoding is done and the name is used as is.

      - * - * @param name personal name - * @see #setPersonal(String name, String charset) - * @exception UnsupportedEncodingException if the charset encoding - * fails. - */ - public void setPersonal(String name) - throws UnsupportedEncodingException { - personal = name; - if (name != null) - encodedPersonal = MimeUtility.encodeWord(name); - else - encodedPersonal = null; - } - - /** - * Get the email address. - * @return email address - */ - public String getAddress() { - return address; - } - - /** - * Get the personal name. If the name is encoded as per RFC 2047, - * it is decoded and converted into Unicode. If the decoding or - * conversion fails, the raw data is returned as is. - * - * @return personal name - */ - public String getPersonal() { - if (personal != null) - return personal; - - if (encodedPersonal != null) { - try { - personal = MimeUtility.decodeText(encodedPersonal); - return personal; - } catch (Exception ex) { - // 1. ParseException: either its an unencoded string or - // it can't be parsed - // 2. UnsupportedEncodingException: can't decode it. - return encodedPersonal; - } - } - // No personal or encodedPersonal, return null - return null; - } - - /** - * Convert this address into a RFC 822 / RFC 2047 encoded address. - * The resulting string contains only US-ASCII characters, and - * hence is mail-safe. - * - * @return possibly encoded address string - */ - public String toString() { - if (encodedPersonal == null && personal != null) - try { - encodedPersonal = MimeUtility.encodeWord(personal); - } catch (UnsupportedEncodingException ex) { } - - if (encodedPersonal != null) - return quotePhrase(encodedPersonal) + " <" + address + ">"; - else if (isGroup() || isSimple()) - return address; - else - return "<" + address + ">"; - } - - /** - * Returns a properly formatted address (RFC 822 syntax) of - * Unicode characters. - * - * @return Unicode address string - * @since JavaMail 1.2 - */ - public String toUnicodeString() { - String p = getPersonal(); - if (p != null) - return quotePhrase(p) + " <" + address + ">"; - else if (isGroup() || isSimple()) - return address; - else - return "<" + address + ">"; - } - - /* - * quotePhrase() quotes the words within a RFC822 phrase. - * - * This is tricky, since a phrase is defined as 1 or more - * RFC822 words, separated by LWSP. Now, a word that contains - * LWSP is supposed to be quoted, and this is exactly what the - * MimeUtility.quote() method does. However, when dealing with - * a phrase, any LWSP encountered can be construed to be the - * separator between words, and not part of the words themselves. - * To deal with this funkiness, we have the below variant of - * MimeUtility.quote(), which essentially ignores LWSP when - * deciding whether to quote a word. - * - * It aint pretty, but it gets the job done :) - */ - - private static final String rfc822phrase = - HeaderTokenizer.RFC822.replace(' ', '\0').replace('\t', '\0'); - - private static String quotePhrase(String phrase) { - int len = phrase.length(); - boolean needQuoting = false; - - for (int i = 0; i < len; i++) { - char c = phrase.charAt(i); - if (c == '"' || c == '\\') { - // need to escape them and then quote the whole string - StringBuffer sb = new StringBuffer(len + 3); - sb.append('"'); - for (int j = 0; j < len; j++) { - char cc = phrase.charAt(j); - if (cc == '"' || cc == '\\') - // Escape the character - sb.append('\\'); - sb.append(cc); - } - sb.append('"'); - return sb.toString(); - } else if ((c < 040 && c != '\r' && c != '\n' && c != '\t') || - c >= 0177 || rfc822phrase.indexOf(c) >= 0) - // These characters cause the string to be quoted - needQuoting = true; - } - - if (needQuoting) { - StringBuffer sb = new StringBuffer(len + 2); - sb.append('"').append(phrase).append('"'); - return sb.toString(); - } else - return phrase; - } - - private static String unquote(String s) { - if (s.startsWith("\"") && s.endsWith("\"") && s.length() > 1) { - s = s.substring(1, s.length() - 1); - // check for any escaped characters - if (s.indexOf('\\') >= 0) { - StringBuffer sb = new StringBuffer(s.length()); // approx - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\\' && i < s.length() - 1) - c = s.charAt(++i); - sb.append(c); - } - s = sb.toString(); - } - } - return s; - } - - /** - * The equality operator. - */ - public boolean equals(Object a) { - if (!(a instanceof InternetAddress)) - return false; - - String s = ((InternetAddress)a).getAddress(); - if (s == address) - return true; - if (address != null && address.equalsIgnoreCase(s)) - return true; - - return false; - } - - /** - * Compute a hash code for the address. - */ - public int hashCode() { - if (address == null) - return 0; - else - return address.toLowerCase(Locale.ENGLISH).hashCode(); - } - - /** - * Convert the given array of InternetAddress objects into - * a comma separated sequence of address strings. The - * resulting string contains only US-ASCII characters, and - * hence is mail-safe.

      - * - * @param addresses array of InternetAddress objects - * @exception ClassCastException, if any address object in the - * given array is not an InternetAddress object. Note - * that this is a RuntimeException. - * @return comma separated string of addresses - */ - public static String toString(Address[] addresses) { - return toString(addresses, 0); - } - - /** - * Convert the given array of InternetAddress objects into - * a comma separated sequence of address strings. The - * resulting string contains only US-ASCII characters, and - * hence is mail-safe.

      - * - * The 'used' parameter specifies the number of character positions - * already taken up in the field into which the resulting address - * sequence string is to be inserted. It is used to determine the - * line-break positions in the resulting address sequence string. - * - * @param addresses array of InternetAddress objects - * @param used number of character positions already used, in - * the field into which the address string is to - * be inserted. - * @exception ClassCastException, if any address object in the - * given array is not an InternetAddress object. Note - * that this is a RuntimeException. - * @return comma separated string of addresses - */ - public static String toString(Address[] addresses, int used) { - if (addresses == null || addresses.length == 0) - return null; - - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < addresses.length; i++) { - if (i != 0) { // need to append comma - sb.append(", "); - used += 2; - } - - String s = addresses[i].toString(); - int len = lengthOfFirstSegment(s); // length till CRLF - if (used + len > 76) { // overflows ... - sb.append("\r\n\t"); // .. start new continuation line - used = 8; // account for the starting char - } - sb.append(s); - used = lengthOfLastSegment(s, used); - } - - return sb.toString(); - } - - /* Return the length of the first segment within this string. - * If no segments exist, the length of the whole line is returned. - */ - private static int lengthOfFirstSegment(String s) { - int pos; - if ((pos = s.indexOf("\r\n")) != -1) - return pos; - else - return s.length(); - } - - /* - * Return the length of the last segment within this string. - * If no segments exist, the length of the whole line plus - * used is returned. - */ - private static int lengthOfLastSegment(String s, int used) { - int pos; - if ((pos = s.lastIndexOf("\r\n")) != -1) - return s.length() - pos - 2; - else - return s.length() + used; - } - - /** - * Return an InternetAddress object representing the current user. - * The entire email address may be specified in the "mail.from" - * property. If not set, the "mail.user" and "mail.host" properties - * are tried. If those are not set, the "user.name" property and - * InetAddress.getLocalHost method are tried. - * Security exceptions that may occur while accessing this information - * are ignored. If it is not possible to determine an email address, - * null is returned. - * - * @param session Session object used for property lookup - * @return current user's email address - */ - public static InternetAddress getLocalAddress(Session session) { - try { - return _getLocalAddress(session); - } catch (SecurityException sex) { // ignore it - } catch (AddressException ex) { // ignore it - } catch (UnknownHostException ex) { } // ignore it - return null; - } - - /** - * A package-private version of getLocalAddress that doesn't swallow - * the exception. Used by MimeMessage.setFrom() to report the reason - * for the failure. - */ - // package-private - static InternetAddress _getLocalAddress(Session session) - throws SecurityException, AddressException, UnknownHostException { - String user = null, host = null, address = null; - if (session == null) { - user = System.getProperty("user.name"); - host = getLocalHostName(); - } else { - address = session.getProperty("mail.from"); - if (address == null) { - user = session.getProperty("mail.user"); - if (user == null || user.length() == 0) - user = session.getProperty("user.name"); - if (user == null || user.length() == 0) - user = System.getProperty("user.name"); - host = session.getProperty("mail.host"); - if (host == null || host.length() == 0) - host = getLocalHostName(); - } - } - - if (address == null && user != null && user.length() != 0 && - host != null && host.length() != 0) - address = MimeUtility.quote(user.trim(), specialsNoDot + "\t ") + - "@" + host; - - if (address == null) - return null; - - return new InternetAddress(address); - } - - /** - * Get the local host name from InetAddress and return it in a form - * suitable for use in an email address. - */ - private static String getLocalHostName() throws UnknownHostException { - String host = null; - InetAddress me = InetAddress.getLocalHost(); - if (me != null) { - host = me.getHostName(); - if (host != null && host.length() > 0 && isInetAddressLiteral(host)) - host = '[' + host + ']'; - } - return host; - } - - /** - * Is the address an IPv4 or IPv6 address literal, which needs to - * be enclosed in "[]" in an email address? IPv4 literals contain - * decimal digits and dots, IPv6 literals contain hex digits, dots, - * and colons. We're lazy and don't check the exact syntax, just - * the allowed characters; strings that have only the allowed - * characters in a literal but don't meet the syntax requirements - * for a literal definitely can't be a host name and thus will fail - * later when used as an address literal. - */ - private static boolean isInetAddressLiteral(String addr) { - boolean sawHex = false, sawColon = false; - for (int i = 0; i < addr.length(); i++) { - char c = addr.charAt(i); - if (c >= '0' && c <= '9') - ; // digits always ok - else if (c == '.') - ; // dot always ok - else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - sawHex = true; // need to see a colon too - else if (c == ':') - sawColon = true; - else - return false; // anything else, definitely not a literal - } - return !sawHex || sawColon; - } - - /** - * Parse the given comma separated sequence of addresses into - * InternetAddress objects. Addresses must follow RFC822 syntax. - * - * @param addresslist comma separated address strings - * @return array of InternetAddress objects - * @exception AddressException if the parse failed - */ - public static InternetAddress[] parse(String addresslist) - throws AddressException { - return parse(addresslist, true); - } - - /** - * Parse the given sequence of addresses into InternetAddress - * objects. If strict is false, simple email addresses - * separated by spaces are also allowed. If strict is - * true, many (but not all) of the RFC822 syntax rules are enforced. - * In particular, even if strict is true, addresses - * composed of simple names (with no "@domain" part) are allowed. - * Such "illegal" addresses are not uncommon in real messages.

      - * - * Non-strict parsing is typically used when parsing a list of - * mail addresses entered by a human. Strict parsing is typically - * used when parsing address headers in mail messages. - * - * @param addresslist comma separated address strings - * @param strict enforce RFC822 syntax - * @return array of InternetAddress objects - * @exception AddressException if the parse failed - */ - public static InternetAddress[] parse(String addresslist, boolean strict) - throws AddressException { - return parse(addresslist, strict, false); - } - - /** - * Parse the given sequence of addresses into InternetAddress - * objects. If strict is false, the full syntax rules for - * individual addresses are not enforced. If strict is - * true, many (but not all) of the RFC822 syntax rules are enforced.

      - * - * To better support the range of "invalid" addresses seen in real - * messages, this method enforces fewer syntax rules than the - * parse method when the strict flag is false - * and enforces more rules when the strict flag is true. If the - * strict flag is false and the parse is successful in separating out an - * email address or addresses, the syntax of the addresses themselves - * is not checked. - * - * @param addresslist comma separated address strings - * @param strict enforce RFC822 syntax - * @return array of InternetAddress objects - * @exception AddressException if the parse failed - * @since JavaMail 1.3 - */ - public static InternetAddress[] parseHeader(String addresslist, - boolean strict) throws AddressException { - return parse(addresslist, strict, true); - } - - /* - * RFC822 Address parser. - * - * XXX - This is complex enough that it ought to be a real parser, - * not this ad-hoc mess, and because of that, this is not perfect. - * - * XXX - Deal with encoded Headers too. - */ - private static InternetAddress[] parse(String s, boolean strict, - boolean parseHdr) throws AddressException { - int start, end, index, nesting; - int start_personal = -1, end_personal = -1; - int length = s.length(); - boolean ignoreErrors = parseHdr && !strict; - boolean in_group = false; // we're processing a group term - boolean route_addr = false; // address came from route-addr term - boolean rfc822 = false; // looks like an RFC822 address - char c; - List v = new ArrayList(); - InternetAddress ma; - - for (start = end = -1, index = 0; index < length; index++) { - c = s.charAt(index); - - switch (c) { - case '(': // We are parsing a Comment. Ignore everything inside. - // XXX - comment fields should be parsed as whitespace, - // more than one allowed per address - rfc822 = true; - if (start >= 0 && end == -1) - end = index; - int pindex = index; - for (index++, nesting = 1; index < length && nesting > 0; - index++) { - c = s.charAt(index); - switch (c) { - case '\\': - index++; // skip both '\' and the escaped char - break; - case '(': - nesting++; - break; - case ')': - nesting--; - break; - default: - break; - } - } - if (nesting > 0) { - if (!ignoreErrors) - throw new AddressException("Missing ')'", s, index); - // pretend the first paren was a regular character and - // continue parsing after it - index = pindex + 1; - break; - } - index--; // point to closing paren - if (start_personal == -1) - start_personal = pindex + 1; - if (end_personal == -1) - end_personal = index; - break; - - case ')': - if (!ignoreErrors) - throw new AddressException("Missing '('", s, index); - // pretend the left paren was a regular character and - // continue parsing - if (start == -1) - start = index; - break; - - case '<': - rfc822 = true; - if (route_addr) { - if (!ignoreErrors) - throw new AddressException( - "Extra route-addr", s, index); - - // assume missing comma between addresses - if (start == -1) { - route_addr = false; - rfc822 = false; - start = end = -1; - break; // nope, nothing there - } - if (!in_group) { - // got a token, add this to our InternetAddress vector - if (end == -1) // should never happen - end = index; - String addr = s.substring(start, end).trim(); - - ma = new InternetAddress(); - ma.setAddress(addr); - if (start_personal >= 0) { - ma.encodedPersonal = unquote( - s.substring(start_personal, end_personal). - trim()); - } - v.add(ma); - - route_addr = false; - rfc822 = false; - start = end = -1; - start_personal = end_personal = -1; - // continue processing this new address... - } - } - - int rindex = index; - boolean inquote = false; - outf: - for (index++; index < length; index++) { - c = s.charAt(index); - switch (c) { - case '\\': // XXX - is this needed? - index++; // skip both '\' and the escaped char - break; - case '"': - inquote = !inquote; - break; - case '>': - if (inquote) - continue; - break outf; // out of for loop - default: - break; - } - } - - // did we find a matching quote? - if (inquote) { - if (!ignoreErrors) - throw new AddressException("Missing '\"'", s, index); - // didn't find matching quote, try again ignoring quotes - // (e.g., ``<"@foo.com>'') - outq: - for (index = rindex + 1; index < length; index++) { - c = s.charAt(index); - if (c == '\\') // XXX - is this needed? - index++; // skip both '\' and the escaped char - else if (c == '>') - break; - } - } - - // did we find a terminating '>'? - if (index >= length) { - if (!ignoreErrors) - throw new AddressException("Missing '>'", s, index); - // pretend the "<" was a regular character and - // continue parsing after it (e.g., ``<@foo.com'') - index = rindex + 1; - if (start == -1) - start = rindex; // back up to include "<" - break; - } - - if (!in_group) { - start_personal = start; - if (start_personal >= 0) - end_personal = rindex; - start = rindex + 1; - } - route_addr = true; - end = index; - break; - - case '>': - if (!ignoreErrors) - throw new AddressException("Missing '<'", s, index); - // pretend the ">" was a regular character and - // continue parsing (e.g., ``>@foo.com'') - if (start == -1) - start = index; - break; - - case '"': // parse quoted string - int qindex = index; - rfc822 = true; - if (start == -1) - start = index; - outq: - for (index++; index < length; index++) { - c = s.charAt(index); - switch (c) { - case '\\': - index++; // skip both '\' and the escaped char - break; - case '"': - break outq; // out of for loop - default: - break; - } - } - if (index >= length) { - if (!ignoreErrors) - throw new AddressException("Missing '\"'", s, index); - // pretend the quote was a regular character and - // continue parsing after it (e.g., ``"@foo.com'') - index = qindex + 1; - } - break; - - case '[': // a domain-literal, probably - rfc822 = true; - int lindex = index; - outb: - for (index++; index < length; index++) { - c = s.charAt(index); - switch (c) { - case '\\': - index++; // skip both '\' and the escaped char - break; - case ']': - break outb; // out of for loop - default: - break; - } - } - if (index >= length) { - if (!ignoreErrors) - throw new AddressException("Missing ']'", s, index); - // pretend the "[" was a regular character and - // continue parsing after it (e.g., ``[@foo.com'') - index = lindex + 1; - } - break; - - case ';': - if (start == -1) { - route_addr = false; - rfc822 = false; - start = end = -1; - break; // nope, nothing there - } - if (in_group) { - in_group = false; - /* - * If parsing headers, but not strictly, peek ahead. - * If next char is "@", treat the group name - * like the local part of the address, e.g., - * "Undisclosed-Recipient:;@java.sun.com". - */ - if (parseHdr && !strict && - index + 1 < length && s.charAt(index + 1) == '@') - break; - ma = new InternetAddress(); - end = index + 1; - ma.setAddress(s.substring(start, end).trim()); - v.add(ma); - - route_addr = false; - rfc822 = false; - start = end = -1; - start_personal = end_personal = -1; - break; - } - if (!ignoreErrors) - throw new AddressException( - "Illegal semicolon, not in group", s, index); - - // otherwise, parsing a header; treat semicolon like comma - // fall through to comma case... - - case ',': // end of an address, probably - if (start == -1) { - route_addr = false; - rfc822 = false; - start = end = -1; - break; // nope, nothing there - } - if (in_group) { - route_addr = false; - break; - } - // got a token, add this to our InternetAddress vector - if (end == -1) - end = index; - - String addr = s.substring(start, end).trim(); - String pers = null; - if (rfc822 && start_personal >= 0) { - pers = unquote( - s.substring(start_personal, end_personal).trim()); - if (pers.trim().length() == 0) - pers = null; - } - - /* - * If the personal name field has an "@" and the address - * field does not, assume they were reversed, e.g., - * ``"joe doe" (john.doe@example.com)''. - */ - if (parseHdr && !strict && pers != null && - pers.indexOf('@') >= 0 && - addr.indexOf('@') < 0 && addr.indexOf('!') < 0) { - String tmp = addr; - addr = pers; - pers = tmp; - } - if (rfc822 || strict || parseHdr) { - if (!ignoreErrors) - checkAddress(addr, route_addr, false); - ma = new InternetAddress(); - ma.setAddress(addr); - if (pers != null) - ma.encodedPersonal = pers; - v.add(ma); - } else { - // maybe we passed over more than one space-separated addr - StringTokenizer st = new StringTokenizer(addr); - while (st.hasMoreTokens()) { - String a = st.nextToken(); - checkAddress(a, false, false); - ma = new InternetAddress(); - ma.setAddress(a); - v.add(ma); - } - } - - route_addr = false; - rfc822 = false; - start = end = -1; - start_personal = end_personal = -1; - break; - - case ':': - rfc822 = true; - if (in_group) - if (!ignoreErrors) - throw new AddressException("Nested group", s, index); - if (start == -1) - start = index; - if (parseHdr && !strict) { - /* - * If next char is a special character that can't occur at - * the start of a valid address, treat the group name - * as the entire address, e.g., "Date:, Tue", "Re:@foo". - */ - if (index + 1 < length) { - String addressSpecials = ")>[]:@\\,."; - char nc = s.charAt(index + 1); - if (addressSpecials.indexOf(nc) >= 0) { - if (nc != '@') - break; // don't change in_group - /* - * Handle a common error: - * ``Undisclosed-Recipient:@example.com;'' - * - * Scan ahead. If we find a semicolon before - * one of these other special characters, - * consider it to be a group after all. - */ - for (int i = index + 2; i < length; i++) { - nc = s.charAt(i); - if (nc == ';') - break; - if (addressSpecials.indexOf(nc) >= 0) - break; - } - if (nc == ';') - break; // don't change in_group - } - } - - // ignore bogus "mailto:" prefix in front of an address, - // or bogus mail header name included in the address field - String gname = s.substring(start, index); - if (ignoreBogusGroupName && - (gname.equalsIgnoreCase("mailto") || - gname.equalsIgnoreCase("From") || - gname.equalsIgnoreCase("To") || - gname.equalsIgnoreCase("Cc") || - gname.equalsIgnoreCase("Subject") || - gname.equalsIgnoreCase("Re"))) - start = -1; // we're not really in a group - else - in_group = true; - } else - in_group = true; - break; - - // Ignore whitespace - case ' ': - case '\t': - case '\r': - case '\n': - break; - - default: - if (start == -1) - start = index; - break; - } - } - - if (start >= 0) { - /* - * The last token, add this to our InternetAddress vector. - * Note that this block of code should be identical to the - * block above for "case ','". - */ - if (end == -1) - end = length; - - String addr = s.substring(start, end).trim(); - String pers = null; - if (rfc822 && start_personal >= 0) { - pers = unquote( - s.substring(start_personal, end_personal).trim()); - if (pers.trim().length() == 0) - pers = null; - } - - /* - * If the personal name field has an "@" and the address - * field does not, assume they were reversed, e.g., - * ``"joe doe" (john.doe@example.com)''. - */ - if (parseHdr && !strict && - pers != null && pers.indexOf('@') >= 0 && - addr.indexOf('@') < 0 && addr.indexOf('!') < 0) { - String tmp = addr; - addr = pers; - pers = tmp; - } - if (rfc822 || strict || parseHdr) { - if (!ignoreErrors) - checkAddress(addr, route_addr, false); - ma = new InternetAddress(); - ma.setAddress(addr); - if (pers != null) - ma.encodedPersonal = pers; - v.add(ma); - } else { - // maybe we passed over more than one space-separated addr - StringTokenizer st = new StringTokenizer(addr); - while (st.hasMoreTokens()) { - String a = st.nextToken(); - checkAddress(a, false, false); - ma = new InternetAddress(); - ma.setAddress(a); - v.add(ma); - } - } - } - - InternetAddress[] a = new InternetAddress[v.size()]; - v.toArray(a); - return a; - } - - /** - * Validate that this address conforms to the syntax rules of - * RFC 822. The current implementation checks many, but not - * all, syntax rules. Note that even though the syntax of - * the address may be correct, there's no guarantee that a - * mailbox of that name exists. - * - * @exception AddressException if the address isn't valid. - * @since JavaMail 1.3 - */ - public void validate() throws AddressException { - if (isGroup()) - getGroup(true); // throw away the result - else - checkAddress(getAddress(), true, true); - } - - private static final String specialsNoDotNoAt = "()<>,;:\\\"[]"; - private static final String specialsNoDot = specialsNoDotNoAt + "@"; - - /** - * Check that the address is a valid "mailbox" per RFC822. - * (We also allow simple names.) - * - * XXX - much more to check - * XXX - doesn't handle domain-literals properly (but no one uses them) - */ - private static void checkAddress(String addr, - boolean routeAddr, boolean validate) - throws AddressException { - int i, start = 0; - - int len = addr.length(); - if (len == 0) - throw new AddressException("Empty address", addr); - - /* - * routeAddr indicates that the address is allowed - * to have an RFC 822 "route". - */ - if (routeAddr && addr.charAt(0) == '@') { - /* - * Check for a legal "route-addr": - * [@domain[,@domain ...]:]local@domain - */ - for (start = 0; (i = indexOfAny(addr, ",:", start)) >= 0; - start = i+1) { - if (addr.charAt(start) != '@') - throw new AddressException("Illegal route-addr", addr); - if (addr.charAt(i) == ':') { - // end of route-addr - start = i + 1; - break; - } - } - } - - /* - * The rest should be "local@domain", but we allow simply "local" - * unless called from validate. - * - * local-part must follow RFC 822 - no specials except '.' - * unless quoted. - */ - - char c = (char)-1; - char lastc = (char)-1; - boolean inquote = false; - for (i = start; i < len; i++) { - lastc = c; - c = addr.charAt(i); - // a quoted-pair is only supposed to occur inside a quoted string, - // but some people use it outside so we're more lenient - if (c == '\\' || lastc == '\\') - continue; - if (c == '"') { - if (inquote) { - // peek ahead, next char must be "@" - if (validate && i + 1 < len && addr.charAt(i + 1) != '@') - throw new AddressException( - "Quote not at end of local address", addr); - inquote = false; - } else { - if (validate && i != 0) - throw new AddressException( - "Quote not at start of local address", addr); - inquote = true; - } - continue; - } - if (inquote) - continue; - if (c == '@') { - if (i == 0) - throw new AddressException("Missing local name", addr); - break; // done with local part - } - if (c <= 040 || c >= 0177) - throw new AddressException( - "Local address contains control or whitespace", addr); - if (specialsNoDot.indexOf(c) >= 0) - throw new AddressException( - "Local address contains illegal character", addr); - } - if (inquote) - throw new AddressException("Unterminated quote", addr); - - /* - * Done with local part, now check domain. - * - * Note that the MimeMessage class doesn't remember addresses - * as separate objects; it writes them out as headers and then - * parses the headers when the addresses are requested. - * In order to support the case where a "simple" address is used, - * but the address also has a personal name and thus looks like - * it should be a valid RFC822 address when parsed, we only check - * this if we're explicitly called from the validate method. - */ - - if (c != '@') { - if (validate) - throw new AddressException("Missing final '@domain'", addr); - return; - } - - // check for illegal chars in the domain, but ignore domain literals - - start = i + 1; - if (start >= len) - throw new AddressException("Missing domain", addr); - - if (addr.charAt(start) == '.') - throw new AddressException("Domain starts with dot", addr); - for (i = start; i < len; i++) { - c = addr.charAt(i); - if (c == '[') - return; // domain literal, don't validate - if (c <= 040 || c >= 0177) - throw new AddressException( - "Domain contains control or whitespace", addr); - // RFC 2822 rule - //if (specialsNoDot.indexOf(c) >= 0) - /* - * RFC 1034 rule is more strict - * the full rule is: - * - * ::= | " " - * ::=

      - * - * This class is mostly intended for service providers. MimeMessage - * and MimeBody use this class for holding their headers.

      - * - *


      A note on RFC822 and MIME headers

      - * - * RFC822 and MIME header fields must contain only - * US-ASCII characters. If a header contains non US-ASCII characters, - * it must be encoded as per the rules in RFC 2047. The MimeUtility - * class provided in this package can be used to to achieve this. - * Callers of the setHeader, addHeader, and - * addHeaderLine methods are responsible for enforcing - * the MIME requirements for the specified headers. In addition, these - * header fields must be folded (wrapped) before being sent if they - * exceed the line length limitation for the transport (1000 bytes for - * SMTP). Received headers may have been folded. The application is - * responsible for folding and unfolding headers as appropriate.

      - * - * The current implementation supports the System property - * mail.mime.ignorewhitespacelines, which if set to true - * will cause a line containing only whitespace to be considered - * a blank line terminating the header. - * - * @see javax.mail.internet.MimeUtility - * @author John Mani - * @author Bill Shannon - */ - -public class InternetHeaders { - private static final boolean ignoreWhitespaceLines = - PropUtil.getBooleanSystemProperty("mail.mime.ignorewhitespacelines", - false); - - /** - * An individual internet header. This class is only used by - * subclasses of InternetHeaders.

      - * - * An InternetHeader object with a null value is used as a placeholder - * for headers of that name, to preserve the order of headers. - * A placeholder InternetHeader object with a name of ":" marks - * the location in the list of headers where new headers are - * added by default. - * - * @since JavaMail 1.4 - */ - protected static final class InternetHeader extends Header { - /* - * Note that the value field from the superclass - * isn't used in this class. We extract the value - * from the line field as needed. We store the line - * rather than just the value to ensure that we can - * get back the exact original line, with the original - * whitespace, etc. - */ - String line; // the entire RFC822 header "line", - // or null if placeholder - - /** - * Constructor that takes a line and splits out - * the header name. - */ - public InternetHeader(String l) { - super("", ""); // XXX - we'll change it later - int i = l.indexOf(':'); - if (i < 0) { - // should never happen - name = l.trim(); - } else { - name = l.substring(0, i).trim(); - } - line = l; - } - - /** - * Constructor that takes a header name and value. - */ - public InternetHeader(String n, String v) { - super(n, ""); - if (v != null) - line = n + ": " + v; - else - line = null; - } - - /** - * Return the "value" part of the header line. - */ - public String getValue() { - int i = line.indexOf(':'); - if (i < 0) - return line; - // skip whitespace after ':' - int j; - for (j = i + 1; j < line.length(); j++) { - char c = line.charAt(j); - if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) - break; - } - return line.substring(j); - } - } - - /* - * The enumeration object used to enumerate an - * InternetHeaders object. Can return - * either a String or a Header object. - */ - static class matchEnum implements Enumeration { - private Iterator e; // enum object of headers List - // XXX - is this overkill? should we step through in index - // order instead? - private String names[]; // names to match, or not - private boolean match; // return matching headers? - private boolean want_line; // return header lines? - private InternetHeader next_header; // the next header to be returned - - /* - * Constructor. Initialize the enumeration for the entire - * List of headers, the set of headers, whether to return - * matching or non-matching headers, and whether to return - * header lines or Header objects. - */ - matchEnum(List v, String n[], boolean m, boolean l) { - e = v.iterator(); - names = n; - match = m; - want_line = l; - next_header = null; - } - - /* - * Any more elements in this enumeration? - */ - public boolean hasMoreElements() { - // if necessary, prefetch the next matching header, - // and remember it. - if (next_header == null) - next_header = nextMatch(); - return next_header != null; - } - - /* - * Return the next element. - */ - public Object nextElement() { - if (next_header == null) - next_header = nextMatch(); - - if (next_header == null) - throw new NoSuchElementException("No more headers"); - - InternetHeader h = next_header; - next_header = null; - if (want_line) - return h.line; - else - return new Header(h.getName(), h.getValue()); - } - - /* - * Return the next Header object according to the match - * criteria, or null if none left. - */ - private InternetHeader nextMatch() { - next: - while (e.hasNext()) { - InternetHeader h = (InternetHeader)e.next(); - - // skip "place holder" headers - if (h.line == null) - continue; - - // if no names to match against, return appropriately - if (names == null) - return match ? null : h; - - // check whether this header matches any of the names - for (int i = 0; i < names.length; i++) { - if (names[i].equalsIgnoreCase(h.getName())) { - if (match) - return h; - else - // found a match, but we're - // looking for non-matches. - // try next header. - continue next; - } - } - // found no matches. if that's what we wanted, return it. - if (!match) - return h; - } - return null; - } - } - - - /** - * The actual list of Headers, including placeholder entries. - * Placeholder entries are Headers with a null value and - * are never seen by clients of the InternetHeaders class. - * Placeholder entries are used to keep track of the preferred - * order of headers. Headers are never actually removed from - * the list, they're converted into placeholder entries. - * New headers are added after existing headers of the same name - * (or before in the case of Received and - * Return-Path headers). If no existing header - * or placeholder for the header is found, new headers are - * added after the special placeholder with the name ":". - * - * @since JavaMail 1.4 - */ - protected List headers; - - /** - * Create an empty InternetHeaders object. Placeholder entries - * are inserted to indicate the preferred order of headers. - */ - public InternetHeaders() { - headers = new ArrayList(40); - headers.add(new InternetHeader("Return-Path", null)); - headers.add(new InternetHeader("Received", null)); - headers.add(new InternetHeader("Resent-Date", null)); - headers.add(new InternetHeader("Resent-From", null)); - headers.add(new InternetHeader("Resent-Sender", null)); - headers.add(new InternetHeader("Resent-To", null)); - headers.add(new InternetHeader("Resent-Cc", null)); - headers.add(new InternetHeader("Resent-Bcc", null)); - headers.add(new InternetHeader("Resent-Message-Id", null)); - headers.add(new InternetHeader("Date", null)); - headers.add(new InternetHeader("From", null)); - headers.add(new InternetHeader("Sender", null)); - headers.add(new InternetHeader("Reply-To", null)); - headers.add(new InternetHeader("To", null)); - headers.add(new InternetHeader("Cc", null)); - headers.add(new InternetHeader("Bcc", null)); - headers.add(new InternetHeader("Message-Id", null)); - headers.add(new InternetHeader("In-Reply-To", null)); - headers.add(new InternetHeader("References", null)); - headers.add(new InternetHeader("Subject", null)); - headers.add(new InternetHeader("Comments", null)); - headers.add(new InternetHeader("Keywords", null)); - headers.add(new InternetHeader("Errors-To", null)); - headers.add(new InternetHeader("MIME-Version", null)); - headers.add(new InternetHeader("Content-Type", null)); - headers.add(new InternetHeader("Content-Transfer-Encoding", null)); - headers.add(new InternetHeader("Content-MD5", null)); - headers.add(new InternetHeader(":", null)); - headers.add(new InternetHeader("Content-Length", null)); - headers.add(new InternetHeader("Status", null)); - } - - /** - * Read and parse the given RFC822 message stream till the - * blank line separating the header from the body. The input - * stream is left positioned at the start of the body. The - * header lines are stored internally.

      - * - * For efficiency, wrap a BufferedInputStream around the actual - * input stream and pass it as the parameter.

      - * - * No placeholder entries are inserted; the original order of - * the headers is preserved. - * - * @param is RFC822 input stream - */ - public InternetHeaders(InputStream is) throws MessagingException { - headers = new ArrayList(40); - load(is); - } - - /** - * Read and parse the given RFC822 message stream till the - * blank line separating the header from the body. Store the - * header lines inside this InternetHeaders object. The order - * of header lines is preserved.

      - * - * Note that the header lines are added into this InternetHeaders - * object, so any existing headers in this object will not be - * affected. Headers are added to the end of the existing list - * of headers, in order. - * - * @param is RFC822 input stream - */ - public void load(InputStream is) throws MessagingException { - // Read header lines until a blank line. It is valid - // to have BodyParts with no header lines. - String line; - LineInputStream lis = new LineInputStream(is); - String prevline = null; // the previous header line, as a string - // a buffer to accumulate the header in, when we know it's needed - StringBuffer lineBuffer = new StringBuffer(); - - try { - //while ((line = lis.readLine()) != null) { - do { - line = lis.readLine(); - if (line != null && - (line.startsWith(" ") || line.startsWith("\t"))) { - // continuation of header - if (prevline != null) { - lineBuffer.append(prevline); - prevline = null; - } - lineBuffer.append("\r\n"); - lineBuffer.append(line); - } else { - // new header - if (prevline != null) - addHeaderLine(prevline); - else if (lineBuffer.length() > 0) { - // store previous header first - addHeaderLine(lineBuffer.toString()); - lineBuffer.setLength(0); - } - prevline = line; - } - } while (line != null && !isEmpty(line)); - } catch (IOException ioex) { - throw new MessagingException("Error in input stream", ioex); - } - } - - /** - * Is this line an empty (blank) line? - */ - private static final boolean isEmpty(String line) { - return line.length() == 0 || - (ignoreWhitespaceLines && line.trim().length() == 0); - } - - /** - * Return all the values for the specified header. The - * values are String objects. Returns null - * if no headers with the specified name exist. - * - * @param name header name - * @return array of header values, or null if none - */ - public String[] getHeader(String name) { - Iterator e = headers.iterator(); - // XXX - should we just step through in index order? - List v = new ArrayList(); // accumulate return values - - while (e.hasNext()) { - InternetHeader h = (InternetHeader)e.next(); - if (name.equalsIgnoreCase(h.getName()) && h.line != null) { - v.add(h.getValue()); - } - } - if (v.size() == 0) - return (null); - // convert List to an array for return - String r[] = new String[v.size()]; - r = (String[])v.toArray(r); - return (r); - } - - /** - * Get all the headers for this header name, returned as a single - * String, with headers separated by the delimiter. If the - * delimiter is null, only the first header is - * returned. Returns null - * if no headers with the specified name exist. - * - * @param name header name - * @param delimiter delimiter - * @return the value fields for all headers with - * this name, or null if none - */ - public String getHeader(String name, String delimiter) { - String s[] = getHeader(name); - - if (s == null) - return null; - - if ((s.length == 1) || delimiter == null) - return s[0]; - - StringBuffer r = new StringBuffer(s[0]); - for (int i = 1; i < s.length; i++) { - r.append(delimiter); - r.append(s[i]); - } - return r.toString(); - } - - /** - * Change the first header line that matches name - * to have value, adding a new header if no existing header - * matches. Remove all matching headers but the first.

      - * - * Note that RFC822 headers can only contain US-ASCII characters - * - * @param name header name - * @param value header value - */ - public void setHeader(String name, String value) { - boolean found = false; - - for (int i = 0; i < headers.size(); i++) { - InternetHeader h = (InternetHeader)headers.get(i); - if (name.equalsIgnoreCase(h.getName())) { - if (!found) { - int j; - if (h.line != null && (j = h.line.indexOf(':')) >= 0) { - h.line = h.line.substring(0, j + 1) + " " + value; - // preserves capitalization, spacing - } else { - h.line = name + ": " + value; - } - found = true; - } else { - headers.remove(i); - i--; // have to look at i again - } - } - } - - if (!found) { - addHeader(name, value); - } - } - - /** - * Add a header with the specified name and value to the header list.

      - * - * The current implementation knows about the preferred order of most - * well-known headers and will insert headers in that order. In - * addition, it knows that Received headers should be - * inserted in reverse order (newest before oldest), and that they - * should appear at the beginning of the headers, preceeded only by - * a possible Return-Path header.

      - * - * Note that RFC822 headers can only contain US-ASCII characters. - * - * @param name header name - * @param value header value - */ - public void addHeader(String name, String value) { - int pos = headers.size(); - boolean addReverse = - name.equalsIgnoreCase("Received") || - name.equalsIgnoreCase("Return-Path"); - if (addReverse) - pos = 0; - for (int i = headers.size() - 1; i >= 0; i--) { - InternetHeader h = (InternetHeader)headers.get(i); - if (name.equalsIgnoreCase(h.getName())) { - if (addReverse) { - pos = i; - } else { - headers.add(i + 1, new InternetHeader(name, value)); - return; - } - } - // marker for default place to add new headers - if (!addReverse && h.getName().equals(":")) - pos = i; - } - headers.add(pos, new InternetHeader(name, value)); - } - - /** - * Remove all header entries that match the given name - * @param name header name - */ - public void removeHeader(String name) { - for (int i = 0; i < headers.size(); i++) { - InternetHeader h = (InternetHeader)headers.get(i); - if (name.equalsIgnoreCase(h.getName())) { - h.line = null; - //headers.remove(i); - //i--; // have to look at i again - } - } - } - - /** - * Return all the headers as an Enumeration of - * {@link javax.mail.Header} objects. - * - * @return Header objects - */ - public Enumeration getAllHeaders() { - return (new matchEnum(headers, null, false, false)); - } - - /** - * Return all matching {@link javax.mail.Header} objects. - * - * @return matching Header objects - */ - public Enumeration getMatchingHeaders(String[] names) { - return (new matchEnum(headers, names, true, false)); - } - - /** - * Return all non-matching {@link javax.mail.Header} objects. - * - * @return non-matching Header objects - */ - public Enumeration getNonMatchingHeaders(String[] names) { - return (new matchEnum(headers, names, false, false)); - } - - /** - * Add an RFC822 header line to the header store. - * If the line starts with a space or tab (a continuation line), - * add it to the last header line in the list. Otherwise, - * append the new header line to the list.

      - * - * Note that RFC822 headers can only contain US-ASCII characters - * - * @param line raw RFC822 header line - */ - public void addHeaderLine(String line) { - try { - char c = line.charAt(0); - if (c == ' ' || c == '\t') { - InternetHeader h = - (InternetHeader)headers.get(headers.size() - 1); - h.line += "\r\n" + line; - } else - headers.add(new InternetHeader(line)); - } catch (StringIndexOutOfBoundsException e) { - // line is empty, ignore it - return; - } catch (NoSuchElementException e) { - // XXX - vector is empty? - } - } - - /** - * Return all the header lines as an Enumeration of Strings. - */ - public Enumeration getAllHeaderLines() { - return (getNonMatchingHeaderLines(null)); - } - - /** - * Return all matching header lines as an Enumeration of Strings. - */ - public Enumeration getMatchingHeaderLines(String[] names) { - return (new matchEnum(headers, names, true, true)); - } - - /** - * Return all non-matching header lines - */ - public Enumeration getNonMatchingHeaderLines(String[] names) { - return (new matchEnum(headers, names, false, true)); - } -} diff --git a/src/main/java/javax/mail/internet/MailDateFormat.java b/src/main/java/javax/mail/internet/MailDateFormat.java deleted file mode 100644 index 53d5162a..00000000 --- a/src/main/java/javax/mail/internet/MailDateFormat.java +++ /dev/null @@ -1,913 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.util.Date; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; -import java.util.logging.Level; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.text.NumberFormat; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.text.ParseException; - -import com.sun.mail.util.MailLogger; - -/** - * Formats and parses date specification based on the - * draft-ietf-drums-msg-fmt-08 dated January 26, 2000. This is a followup - * spec to RFC822.

      - * - * This class does not take pattern strings. It always formats the - * date based on the specification below.

      - * - * 3.3 Date and Time Specification

      - * - * Date and time occur in several header fields of a message. This section - * specifies the syntax for a full date and time specification. Though folding - * whitespace is permitted throughout the date-time specification, it is - * recommended that only a single space be used where FWS is required and no - * space be used where FWS is optional in the date-time specification; some - * older implementations may not interpret other occurrences of folding - * whitespace correctly.

      - * - * date-time = [ day-of-week "," ] date FWS time [CFWS]

      - * - * day-of-week = ([FWS] day-name) / obs-day-of-week

      - * - * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"

      - * - * date = day month year

      - * - * year = 4*DIGIT / obs-year

      - * - * month = (FWS month-name FWS) / obs-month

      - * - *

      month-name = "Jan" / "Feb" / "Mar" / "Apr" /
      - *             "May" / "Jun" / "Jul" / "Aug" /
      - *             "Sep" / "Oct" / "Nov" / "Dec"
      - * 

      - * day = ([FWS] 1*2DIGIT) / obs-day

      - * - * time = time-of-day FWS zone

      - * - * time-of-day = hour ":" minute [ ":" second ]

      - * - * hour = 2DIGIT / obs-hour

      - * - * minute = 2DIGIT / obs-minute

      - * - * second = 2DIGIT / obs-second

      - * - * zone = (( "+" / "-" ) 4DIGIT) / obs-zone

      - * - * - * The day is the numeric day of the month. The year is any numeric year in - * the common era.

      - * - * The time-of-day specifies the number of hours, minutes, and optionally - * seconds since midnight of the date indicated.

      - * - * The date and time-of-day SHOULD express local time.

      - * - * The zone specifies the offset from Coordinated Universal Time (UTC, - * formerly referred to as "Greenwich Mean Time") that the date and - * time-of-day represent. The "+" or "-" indicates whether the time-of-day is - * ahead of or behind Universal Time. The first two digits indicate the number - * of hours difference from Universal Time, and the last two digits indicate - * the number of minutes difference from Universal Time. (Hence, +hhmm means - * +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm) minutes). The form - * "+0000" SHOULD be used to indicate a time zone at Universal Time. Though - * "-0000" also indicates Universal Time, it is used to indicate that the time - * was generated on a system that may be in a local time zone other than - * Universal Time.

      - * - * A date-time specification MUST be semantically valid. That is, the - * day-of-the week (if included) MUST be the day implied by the date, the - * numeric day-of-month MUST be between 1 and the number of days allowed for - * the specified month (in the specified year), the time-of-day MUST be in the - * range 00:00:00 through 23:59:60 (the number of seconds allowing for a leap - * second; see [STD-12]), and the zone MUST be within the range -9959 through - * +9959.

      - * - * @author Max Spivak - * @since JavaMail 1.2 - */ - -public class MailDateFormat extends SimpleDateFormat { - - private static final long serialVersionUID = -8148227605210628779L; - - public MailDateFormat() { - super("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)", Locale.US); - } - - /** - * Formats the given date in the format specified by - * draft-ietf-drums-msg-fmt-08 in the current TimeZone. - * - * @param date the Date object - * @param dateStrBuf the formatted string - * @param fieldPosition the current field position - * @return StringBuffer the formatted String - * @since JavaMail 1.2 - */ - public StringBuffer format(Date date, StringBuffer dateStrBuf, - FieldPosition fieldPosition) { - - /* How this method works: First format the date with the - * format specified in the constructor inserting string 'XXXXX' - * where the timezone offset goes. Find where in the string the - * string 'XXXXX' appears and remember that in var "pos". - * Calculate the offset, taking the DST into account and insert - * it into the stringbuffer at position pos. - */ - - int start = dateStrBuf.length(); - super.format(date, dateStrBuf, fieldPosition); - int pos = 0; - // find the beginning of the 'XXXXX' string in the formatted date - // 25 is the first position that we expect to find XXXXX at - for (pos = start + 25; dateStrBuf.charAt(pos) != 'X'; pos++) - ; - - // set the timezone to +HHMM or -HHMM - calendar.clear(); - calendar.setTime(date); - int offset = calendar.get(Calendar.ZONE_OFFSET) + - calendar.get(Calendar.DST_OFFSET); - // take care of the sign - if (offset < 0) { - dateStrBuf.setCharAt(pos++, '-'); - offset = (-offset); - } else - dateStrBuf.setCharAt(pos++, '+'); - - int rawOffsetInMins = offset / 60 / 1000; // offset from GMT in mins - int offsetInHrs = rawOffsetInMins / 60; - int offsetInMins = rawOffsetInMins % 60; - - dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs/10), 10)); - dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs%10), 10)); - dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins/10), 10)); - dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins%10), 10)); - // done with timezone - - return dateStrBuf; - } - - //////////////////////////////////////////////////////////// - - /** - * Parses the given date in the format specified by - * draft-ietf-drums-msg-fmt-08 in the current TimeZone. - * - * @param text the formatted date to be parsed - * @param pos the current parse position - * @return Date the parsed date in a Date object - * @since JavaMail 1.2 - */ - public Date parse(String text, ParsePosition pos) { - return parseDate(text.toCharArray(), pos, isLenient()); - } - - /* - Valid Examples: - - Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST) - Date: - Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST) - Date: 26 Aug 76 14:29 EDT - - */ - - /** - * method of what to look for: - * - * - * skip WS - * skip day "," (this is "Mon", "Tue") - * skip WS - * - * parse number (until WS) ==> 1*2DIGIT (day of month) - * - * skip WS - * - * parse alpha chars (until WS) ==> find month - * - * skip WS - * - * parse number (until WS) ==> 2*4DIGIT (year) - * - * skip WS - * - * // now looking for time - * parse number (until ':') ==> hours - * parse number (until ':') ==> minutes - * parse number (until WS) ==> seconds - * - * // now look for Time Zone - * skip WS - * if ('+' or '-') then numerical time zone offset - * if (alpha) then alpha time zone offset - */ - - static boolean debug = false; - private static MailLogger logger = new MailLogger( - MailDateFormat.class, - "DEBUG", - debug, - System.out); - - /** - * create a Date by parsing the char array - */ - static private Date parseDate(char[] orig, ParsePosition pos, - boolean lenient) { - try { - int day = -1; - int month = -1; - int year = -1; - int hours = 0; - int minutes = 0; - int seconds = 0; - int offset = 0; - - MailDateParser p = new MailDateParser(orig, pos.getIndex()); - - // get the day - p.skipUntilNumber(); - day = p.parseNumber(); - - if (!p.skipIfChar('-')) { // for IMAP internal Date - p.skipWhiteSpace(); - } - - // get the month - month = p.parseMonth(); - if (!p.skipIfChar('-')) { // for IMAP internal Date - p.skipWhiteSpace(); - } - - // get the year - year = p.parseNumber(); // should not return a negative number - if (year < 50) { - year += 2000; - } else if (year < 100) { - year += 1900; - } // otherwise the year is correct (and should be 4 digits) - - - // get the time - // first get hours - p.skipWhiteSpace(); - hours = p.parseNumber(); - - // get minutes - p.skipChar(':'); - minutes = p.parseNumber(); - - // get seconds (may be no seconds) - if (p.skipIfChar(':')) { - seconds = p.parseNumber(); - } - - - // try to get a Time Zone - try { - p.skipWhiteSpace(); - offset = p.parseTimeZone(); - } catch (ParseException pe) { - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, - "No timezone? : '" + new String(orig) + "'", pe); - } - } - - pos.setIndex(p.getIndex()); - Date d = ourUTC(year, month, day, hours, minutes, seconds, offset, - lenient); - return d; - - } catch (Exception e) { - // Catch *all* exceptions, including RuntimeExceptions like - // ArrayIndexOutofBoundsException ... we need to be - // extra tolerant of all those bogus dates that might screw - // up our parser. Sigh. - - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, - "Bad date: '" + new String(orig) + "'", e); - } - pos.setIndex(1); // to prevent DateFormat.parse() from throwing ex - return null; - } - } - - private static final Calendar cal = - new GregorianCalendar(TimeZone.getTimeZone("GMT")); - private synchronized static Date ourUTC(int year, int mon, int mday, - int hour, int min, int sec, - int tzoffset, boolean lenient) { - // clear the time and then set all the values - cal.clear(); - cal.setLenient(lenient); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.MONTH, mon); - cal.set(Calendar.DATE, mday); - cal.set(Calendar.HOUR_OF_DAY, hour); - cal.set(Calendar.MINUTE, min); - cal.add(Calendar.MINUTE, tzoffset); // adjust for the timezone - cal.set(Calendar.SECOND, sec); - - return cal.getTime(); - } - - - //////////////////////////////////////////////////////////// - - /** Don't allow setting the calendar */ - public void setCalendar(Calendar newCalendar) { - throw new RuntimeException("Method setCalendar() shouldn't be called"); - } - - /** Don't allow setting the NumberFormat */ - public void setNumberFormat(NumberFormat newNumberFormat) { - throw new RuntimeException("Method setNumberFormat() shouldn't be called"); - } - - /* test code for MailDateFormat */ - /* - public static void main(String[] args) { - DateFormat df = new MailDateFormat(); - Date d = new Date(); - - // test output in all the timezones - System.out.println("------- test all timezones ---------------"); - System.out.println("Current date: " + d); - String[] allIDs = TimeZone.getAvailableIDs(); - for (int i = 0; i < allIDs.length; i++) { - TimeZone tz = TimeZone.getTimeZone(allIDs[i]); - df.setTimeZone(tz); - System.out.println("Date in " + tz.getID() + ": " + - df.format(new Date())); - } - try { - System.out.println(df.parse("Sun, 21 Mar 1993 23:56:48 -0800 (PST)")); - System.out.println(df.parse("Mon, 22 Mar 1994 13:34:51 +0000")); - System.out.println(df.parse("26 Aug 76 14:29 EDT")); - System.out.println(df.parse("15 Apr 11 23:49 EST")); - System.out.println(df.parse("15 Apr 11 23:49 ABC")); - } catch (ParseException pex) { - pex.printStackTrace(); - } - - // reset DateFormat TZ - df.setTimeZone(TimeZone.getDefault()); - - // test all days in a month - System.out.println(); - System.out.println("------- test all days in a month ---------------"); - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.YEAR, 1972); - cal.set(Calendar.MONTH, Calendar.OCTOBER); - cal.set(Calendar.DATE, 1); - cal.set(Calendar.HOUR, 10); - cal.set(Calendar.MINUTE, 50); - cal.set(Calendar.AM_PM, Calendar.PM); - System.out.println("Initial Date: " + cal.getTime()); - System.out.println("Current Date: " + df.format(cal.getTime())); - for (int i = 0; i < 30; i++) { - cal.roll(Calendar.DATE, true); - System.out.println("Current Date: " + df.format(cal.getTime())); - } - - // test all months - System.out.println(); - System.out.println("------- test all months in a year -----------"); - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(Calendar.DATE, 7); - System.out.println("Initial Date: " + cal.getTime()); - System.out.println("Current Date: " + df.format(cal.getTime())); - for (int i = 1; i < 12; i++) { - cal.roll(Calendar.MONTH, true); - System.out.println("Current Date: " + df.format(cal.getTime())); - } - - // test leap years - System.out.println(); - System.out.println("------- test leap years -----------"); - cal.set(Calendar.YEAR, 1999); - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(Calendar.DATE, 31); - cal.roll(Calendar.MONTH, true); - System.out.println("Initial Date: " + cal.getTime()); - System.out.println("Current Date: " + df.format(cal.getTime())); - for (int i = 1; i < 12; i++) { - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(Calendar.DATE, 31); - cal.roll(Calendar.YEAR, true); - cal.roll(Calendar.MONTH, true); - System.out.println("Current Date: " + df.format(cal.getTime())); - } - } - */ - -} - -/** - * Helper class to deal with parsing the characters - */ -class MailDateParser { - - int index = 0; - char[] orig = null; - - public MailDateParser(char[] orig, int index) { - this.orig = orig; - this.index = index; - } - - /** - * skips chars until it finds a number (0-9) - * - * if it does not find a number, it will throw - * an ArrayIndexOutOfBoundsException - */ - public void skipUntilNumber() throws ParseException { - try { - while (true) { - switch ( orig[index] ) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return; - - default: - index++; - break; - } - } - } catch (ArrayIndexOutOfBoundsException e) { - throw new ParseException("No Number Found", index); - } - } - - /** - * skips any number of tabs, spaces, CR, and LF - folding whitespace - */ - public void skipWhiteSpace() { - int len = orig.length; - while (index < len) { - switch (orig[index]) { - case ' ': // space - case '\t': // tab - case '\r': // CR - case '\n': // LF - index++; - break; - - default: - return; - } - } - } - - - /** - * used to look at the next character without "parsing" that - * character. - */ - public int peekChar() throws ParseException { - if (index < orig.length) - return orig[index]; - else - throw new ParseException("No more characters", index); - } - - /** - * skips the given character. if the current char does not - * match a ParseException will be thrown - */ - public void skipChar(char c) throws ParseException { - if (index < orig.length) { - if (orig[index] == c) { - index++; - } else { - throw new ParseException("Wrong char", index); - } - } else { - throw new ParseException("No more characters", index); - } - } - - /** - * will only skip the current char if it matches the given - * char - */ - public boolean skipIfChar(char c) throws ParseException { - if (index < orig.length) { - if (orig[index] == c) { - index++; - return true; - } else { - return false; - } - } else { - throw new ParseException("No more characters", index); - } - } - - - /** - * current char must point to a number. the number will be - * parsed and the resulting number will be returned. if a - * number is not found, a ParseException will be thrown - */ - public int parseNumber() throws ParseException { - int length = orig.length; - boolean gotNum = false; - int result = 0; - - while (index < length) { - switch( orig[index] ) { - case '0': - result *= 10; - gotNum = true; - break; - - case '1': - result = result * 10 + 1; - gotNum = true; - break; - - case '2': - result = result * 10 + 2; - gotNum = true; - break; - - case '3': - result = result * 10 + 3; - gotNum = true; - break; - - case '4': - result = result * 10 + 4; - gotNum = true; - break; - - case '5': - result = result * 10 + 5; - gotNum = true; - break; - - case '6': - result = result * 10 + 6; - gotNum = true; - break; - - case '7': - result = result * 10 + 7; - gotNum = true; - break; - - case '8': - result = result * 10 + 8; - gotNum = true; - break; - - case '9': - result = result * 10 + 9; - gotNum = true; - break; - - default: - if (gotNum) - return result; - else - throw new ParseException("No Number found", index); - } - - index++; - } - - // check the result - if (gotNum) - return result; - - // else, throw a parse error - throw new ParseException("No Number found", index); - } - - - /** - * will look for one of "Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dev" - * and return the numerical version of the month. (0-11). a ParseException - * error is thrown if a month cannot be found. - */ - public int parseMonth() throws ParseException { - char curr; - - try { - switch(orig[index++]) { - case 'J': - case 'j': // "Jan" (0) / "Jun" (5) / "Jul" (6) - // check next char - switch(orig[index++]) { - case 'A': - case 'a': - curr = orig[index++]; - if (curr == 'N' || curr == 'n') { - return 0; - } - break; - - case 'U': - case 'u': - curr = orig[index++]; - if (curr == 'N' || curr == 'n') { - return 5; - } else if (curr == 'L' || curr == 'l') { - return 6; - } - break; - } - break; - - case 'F': - case 'f': // "Feb" - curr = orig[index++]; - if (curr == 'E' || curr == 'e') { - curr = orig[index++]; - if (curr == 'B' || curr == 'b') { - return 1; - } - } - break; - - case 'M': - case 'm': // "Mar" (2) / "May" (4) - curr = orig[index++]; - if (curr == 'A' || curr == 'a') { - curr = orig[index++]; - if (curr == 'R' || curr == 'r') { - return 2; - } else if (curr == 'Y' || curr == 'y') { - return 4; - } - } - break; - - case 'A': - case 'a': // "Apr" (3) / "Aug" (7) - curr = orig[index++]; - if (curr == 'P' || curr == 'p') { - curr = orig[index++]; - if (curr == 'R' || curr == 'r') { - return 3; - } - } else if (curr == 'U' || curr == 'u') { - curr = orig[index++]; - if (curr == 'G' || curr == 'g') { - return 7; - } - } - break; - - case 'S': - case 's': // "Sep" (8) - curr = orig[index++]; - if (curr == 'E' || curr == 'e') { - curr = orig[index++]; - if (curr == 'P' || curr == 'p') { - return 8; - } - } - break; - - case 'O': - case 'o': // "Oct" - curr = orig[index++]; - if (curr == 'C' || curr == 'c') { - curr = orig[index++]; - if (curr == 'T' || curr == 't') { - return 9; - } - } - break; - - case 'N': - case 'n': // "Nov" - curr = orig[index++]; - if (curr == 'O' || curr == 'o') { - curr = orig[index++]; - if (curr == 'V' || curr == 'v') { - return 10; - } - } - break; - - case 'D': - case 'd': // "Dec" - curr = orig[index++]; - if (curr == 'E' || curr == 'e') { - curr = orig[index++]; - if (curr == 'C' || curr == 'c') { - return 11; - } - } - break; - } - } catch (ArrayIndexOutOfBoundsException e) { - } - - throw new ParseException("Bad Month", index); - } - - /** - * will parse the timezone - either Numerical version (e.g. +0800, -0500) - * or the alpha version (e.g. PDT, PST). the result will be returned in - * minutes needed to be added to the date to bring it to GMT. - */ - public int parseTimeZone() throws ParseException { - if (index >= orig.length) - throw new ParseException("No more characters", index); - - char test = orig[index]; - if ( test == '+' || test == '-' ) { - return parseNumericTimeZone(); - } else { - return parseAlphaTimeZone(); - } - } - - - /** - * will parse the Numerical time zone version (e.g. +0800, -0500) - * the result will be returned in minutes needed to be added - * to the date to bring it to GMT. - */ - public int parseNumericTimeZone() throws ParseException { - // we switch the sign if it is a '+' - // since the time in the string we are - // parsing is off from GMT by that amount. - // and we want to get the time back into - // GMT, so we substract it. - boolean switchSign = false; - char first = orig[index++]; - if (first == '+') { - switchSign = true; - } else if (first != '-') { - throw new ParseException("Bad Numeric TimeZone", index); - } - - int oindex = index; - int tz = parseNumber(); - if (tz >= 2400) - throw new ParseException("Numeric TimeZone out of range", oindex); - int offset = (tz / 100) * 60 + (tz % 100); - if (switchSign) { - return -offset; - } else { - return offset; - } - } - - /** - * will parse the alpha time zone version (e.g. PDT, PST). - * the result will be returned in minutes needed to be added - * to the date to bring it to GMT. - */ - public int parseAlphaTimeZone() throws ParseException { - int result = 0; - boolean foundCommon = false; - char curr; - - try { - switch(orig[index++]) { - case 'U': - case 'u': // "UT" / Universal Time - curr = orig[index++]; - if (curr == 'T' || curr == 't') { - result = 0; - break; - } - throw new ParseException("Bad Alpha TimeZone", index); - - case 'G': - case 'g': // "GMT" ; Universal Time - curr = orig[index++]; - if (curr == 'M' || curr == 'm') { - curr = orig[index++]; - if (curr == 'T' || curr == 't') { - result = 0; - break; - } - } - throw new ParseException("Bad Alpha TimeZone", index); - - case 'E': - case 'e': // "EST" / "EDT" ; Eastern: - 5/ - 4 - result = 300; - foundCommon = true; - break; - - case 'C': - case 'c': // "CST" / "CDT" ; Central: - 6/ - 5 - result = 360; - foundCommon = true; - break; - - case 'M': - case 'm': // "MST" / "MDT" ; Mountain: - 7/ - 6 - result = 420; - foundCommon = true; - break; - - case 'P': - case 'p': // "PST" / "PDT" ; Pacific: - 8/ - 7 - result = 480; - foundCommon = true; - break; - - default: - throw new ParseException("Bad Alpha TimeZone", index); - } - } catch (ArrayIndexOutOfBoundsException e) { - throw new ParseException("Bad Alpha TimeZone", index); - } - - if (foundCommon) { - curr = orig[index++]; - if (curr == 'S' || curr == 's') { - curr = orig[index++]; - if (curr != 'T' && curr != 't') { - throw new ParseException("Bad Alpha TimeZone", index); - } - } else if (curr == 'D' || curr == 'd') { - curr = orig[index++]; - if (curr == 'T' || curr != 't') { - // for daylight time - result -= 60; - } else { - throw new ParseException("Bad Alpha TimeZone", index); - } - } - } - - return result; - } - - int getIndex() { - return index; - } -} diff --git a/src/main/java/javax/mail/internet/MimeBodyPart.java b/src/main/java/javax/mail/internet/MimeBodyPart.java deleted file mode 100644 index 0256da93..00000000 --- a/src/main/java/javax/mail/internet/MimeBodyPart.java +++ /dev/null @@ -1,1597 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import javax.activation.*; -import java.io.*; -import java.util.*; -import com.sun.mail.util.*; - -/** - * This class represents a MIME body part. It implements the - * BodyPart abstract class and the MimePart - * interface. MimeBodyParts are contained in MimeMultipart - * objects.

      - * - * MimeBodyPart uses the InternetHeaders class to parse - * and store the headers of that body part.

      - * - *


      A note on RFC 822 and MIME headers

      - * - * RFC 822 header fields must contain only - * US-ASCII characters. MIME allows non ASCII characters to be present - * in certain portions of certain headers, by encoding those characters. - * RFC 2047 specifies the rules for doing this. The MimeUtility - * class provided in this package can be used to to achieve this. - * Callers of the setHeader, addHeader, and - * addHeaderLine methods are responsible for enforcing - * the MIME requirements for the specified headers. In addition, these - * header fields must be folded (wrapped) before being sent if they - * exceed the line length limitation for the transport (1000 bytes for - * SMTP). Received headers may have been folded. The application is - * responsible for folding and unfolding headers as appropriate.

      - * - * @author John Mani - * @author Bill Shannon - * @author Kanwar Oberoi - * @see javax.mail.Part - * @see javax.mail.internet.MimePart - * @see javax.mail.internet.MimeUtility - */ - -public class MimeBodyPart extends BodyPart implements MimePart { - - // Paranoia: - // allow this last minute change to be disabled if it causes problems - private static final boolean setDefaultTextCharset = - PropUtil.getBooleanSystemProperty( - "mail.mime.setdefaulttextcharset", true); - - private static final boolean setContentTypeFileName = - PropUtil.getBooleanSystemProperty( - "mail.mime.setcontenttypefilename", true); - - private static final boolean encodeFileName = - PropUtil.getBooleanSystemProperty("mail.mime.encodefilename", false); - private static final boolean decodeFileName = - PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false); - private static final boolean ignoreMultipartEncoding = - PropUtil.getBooleanSystemProperty( - "mail.mime.ignoremultipartencoding", true); - - // Paranoia: - // allow this last minute change to be disabled if it causes problems - static final boolean cacheMultipart = // accessed by MimeMessage - PropUtil.getBooleanSystemProperty("mail.mime.cachemultipart", true); - - - /** - * The DataHandler object representing this Part's content. - */ - protected DataHandler dh; - - /** - * Byte array that holds the bytes of the content of this Part. - */ - protected byte[] content; - - /** - * If the data for this body part was supplied by an - * InputStream that implements the SharedInputStream interface, - * contentStream is another such stream representing - * the content of this body part. In this case, content - * will be null. - * - * @since JavaMail 1.2 - */ - protected InputStream contentStream; - - /** - * The InternetHeaders object that stores all the headers - * of this body part. - */ - protected InternetHeaders headers; - - /** - * If our content is a Multipart of Message object, we save it - * the first time it's created by parsing a stream so that changes - * to the contained objects will not be lost. - * - * If this field is not null, it's return by the {@link #getContent} - * method. The {@link #getContent} method sets this field if it - * would return a Multipart or MimeMessage object. This field is - * is cleared by the {@link #setDataHandler} method. - * - * @since JavaMail 1.5 - */ - protected Object cachedContent; - - /** - * An empty MimeBodyPart object is created. - * This body part maybe filled in by a client constructing a multipart - * message. - */ - public MimeBodyPart() { - super(); - headers = new InternetHeaders(); - } - - /** - * Constructs a MimeBodyPart by reading and parsing the data from - * the specified input stream. The parser consumes data till the end - * of the given input stream. The input stream must start at the - * beginning of a valid MIME body part and must terminate at the end - * of that body part.

      - * - * Note that the "boundary" string that delimits body parts must - * not be included in the input stream. The intention - * is that the MimeMultipart parser will extract each body part's bytes - * from a multipart stream and feed them into this constructor, without - * the delimiter strings. - * - * @param is the body part Input Stream - */ - public MimeBodyPart(InputStream is) throws MessagingException { - if (!(is instanceof ByteArrayInputStream) && - !(is instanceof BufferedInputStream) && - !(is instanceof SharedInputStream)) - is = new BufferedInputStream(is); - - headers = new InternetHeaders(is); - - if (is instanceof SharedInputStream) { - SharedInputStream sis = (SharedInputStream)is; - contentStream = sis.newStream(sis.getPosition(), -1); - } else { - try { - content = ASCIIUtility.getBytes(is); - } catch (IOException ioex) { - throw new MessagingException("Error reading input stream", ioex); - } - } - - } - - /** - * Constructs a MimeBodyPart using the given header and - * content bytes.

      - * - * Used by providers. - * - * @param headers The header of this part - * @param content bytes representing the body of this part. - */ - public MimeBodyPart(InternetHeaders headers, byte[] content) - throws MessagingException { - super(); - this.headers = headers; - this.content = content; - } - - /** - * Return the size of the content of this body part in bytes. - * Return -1 if the size cannot be determined.

      - * - * Note that this number may not be an exact measure of the - * content size and may or may not account for any transfer - * encoding of the content.

      - * - * This implementation returns the size of the content - * array (if not null), or, if contentStream is not - * null, and the available method returns a positive - * number, it returns that number as the size. Otherwise, it returns - * -1. - * - * @return size in bytes, or -1 if not known - */ - public int getSize() throws MessagingException { - if (content != null) - return content.length; - if (contentStream != null) { - try { - int size = contentStream.available(); - // only believe the size if it's greate than zero, since zero - // is the default returned by the InputStream class itself - if (size > 0) - return size; - } catch (IOException ex) { - // ignore it - } - } - return -1; - } - - /** - * Return the number of lines for the content of this Part. - * Return -1 if this number cannot be determined.

      - * - * Note that this number may not be an exact measure of the - * content length and may or may not account for any transfer - * encoding of the content.

      - * - * This implementation returns -1. - * - * @return number of lines, or -1 if not known - */ - public int getLineCount() throws MessagingException { - return -1; - } - - /** - * Returns the value of the RFC 822 "Content-Type" header field. - * This represents the content type of the content of this - * body part. This value must not be null. If this field is - * unavailable, "text/plain" should be returned.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - * - * @return Content-Type of this body part - */ - public String getContentType() throws MessagingException { - String s = getHeader("Content-Type", null); - s = MimeUtil.cleanContentType(this, s); - if (s == null) - s = "text/plain"; - return s; - } - - /** - * Is this Part of the specified MIME type? This method - * compares only the primaryType and - * subType. - * The parameters of the content types are ignored.

      - * - * For example, this method will return true when - * comparing a Part of content type "text/plain" - * with "text/plain; charset=foobar".

      - * - * If the subType of mimeType is the - * special character '*', then the subtype is ignored during the - * comparison. - */ - public boolean isMimeType(String mimeType) throws MessagingException { - return isMimeType(this, mimeType); - } - - /** - * Returns the value of the "Content-Disposition" header field. - * This represents the disposition of this part. The disposition - * describes how the part should be presented to the user.

      - * - * If the Content-Disposition field is unavailable, - * null is returned.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - * - * @see #headers - */ - public String getDisposition() throws MessagingException { - return getDisposition(this); - } - - /** - * Set the "Content-Disposition" header field of this body part. - * If the disposition is null, any existing "Content-Disposition" - * header field is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setDisposition(String disposition) throws MessagingException { - setDisposition(this, disposition); - } - - /** - * Returns the content transfer encoding from the - * "Content-Transfer-Encoding" header - * field. Returns null if the header is unavailable - * or its value is absent.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - * - * @see #headers - */ - public String getEncoding() throws MessagingException { - return getEncoding(this); - } - - /** - * Returns the value of the "Content-ID" header field. Returns - * null if the field is unavailable or its value is - * absent.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - */ - public String getContentID() throws MessagingException { - return getHeader("Content-Id", null); - } - - /** - * Set the "Content-ID" header field of this body part. - * If the cid parameter is null, any existing - * "Content-ID" is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @since JavaMail 1.3 - */ - public void setContentID(String cid) throws MessagingException { - if (cid == null) - removeHeader("Content-ID"); - else - setHeader("Content-ID", cid); - } - - /** - * Return the value of the "Content-MD5" header field. Returns - * null if this field is unavailable or its value - * is absent.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - */ - public String getContentMD5() throws MessagingException { - return getHeader("Content-MD5", null); - } - - /** - * Set the "Content-MD5" header field of this body part. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setContentMD5(String md5) throws MessagingException { - setHeader("Content-MD5", md5); - } - - /** - * Get the languages specified in the Content-Language header - * of this MimePart. The Content-Language header is defined by - * RFC 1766. Returns null if this header is not - * available or its value is absent.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - */ - public String[] getContentLanguage() throws MessagingException { - return getContentLanguage(this); - } - - /** - * Set the Content-Language header of this MimePart. The - * Content-Language header is defined by RFC 1766. - * - * @param languages array of language tags - */ - public void setContentLanguage(String[] languages) - throws MessagingException { - setContentLanguage(this, languages); - } - - /** - * Returns the "Content-Description" header field of this body part. - * This typically associates some descriptive information with - * this part. Returns null if this field is unavailable or its - * value is absent.

      - * - * If the Content-Description field is encoded as per RFC 2047, - * it is decoded and converted into Unicode. If the decoding or - * conversion fails, the raw data is returned as is.

      - * - * This implementation uses getHeader(name) - * to obtain the requisite header field. - * - * @return content description - */ - public String getDescription() throws MessagingException { - return getDescription(this); - } - - /** - * Set the "Content-Description" header field for this body part. - * If the description parameter is null, then any - * existing "Content-Description" fields are removed.

      - * - * If the description contains non US-ASCII characters, it will - * be encoded using the platform's default charset. If the - * description contains only US-ASCII characters, no encoding - * is done and it is used as is.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param description content description - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - * @exception MessagingException otherwise; an - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setDescription(String description) throws MessagingException { - setDescription(description, null); - } - - /** - * Set the "Content-Description" header field for this body part. - * If the description parameter is null, then any - * existing "Content-Description" fields are removed.

      - * - * If the description contains non US-ASCII characters, it will - * be encoded using the specified charset. If the description - * contains only US-ASCII characters, no encoding is done and - * it is used as is.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param description Description - * @param charset Charset for encoding - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - * @exception MessagingException otherwise; an - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setDescription(String description, String charset) - throws MessagingException { - setDescription(this, description, charset); - } - - /** - * Get the filename associated with this body part.

      - * - * Returns the value of the "filename" parameter from the - * "Content-Disposition" header field of this body part. If its - * not available, returns the value of the "name" parameter from - * the "Content-Type" header field of this body part. - * Returns null if both are absent.

      - * - * If the mail.mime.decodefilename System property - * is set to true, the {@link MimeUtility#decodeText - * MimeUtility.decodeText} method will be used to decode the - * filename. While such encoding is not supported by the MIME - * spec, many mailers use this technique to support non-ASCII - * characters in filenames. The default value of this property - * is false. - * - * @return filename - */ - public String getFileName() throws MessagingException { - return getFileName(this); - } - - /** - * Set the filename associated with this body part, if possible.

      - * - * Sets the "filename" parameter of the "Content-Disposition" - * header field of this body part. For compatibility with older - * mailers, the "name" parameter of the "Content-Type" header is - * also set.

      - * - * If the mail.mime.encodefilename System property - * is set to true, the {@link MimeUtility#encodeText - * MimeUtility.encodeText} method will be used to encode the - * filename. While such encoding is not supported by the MIME - * spec, many mailers use this technique to support non-ASCII - * characters in filenames. The default value of this property - * is false. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setFileName(String filename) throws MessagingException { - setFileName(this, filename); - } - - /** - * Return a decoded input stream for this body part's "content".

      - * - * This implementation obtains the input stream from the DataHandler. - * That is, it invokes getDataHandler().getInputStream(); - * - * @return an InputStream - * @exception MessagingException - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - * - * @see #getContentStream - * @see javax.activation.DataHandler#getInputStream - */ - public InputStream getInputStream() - throws IOException, MessagingException { - return getDataHandler().getInputStream(); - } - - /** - * Produce the raw bytes of the content. This method is used - * when creating a DataHandler object for the content. Subclasses - * that can provide a separate input stream for just the Part - * content might want to override this method.

      - * - * @see #content - * @see MimeMessage#getContentStream - */ - protected InputStream getContentStream() throws MessagingException { - if (contentStream != null) - return ((SharedInputStream)contentStream).newStream(0, -1); - if (content != null) - return new ByteArrayInputStream(content); - - throw new MessagingException("No MimeBodyPart content"); - } - - /** - * Return an InputStream to the raw data with any Content-Transfer-Encoding - * intact. This method is useful if the "Content-Transfer-Encoding" - * header is incorrect or corrupt, which would prevent the - * getInputStream method or getContent method - * from returning the correct data. In such a case the application may - * use this method and attempt to decode the raw data itself.

      - * - * This implementation simply calls the getContentStream - * method. - * - * @see #getInputStream - * @see #getContentStream - * @since JavaMail 1.2 - */ - public InputStream getRawInputStream() throws MessagingException { - return getContentStream(); - } - - /** - * Return a DataHandler for this body part's content.

      - * - * The implementation provided here works just like the - * the implementation in MimeMessage. - * @see MimeMessage#getDataHandler - */ - public DataHandler getDataHandler() throws MessagingException { - if (dh == null) - dh = new MimePartDataHandler(new MimePartDataSource(this)); - return dh; - } - - /** - * Return the content as a Java object. The type of the object - * returned is of course dependent on the content itself. For - * example, the native format of a text/plain content is usually - * a String object. The native format for a "multipart" - * content is always a Multipart subclass. For content types that are - * unknown to the DataHandler system, an input stream is returned - * as the content.

      - * - * This implementation obtains the content from the DataHandler. - * That is, it invokes getDataHandler().getContent(); - * If the content is a Multipart or Message object and was created by - * parsing a stream, the object is cached and returned in subsequent - * calls so that modifications to the content will not be lost. - * - * @return Object - * @exception MessagingException - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - */ - public Object getContent() throws IOException, MessagingException { - if (cachedContent != null) - return cachedContent; - Object c; - try { - c = getDataHandler().getContent(); - } catch (FolderClosedIOException fex) { - throw new FolderClosedException(fex.getFolder(), fex.getMessage()); - } catch (MessageRemovedIOException mex) { - throw new MessageRemovedException(mex.getMessage()); - } - if (cacheMultipart && - (c instanceof Multipart || c instanceof Message) && - (content != null || contentStream != null)) { - cachedContent = c; - /* - * We may abandon the input stream so make sure - * the MimeMultipart has consumed the stream. - */ - if (c instanceof MimeMultipart) - ((MimeMultipart)c).parse(); - } - return c; - } - - /** - * This method provides the mechanism to set this body part's content. - * The given DataHandler object should wrap the actual content. - * - * @param dh The DataHandler for the content - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setDataHandler(DataHandler dh) - throws MessagingException { - this.dh = dh; - cachedContent = null; - MimeBodyPart.invalidateContentHeaders(this); - } - - /** - * A convenience method for setting this body part's content.

      - * - * The content is wrapped in a DataHandler object. Note that a - * DataContentHandler class for the specified type should be - * available to the JavaMail implementation for this to work right. - * That is, to do setContent(foobar, "application/x-foobar"), - * a DataContentHandler for "application/x-foobar" should be installed. - * Refer to the Java Activation Framework for more information. - * - * @param o the content object - * @param type Mime type of the object - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setContent(Object o, String type) - throws MessagingException { - if (o instanceof Multipart) { - setContent((Multipart)o); - } else { - setDataHandler(new DataHandler(o, type)); - } - } - - /** - * Convenience method that sets the given String as this - * part's content, with a MIME type of "text/plain". If the - * string contains non US-ASCII characters, it will be encoded - * using the platform's default charset. The charset is also - * used to set the "charset" parameter.

      - * - * Note that there may be a performance penalty if - * text is large, since this method may have - * to scan all the characters to determine what charset to - * use.

      - * - * If the charset is already known, use the - * setText method that takes the charset parameter. - * - * @param text the text content to set - * @exception MessagingException if an error occurs - * @see #setText(String text, String charset) - */ - public void setText(String text) throws MessagingException { - setText(text, null); - } - - /** - * Convenience method that sets the given String as this part's - * content, with a MIME type of "text/plain" and the specified - * charset. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * the "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @exception MessagingException if an error occurs - */ - public void setText(String text, String charset) - throws MessagingException { - setText(this, text, charset, "plain"); - } - - /** - * Convenience method that sets the given String as this part's - * content, with a primary MIME type of "text" and the specified - * MIME subtype. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * the "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @param subtype the MIME subtype to use (e.g., "html") - * @exception MessagingException if an error occurs - * @since JavaMail 1.4 - */ - public void setText(String text, String charset, String subtype) - throws MessagingException { - setText(this, text, charset, subtype); - } - - /** - * This method sets the body part's content to a Multipart object. - * - * @param mp The multipart object that is the Message's content - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values. - * @exception IllegalStateException if this body part is - * obtained from a READ_ONLY folder. - */ - public void setContent(Multipart mp) throws MessagingException { - setDataHandler(new DataHandler(mp, mp.getContentType())); - mp.setParent(this); - } - - /** - * Use the specified file to provide the data for this part. - * The simple file name is used as the file name for this - * part and the data in the file is used as the data for this - * part. The encoding will be chosen appropriately for the - * file data. The disposition of this part is set to - * {@link Part#ATTACHMENT Part.ATTACHMENT}. - * - * @param file the File object to attach - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.4 - */ - public void attachFile(File file) throws IOException, MessagingException { - FileDataSource fds = new FileDataSource(file); - this.setDataHandler(new DataHandler(fds)); - this.setFileName(fds.getName()); - this.setDisposition(ATTACHMENT); - } - - /** - * Use the specified file to provide the data for this part. - * The simple file name is used as the file name for this - * part and the data in the file is used as the data for this - * part. The encoding will be chosen appropriately for the - * file data. - * - * @param file the name of the file to attach - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.4 - */ - public void attachFile(String file) throws IOException, MessagingException { - File f = new File(file); - attachFile(f); - } - - /** - * Use the specified file with the specified Content-Type and - * Content-Transfer-Encoding to provide the data for this part. - * If contentType or encoding are null, appropriate values will - * be chosen. - * The simple file name is used as the file name for this - * part and the data in the file is used as the data for this - * part. The disposition of this part is set to - * {@link Part#ATTACHMENT Part.ATTACHMENT}. - * - * @param file the File object to attach - * @param contentType the Content-Type, or null - * @param encoding the Content-Transfer-Encoding, or null - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.5 - */ - public void attachFile(File file, String contentType, String encoding) - throws IOException, MessagingException { - DataSource fds = new EncodedFileDataSource(file, contentType, encoding); - this.setDataHandler(new DataHandler(fds)); - this.setFileName(fds.getName()); - this.setDisposition(ATTACHMENT); - } - - /** - * Use the specified file with the specified Content-Type and - * Content-Transfer-Encoding to provide the data for this part. - * If contentType or encoding are null, appropriate values will - * be chosen. - * The simple file name is used as the file name for this - * part and the data in the file is used as the data for this - * part. The disposition of this part is set to - * {@link Part#ATTACHMENT Part.ATTACHMENT}. - * - * @param file the name of the file - * @param contentType the Content-Type, or null - * @param encoding the Content-Transfer-Encoding, or null - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.5 - */ - public void attachFile(String file, String contentType, String encoding) - throws IOException, MessagingException { - attachFile(new File(file), contentType, encoding); - } - - /** - * A FileDataSource class that allows us to specify the - * Content-Type and Content-Transfer-Encoding. - */ - private static class EncodedFileDataSource extends FileDataSource - implements EncodingAware { - private String contentType; - private String encoding; - - public EncodedFileDataSource(File file, String contentType, - String encoding) { - super(file); - this.contentType = contentType; - this.encoding = encoding; - } - - // overrides DataSource.getContentType() - public String getContentType() { - return contentType != null ? contentType : super.getContentType(); - } - - // implements EncodingAware.getEncoding() - public String getEncoding() { - return encoding; - } - } - - /** - * Save the contents of this part in the specified file. The content - * is decoded and saved, without any of the MIME headers. - * - * @param file the File object to write to - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.4 - */ - public void saveFile(File file) throws IOException, MessagingException { - OutputStream out = null; - InputStream in = null; - try { - out = new BufferedOutputStream(new FileOutputStream(file)); - in = this.getInputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = in.read(buf)) > 0) - out.write(buf, 0, len); - } finally { - // close streams, but don't mask original exception, if any - try { - if (in != null) - in.close(); - } catch (IOException ex) { } - try { - if (out != null) - out.close(); - } catch (IOException ex) { } - } - } - - /** - * Save the contents of this part in the specified file. The content - * is decoded and saved, without any of the MIME headers. - * - * @param file the name of the file to write to - * @exception IOException errors related to accessing the file - * @exception MessagingException message related errors - * @since JavaMail 1.4 - */ - public void saveFile(String file) throws IOException, MessagingException { - File f = new File(file); - saveFile(f); - } - - /** - * Output the body part as an RFC 822 format stream. - * - * @exception MessagingException - * @exception IOException if an error occurs writing to the - * stream or if an error is generated - * by the javax.activation layer. - * @see javax.activation.DataHandler#writeTo - */ - public void writeTo(OutputStream os) - throws IOException, MessagingException { - writeTo(this, os, null); - } - - /** - * Get all the headers for this header_name. Note that certain - * headers may be encoded as per RFC 2047 if they contain - * non US-ASCII characters and these should be decoded. - * - * @param name name of header - * @return array of headers - * @see javax.mail.internet.MimeUtility - */ - public String[] getHeader(String name) throws MessagingException { - return headers.getHeader(name); - } - - /** - * Get all the headers for this header name, returned as a single - * String, with headers separated by the delimiter. If the - * delimiter is null, only the first header is - * returned. - * - * @param name the name of this header - * @param delimiter delimiter between fields in returned string - * @return the value fields for all headers with - * this name - * @exception MessagingException - */ - public String getHeader(String name, String delimiter) - throws MessagingException { - return headers.getHeader(name, delimiter); - } - - /** - * Set the value for this header_name. Replaces all existing - * header values with this new value. Note that RFC 822 headers - * must contain only US-ASCII characters, so a header that - * contains non US-ASCII characters must be encoded as per the - * rules of RFC 2047. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - */ - public void setHeader(String name, String value) - throws MessagingException { - headers.setHeader(name, value); - } - - /** - * Add this value to the existing values for this header_name. - * Note that RFC 822 headers must contain only US-ASCII - * characters, so a header that contains non US-ASCII characters - * must be encoded as per the rules of RFC 2047. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - */ - public void addHeader(String name, String value) - throws MessagingException { - headers.addHeader(name, value); - } - - /** - * Remove all headers with this name. - */ - public void removeHeader(String name) throws MessagingException { - headers.removeHeader(name); - } - - /** - * Return all the headers from this Message as an Enumeration of - * Header objects. - */ - public Enumeration getAllHeaders() throws MessagingException { - return headers.getAllHeaders(); - } - - /** - * Return matching headers from this Message as an Enumeration of - * Header objects.

      - */ - public Enumeration getMatchingHeaders(String[] names) - throws MessagingException { - return headers.getMatchingHeaders(names); - } - - /** - * Return non-matching headers from this Message as an - * Enumeration of Header objects. - */ - public Enumeration getNonMatchingHeaders(String[] names) - throws MessagingException { - return headers.getNonMatchingHeaders(names); - } - - /** - * Add a header line to this body part - */ - public void addHeaderLine(String line) throws MessagingException { - headers.addHeaderLine(line); - } - - /** - * Get all header lines as an Enumeration of Strings. A Header - * line is a raw RFC 822 header line, containing both the "name" - * and "value" field. - */ - public Enumeration getAllHeaderLines() throws MessagingException { - return headers.getAllHeaderLines(); - } - - /** - * Get matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC 822 header line, containing both - * the "name" and "value" field. - */ - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException { - return headers.getMatchingHeaderLines(names); - } - - /** - * Get non-matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC 822 header line, containing both - * the "name" and "value" field. - */ - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException { - return headers.getNonMatchingHeaderLines(names); - } - - /** - * Examine the content of this body part and update the appropriate - * MIME headers. Typical headers that get set here are - * Content-Type and Content-Transfer-Encoding. - * Headers might need to be updated in two cases: - * - *
      - * - A message being crafted by a mail application will certainly - * need to activate this method at some point to fill up its internal - * headers. - * - *
      - * - A message read in from a Store will have obtained - * all its headers from the store, and so doesn't need this. - * However, if this message is editable and if any edits have - * been made to either the content or message structure, we might - * need to resync our headers. - * - *
      - * In both cases this method is typically called by the - * Message.saveChanges method.

      - * - * If the {@link #cachedContent} field is not null (that is, - * it references a Multipart or Message object), then - * that object is used to set a new DataHandler, any - * stream data used to create this object is discarded, - * and the {@link #cachedContent} field is cleared. - */ - protected void updateHeaders() throws MessagingException { - updateHeaders(this); - /* - * If we've cached a Multipart or Message object then - * we're now committed to using this instance of the - * object and we discard any stream data used to create - * this object. - */ - if (cachedContent != null) { - dh = new DataHandler(cachedContent, getContentType()); - cachedContent = null; - content = null; - if (contentStream != null) { - try { - contentStream.close(); - } catch (IOException ioex) { } // nothing to do - } - contentStream = null; - } - } - - ///////////////////////////////////////////////////////////// - // Package private convenience methods to share code among // - // MimeMessage and MimeBodyPart // - ///////////////////////////////////////////////////////////// - - static boolean isMimeType(MimePart part, String mimeType) - throws MessagingException { - // XXX - lots of room for optimization here! - try { - ContentType ct = new ContentType(part.getContentType()); - return ct.match(mimeType); - } catch (ParseException ex) { - return part.getContentType().equalsIgnoreCase(mimeType); - } - } - - static void setText(MimePart part, String text, String charset, - String subtype) throws MessagingException { - if (charset == null) { - if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII) - charset = MimeUtility.getDefaultMIMECharset(); - else - charset = "us-ascii"; - } - // XXX - should at least ensure that subtype is an atom - part.setContent(text, "text/" + subtype + "; charset=" + - MimeUtility.quote(charset, HeaderTokenizer.MIME)); - } - - static String getDisposition(MimePart part) throws MessagingException { - String s = part.getHeader("Content-Disposition", null); - - if (s == null) - return null; - - ContentDisposition cd = new ContentDisposition(s); - return cd.getDisposition(); - } - - static void setDisposition(MimePart part, String disposition) - throws MessagingException { - if (disposition == null) - part.removeHeader("Content-Disposition"); - else { - String s = part.getHeader("Content-Disposition", null); - if (s != null) { - /* A Content-Disposition header already exists .. - * - * Override disposition, but attempt to retain - * existing disposition parameters - */ - ContentDisposition cd = new ContentDisposition(s); - cd.setDisposition(disposition); - disposition = cd.toString(); - } - part.setHeader("Content-Disposition", disposition); - } - } - - static String getDescription(MimePart part) - throws MessagingException { - String rawvalue = part.getHeader("Content-Description", null); - - if (rawvalue == null) - return null; - - try { - return MimeUtility.decodeText(MimeUtility.unfold(rawvalue)); - } catch (UnsupportedEncodingException ex) { - return rawvalue; - } - } - - static void - setDescription(MimePart part, String description, String charset) - throws MessagingException { - if (description == null) { - part.removeHeader("Content-Description"); - return; - } - - try { - part.setHeader("Content-Description", MimeUtility.fold(21, - MimeUtility.encodeText(description, charset, null))); - } catch (UnsupportedEncodingException uex) { - throw new MessagingException("Encoding error", uex); - } - } - - static String getFileName(MimePart part) throws MessagingException { - String filename = null; - String s = part.getHeader("Content-Disposition", null); - - if (s != null) { - // Parse the header .. - ContentDisposition cd = new ContentDisposition(s); - filename = cd.getParameter("filename"); - } - if (filename == null) { - // Still no filename ? Try the "name" ContentType parameter - s = part.getHeader("Content-Type", null); - s = MimeUtil.cleanContentType(part, s); - if (s != null) { - try { - ContentType ct = new ContentType(s); - filename = ct.getParameter("name"); - } catch (ParseException pex) { } // ignore it - } - } - if (decodeFileName && filename != null) { - try { - filename = MimeUtility.decodeText(filename); - } catch (UnsupportedEncodingException ex) { - throw new MessagingException("Can't decode filename", ex); - } - } - return filename; - } - - static void setFileName(MimePart part, String name) - throws MessagingException { - if (encodeFileName && name != null) { - try { - name = MimeUtility.encodeText(name); - } catch (UnsupportedEncodingException ex) { - throw new MessagingException("Can't encode filename", ex); - } - } - - // Set the Content-Disposition "filename" parameter - String s = part.getHeader("Content-Disposition", null); - ContentDisposition cd = - new ContentDisposition(s == null ? Part.ATTACHMENT : s); - cd.setParameter("filename", name); - part.setHeader("Content-Disposition", cd.toString()); - - /* - * Also attempt to set the Content-Type "name" parameter, - * to satisfy ancient MUAs. XXX - This is not RFC compliant. - */ - if (setContentTypeFileName) { - s = part.getHeader("Content-Type", null); - s = MimeUtil.cleanContentType(part, s); - if (s != null) { - try { - ContentType cType = new ContentType(s); - cType.setParameter("name", name); - part.setHeader("Content-Type", cType.toString()); - } catch (ParseException pex) { } // ignore it - } - } - } - - static String[] getContentLanguage(MimePart part) - throws MessagingException { - String s = part.getHeader("Content-Language", null); - - if (s == null) - return null; - - // Tokenize the header to obtain the Language-tags (skip comments) - HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); - Vector v = new Vector(); - - HeaderTokenizer.Token tk; - int tkType; - - while (true) { - tk = h.next(); // get a language-tag - tkType = tk.getType(); - if (tkType == HeaderTokenizer.Token.EOF) - break; // done - else if (tkType == HeaderTokenizer.Token.ATOM) - v.addElement(tk.getValue()); - else // invalid token, skip it. - continue; - } - - if (v.size() == 0) - return null; - - String[] language = new String[v.size()]; - v.copyInto(language); - return language; - } - - static void setContentLanguage(MimePart part, String[] languages) - throws MessagingException { - StringBuffer sb = new StringBuffer(languages[0]); - int len = "Content-Language".length() + 2 + languages[0].length(); - for (int i = 1; i < languages.length; i++) { - sb.append(','); - len++; - if (len > 76) { - sb.append("\r\n\t"); - len = 8; - } - sb.append(languages[i]); - len += languages[i].length(); - } - part.setHeader("Content-Language", sb.toString()); - } - - static String getEncoding(MimePart part) throws MessagingException { - String s = part.getHeader("Content-Transfer-Encoding", null); - - if (s == null) - return null; - - s = s.trim(); // get rid of trailing spaces - // quick check for known values to avoid unnecessary use - // of tokenizer. - if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") || - s.equalsIgnoreCase("quoted-printable") || - s.equalsIgnoreCase("binary") || - s.equalsIgnoreCase("base64")) - return s; - - // Tokenize the header to obtain the encoding (skip comments) - HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); - - HeaderTokenizer.Token tk; - int tkType; - - for (;;) { - tk = h.next(); // get a token - tkType = tk.getType(); - if (tkType == HeaderTokenizer.Token.EOF) - break; // done - else if (tkType == HeaderTokenizer.Token.ATOM) - return tk.getValue(); - else // invalid token, skip it. - continue; - } - return s; - } - - static void setEncoding(MimePart part, String encoding) - throws MessagingException { - part.setHeader("Content-Transfer-Encoding", encoding); - } - - /** - * Restrict the encoding to values allowed for the - * Content-Type of the specified MimePart. Returns - * either the original encoding or null. - */ - static String restrictEncoding(MimePart part, String encoding) - throws MessagingException { - if (!ignoreMultipartEncoding || encoding == null) - return encoding; - - if (encoding.equalsIgnoreCase("7bit") || - encoding.equalsIgnoreCase("8bit") || - encoding.equalsIgnoreCase("binary")) - return encoding; // these encodings are always valid - - String type = part.getContentType(); - if (type == null) - return encoding; - - try { - /* - * multipart and message types aren't allowed to have - * encodings except for the three mentioned above. - * If it's one of these types, ignore the encoding. - */ - ContentType cType = new ContentType(type); - if (cType.match("multipart/*")) - return null; - if (cType.match("message/*") && - !PropUtil.getBooleanSystemProperty( - "mail.mime.allowencodedmessages", false)) - return null; - } catch (ParseException pex) { - // ignore it - } - return encoding; - } - - static void updateHeaders(MimePart part) throws MessagingException { - DataHandler dh = part.getDataHandler(); - if (dh == null) // Huh ? - return; - - try { - String type = dh.getContentType(); - boolean composite = false; - boolean needCTHeader = part.getHeader("Content-Type") == null; - - ContentType cType = new ContentType(type); - - /* - * If this is a multipart, give sub-parts a chance to - * update their headers. Even though the data for this - * multipart may have come from a stream, one of the - * sub-parts may have been updated. - */ - if (cType.match("multipart/*")) { - // If multipart, recurse - composite = true; - Object o; - if (part instanceof MimeBodyPart) { - MimeBodyPart mbp = (MimeBodyPart)part; - o = mbp.cachedContent != null ? - mbp.cachedContent : dh.getContent(); - } else if (part instanceof MimeMessage) { - MimeMessage msg = (MimeMessage)part; - o = msg.cachedContent != null ? - msg.cachedContent : dh.getContent(); - } else - o = dh.getContent(); - if (o instanceof MimeMultipart) - ((MimeMultipart)o).updateHeaders(); - else - throw new MessagingException("MIME part of type \"" + - type + "\" contains object of type " + - o.getClass().getName() + " instead of MimeMultipart"); - } else if (cType.match("message/rfc822")) { - composite = true; - // XXX - call MimeMessage.updateHeaders()? - } - - /* - * If the data for this part comes from a stream, - * we can't update it. - */ - if (dh instanceof MimePartDataHandler) - return; // can't update it - - // Content-Transfer-Encoding, but only if we don't - // already have one - if (!composite) { // not allowed on composite parts - if (part.getHeader("Content-Transfer-Encoding") == null) - setEncoding(part, MimeUtility.getEncoding(dh)); - - if (needCTHeader && setDefaultTextCharset && - cType.match("text/*") && - cType.getParameter("charset") == null) { - /* - * Set a default charset for text parts. - * We really should examine the data to determine - * whether or not it's all ASCII, but that's too - * expensive so we make an assumption: If we - * chose 7bit encoding for this data, it's probably - * ASCII. (MimeUtility.getEncoding will choose - * 7bit only in this case, but someone might've - * set the Content-Transfer-Encoding header manually.) - */ - String charset; - String enc = part.getEncoding(); - if (enc != null && enc.equalsIgnoreCase("7bit")) - charset = "us-ascii"; - else - charset = MimeUtility.getDefaultMIMECharset(); - cType.setParameter("charset", charset); - type = cType.toString(); - } - } - - // Now, let's update our own headers ... - - // Content-type, but only if we don't already have one - if (needCTHeader) { - /* - * Pull out "filename" from Content-Disposition, and - * use that to set the "name" parameter. This is to - * satisfy older MUAs (DtMail, Roam and probably - * a bunch of others). - */ - String s = part.getHeader("Content-Disposition", null); - if (s != null) { - // Parse the header .. - ContentDisposition cd = new ContentDisposition(s); - String filename = cd.getParameter("filename"); - if (filename != null) { - cType.setParameter("name", filename); - type = cType.toString(); - } - } - - part.setHeader("Content-Type", type); - } - } catch (IOException ex) { - throw new MessagingException("IOException updating headers", ex); - } - } - - static void invalidateContentHeaders(MimePart part) - throws MessagingException { - part.removeHeader("Content-Type"); - part.removeHeader("Content-Transfer-Encoding"); - } - - static void writeTo(MimePart part, OutputStream os, String[] ignoreList) - throws IOException, MessagingException { - - // see if we already have a LOS - LineOutputStream los = null; - if (os instanceof LineOutputStream) { - los = (LineOutputStream) os; - } else { - los = new LineOutputStream(os); - } - - // First, write out the header - Enumeration hdrLines = part.getNonMatchingHeaderLines(ignoreList); - while (hdrLines.hasMoreElements()) - los.writeln((String)hdrLines.nextElement()); - - // The CRLF separator between header and content - los.writeln(); - - // Finally, the content. Encode if required. - // XXX: May need to account for ESMTP ? - InputStream is = null; - byte[] buf = null; - try { - /* - * If the data for this part comes from a stream, - * just copy it to the output stream without decoding - * and reencoding it. - */ - DataHandler dh = part.getDataHandler(); - if (dh instanceof MimePartDataHandler) { - // call getContentStream to give subclass a chance to - // provide the data on demand - if (part instanceof MimeBodyPart) { - MimeBodyPart mbp = (MimeBodyPart)part; - is = mbp.getContentStream(); - } else if (part instanceof MimeMessage) { - MimeMessage msg = (MimeMessage)part; - is = msg.getContentStream(); - } - } - if (is != null) { - // now copy the data to the output stream - buf = new byte[8192]; - int len; - while ((len = is.read(buf)) > 0) - os.write(buf, 0, len); - } else { - os = MimeUtility.encode(os, - restrictEncoding(part, part.getEncoding())); - part.getDataHandler().writeTo(os); - } - } finally { - if (is != null) - is.close(); - buf = null; - } - os.flush(); // Needed to complete encoding - } - - /** - * A special DataHandler used only as a marker to indicate that - * the source of the data is a MimePart (that is, a byte array - * or a stream). This prevents updateHeaders from trying to - * change the headers for such data. In particular, the original - * Content-Transfer-Encoding for the data must be preserved. - * Otherwise the data would need to be decoded and reencoded. - */ - static class MimePartDataHandler extends DataHandler { - public MimePartDataHandler(DataSource ds) { - super(ds); - } - } -} diff --git a/src/main/java/javax/mail/internet/MimeMessage.java b/src/main/java/javax/mail/internet/MimeMessage.java deleted file mode 100644 index ebd6d353..00000000 --- a/src/main/java/javax/mail/internet/MimeMessage.java +++ /dev/null @@ -1,2228 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import javax.activation.*; -import java.lang.*; -import java.io.*; -import java.util.*; -import java.text.ParseException; -import com.sun.mail.util.*; -import javax.mail.util.SharedByteArrayInputStream; - -/** - * This class represents a MIME style email message. It implements - * the Message abstract class and the MimePart - * interface.

      - * - * Clients wanting to create new MIME style messages will instantiate - * an empty MimeMessage object and then fill it with appropriate - * attributes and content.

      - * - * Service providers that implement MIME compliant backend stores may - * want to subclass MimeMessage and override certain methods to provide - * specific implementations. The simplest case is probably a provider - * that generates a MIME style input stream and leaves the parsing of - * the stream to this class.

      - * - * MimeMessage uses the InternetHeaders class to parse and - * store the top level RFC 822 headers of a message.

      - * - * The mail.mime.address.strict session property controls - * the parsing of address headers. By default, strict parsing of address - * headers is done. If this property is set to "false", - * strict parsing is not done and many illegal addresses that sometimes - * occur in real messages are allowed. See the InternetAddress - * class for details.

      - * - *


      A note on RFC 822 and MIME headers

      - * - * RFC 822 header fields must contain only - * US-ASCII characters. MIME allows non ASCII characters to be present - * in certain portions of certain headers, by encoding those characters. - * RFC 2047 specifies the rules for doing this. The MimeUtility - * class provided in this package can be used to to achieve this. - * Callers of the setHeader, addHeader, and - * addHeaderLine methods are responsible for enforcing - * the MIME requirements for the specified headers. In addition, these - * header fields must be folded (wrapped) before being sent if they - * exceed the line length limitation for the transport (1000 bytes for - * SMTP). Received headers may have been folded. The application is - * responsible for folding and unfolding headers as appropriate.

      - * - * @author John Mani - * @author Bill Shannon - * @author Max Spivak - * @author Kanwar Oberoi - * @see javax.mail.internet.MimeUtility - * @see javax.mail.Part - * @see javax.mail.Message - * @see javax.mail.internet.MimePart - * @see javax.mail.internet.InternetAddress - */ - -public class MimeMessage extends Message implements MimePart { - - /** - * The DataHandler object representing this Message's content. - */ - protected DataHandler dh; - - /** - * Byte array that holds the bytes of this Message's content. - */ - protected byte[] content; - - /** - * If the data for this message was supplied by an - * InputStream that implements the SharedInputStream interface, - * contentStream is another such stream representing - * the content of this message. In this case, content - * will be null. - * - * @since JavaMail 1.2 - */ - protected InputStream contentStream; - - /** - * The InternetHeaders object that stores the header - * of this message. - */ - protected InternetHeaders headers; - - /** - * The Flags for this message. - */ - protected Flags flags; - - /** - * A flag indicating whether the message has been modified. - * If the message has not been modified, any data in the - * content array is assumed to be valid and is used - * directly in the writeTo method. This flag is - * set to true when an empty message is created or when the - * saveChanges method is called. - * - * @since JavaMail 1.2 - */ - protected boolean modified = false; - - /** - * Does the saveChanges method need to be called on - * this message? This flag is set to false by the public constructor - * and set to true by the saveChanges method. The - * writeTo method checks this flag and calls the - * saveChanges method as necessary. This avoids the - * common mistake of forgetting to call the saveChanges - * method on a newly constructed message. - * - * @since JavaMail 1.2 - */ - protected boolean saved = false; - - /** - * If our content is a Multipart or Message object, we save it - * the first time it's created by parsing a stream so that changes - * to the contained objects will not be lost.

      - * - * If this field is not null, it's return by the {@link #getContent} - * method. The {@link #getContent} method sets this field if it - * would return a Multipart or MimeMessage object. This field is - * is cleared by the {@link #setDataHandler} method. - * - * @since JavaMail 1.5 - */ - protected Object cachedContent; - - // Used to parse dates - private static final MailDateFormat mailDateFormat = new MailDateFormat(); - - // Should addresses in headers be parsed in "strict" mode? - private boolean strict = true; - - /** - * Default constructor. An empty message object is created. - * The headers field is set to an empty InternetHeaders - * object. The flags field is set to an empty Flags - * object. The modified flag is set to true. - */ - public MimeMessage(Session session) { - super(session); - modified = true; - headers = new InternetHeaders(); - flags = new Flags(); // empty flags object - initStrict(); - } - - /** - * Constructs a MimeMessage by reading and parsing the data from the - * specified MIME InputStream. The InputStream will be left positioned - * at the end of the data for the message. Note that the input stream - * parse is done within this constructor itself.

      - * - * The input stream contains an entire MIME formatted message with - * headers and data. - * - * @param session Session object for this message - * @param is the message input stream - * @exception MessagingException - */ - public MimeMessage(Session session, InputStream is) - throws MessagingException { - super(session); - flags = new Flags(); // empty Flags object - initStrict(); - parse(is); - saved = true; - } - - /** - * Constructs a new MimeMessage with content initialized from the - * source MimeMessage. The new message is independent - * of the original.

      - * - * Note: The current implementation is rather inefficient, copying - * the data more times than strictly necessary. - * - * @param source the message to copy content from - * @exception MessagingException - * @since JavaMail 1.2 - */ - public MimeMessage(MimeMessage source) throws MessagingException { - super(source.session); - flags = source.getFlags(); - if (flags == null) // make sure flags is always set - flags = new Flags(); - ByteArrayOutputStream bos; - int size = source.getSize(); - if (size > 0) - bos = new ByteArrayOutputStream(size); - else - bos = new ByteArrayOutputStream(); - try { - strict = source.strict; - source.writeTo(bos); - bos.close(); - SharedByteArrayInputStream bis = - new SharedByteArrayInputStream(bos.toByteArray()); - parse(bis); - bis.close(); - saved = true; - } catch (IOException ex) { - // should never happen, but just in case... - throw new MessagingException("IOException while copying message", - ex); - } - } - - /** - * Constructs an empty MimeMessage object with the given Folder - * and message number.

      - * - * This method is for providers subclassing MimeMessage. - */ - protected MimeMessage(Folder folder, int msgnum) { - super(folder, msgnum); - flags = new Flags(); // empty Flags object - saved = true; - initStrict(); - } - - /** - * Constructs a MimeMessage by reading and parsing the data from the - * specified MIME InputStream. The InputStream will be left positioned - * at the end of the data for the message. Note that the input stream - * parse is done within this constructor itself.

      - * - * This method is for providers subclassing MimeMessage. - * - * @param folder The containing folder. - * @param is the message input stream - * @param msgnum Message number of this message within its folder - * @exception MessagingException - */ - protected MimeMessage(Folder folder, InputStream is, int msgnum) - throws MessagingException { - this(folder, msgnum); - initStrict(); - parse(is); - } - - /** - * Constructs a MimeMessage from the given InternetHeaders object - * and content. - * - * This method is for providers subclassing MimeMessage. - * - * @param folder The containing folder. - * @param headers The headers - * @param content The message content - * @param msgnum Message number of this message within its folder - * @exception MessagingException - */ - protected MimeMessage(Folder folder, InternetHeaders headers, - byte[] content, int msgnum) throws MessagingException { - this(folder, msgnum); - this.headers = headers; - this.content = content; - initStrict(); - } - - /** - * Set the strict flag based on property. - */ - private void initStrict() { - if (session != null) - strict = PropUtil.getBooleanSessionProperty(session, - "mail.mime.address.strict", true); - } - - /** - * Parse the InputStream setting the headers and - * content fields appropriately. Also resets the - * modified flag.

      - * - * This method is intended for use by subclasses that need to - * control when the InputStream is parsed. - * - * @param is The message input stream - * @exception MessagingException - */ - protected void parse(InputStream is) throws MessagingException { - - if (!(is instanceof ByteArrayInputStream) && - !(is instanceof BufferedInputStream) && - !(is instanceof SharedInputStream)) - is = new BufferedInputStream(is); - - headers = createInternetHeaders(is); - - if (is instanceof SharedInputStream) { - SharedInputStream sis = (SharedInputStream)is; - contentStream = sis.newStream(sis.getPosition(), -1); - } else { - try { - content = ASCIIUtility.getBytes(is); - } catch (IOException ioex) { - throw new MessagingException("IOException", ioex); - } - } - - modified = false; - } - - /** - * Returns the value of the RFC 822 "From" header fields. If this - * header field is absent, the "Sender" header field is used. - * If the "Sender" header field is also absent, null - * is returned.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return Address object - * @exception MessagingException - * @see #headers - */ - public Address[] getFrom() throws MessagingException { - Address[] a = getAddressHeader("From"); - if (a == null) - a = getAddressHeader("Sender"); - - return a; - } - - /** - * Set the RFC 822 "From" header field. Any existing values are - * replaced with the given address. If address is null, - * this header is removed. - * - * @param address the sender of this message - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setFrom(Address address) throws MessagingException { - if (address == null) - removeHeader("From"); - else - setHeader("From", address.toString()); - } - - /** - * Set the RFC 822 "From" header field. Any existing values are - * replaced with the given addresses. If address is null, - * this header is removed. - * - * @param address the sender(s) of this message - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @since JvaMail 1.5 - */ - public void setFrom(String address) throws MessagingException { - if (address == null) - removeHeader("From"); - else - setAddressHeader("From", InternetAddress.parse(address)); - } - - /** - * Set the RFC 822 "From" header field using the value of the - * InternetAddress.getLocalAddress method. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setFrom() throws MessagingException { - InternetAddress me = null; - try { - me = InternetAddress._getLocalAddress(session); - } catch (Exception ex) { - // if anything goes wrong (SecurityException, UnknownHostException), - // chain the exception - throw new MessagingException("No From address", ex); - } - if (me != null) - setFrom(me); - else - throw new MessagingException("No From address"); - } - - /** - * Add the specified addresses to the existing "From" field. If - * the "From" field does not already exist, it is created. - * - * @param addresses the senders of this message - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void addFrom(Address[] addresses) throws MessagingException { - addAddressHeader("From", addresses); - } - - /** - * Returns the value of the RFC 822 "Sender" header field. - * If the "Sender" header field is absent, null - * is returned.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return Address object - * @exception MessagingException - * @see #headers - * @since JavaMail 1.3 - */ - public Address getSender() throws MessagingException { - Address[] a = getAddressHeader("Sender"); - if (a == null || a.length == 0) - return null; - return a[0]; // there can be only one - } - - /** - * Set the RFC 822 "Sender" header field. Any existing values are - * replaced with the given address. If address is null, - * this header is removed. - * - * @param address the sender of this message - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @since JavaMail 1.3 - */ - public void setSender(Address address) throws MessagingException { - if (address == null) - removeHeader("Sender"); - else - setHeader("Sender", address.toString()); - } - - /** - * This inner class extends the javax.mail.Message.RecipientType - * class to add additional RecipientTypes. The one additional - * RecipientType currently defined here is NEWSGROUPS. - * - * @see javax.mail.Message.RecipientType - */ - public static class RecipientType extends Message.RecipientType { - - private static final long serialVersionUID = -5468290701714395543L; - - /** - * The "Newsgroup" (Usenet news) recipients. - */ - public static final RecipientType NEWSGROUPS = - new RecipientType("Newsgroups"); - protected RecipientType(String type) { - super(type); - } - - protected Object readResolve() throws ObjectStreamException { - if (type.equals("Newsgroups")) - return NEWSGROUPS; - else - return super.readResolve(); - } - } - - /** - * Returns the recepients specified by the type. The mapping - * between the type and the corresponding RFC 822 header is - * as follows: - *

      -     *		Message.RecipientType.TO		"To"
      -     *		Message.RecipientType.CC		"Cc"
      -     *		Message.RecipientType.BCC		"Bcc"
      -     *		MimeMessage.RecipientType.NEWSGROUPS	"Newsgroups"
      -     * 

      - * - * Returns null if the header specified by the type is not found - * or if its value is empty.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @param type Type of recepient - * @return array of Address objects - * @exception MessagingException if header could not - * be retrieved - * @exception AddressException if the header is misformatted - * @see #headers - * @see javax.mail.Message.RecipientType#TO - * @see javax.mail.Message.RecipientType#CC - * @see javax.mail.Message.RecipientType#BCC - * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS - */ - public Address[] getRecipients(Message.RecipientType type) - throws MessagingException { - if (type == RecipientType.NEWSGROUPS) { - String s = getHeader("Newsgroups", ","); - return (s == null) ? null : NewsAddress.parse(s); - } else - return getAddressHeader(getHeaderName(type)); - } - - /** - * Get all the recipient addresses for the message. - * Extracts the TO, CC, BCC, and NEWSGROUPS recipients. - * - * @return array of Address objects - * @exception MessagingException - * @see javax.mail.Message.RecipientType#TO - * @see javax.mail.Message.RecipientType#CC - * @see javax.mail.Message.RecipientType#BCC - * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS - */ - public Address[] getAllRecipients() throws MessagingException { - Address[] all = super.getAllRecipients(); - Address[] ng = getRecipients(RecipientType.NEWSGROUPS); - - if (ng == null) - return all; // the common case - if (all == null) - return ng; // a rare case - - Address[] addresses = new Address[all.length + ng.length]; - System.arraycopy(all, 0, addresses, 0, all.length); - System.arraycopy(ng, 0, addresses, all.length, ng.length); - return addresses; - } - - /** - * Set the specified recipient type to the given addresses. - * If the address parameter is null, the corresponding - * recipient field is removed. - * - * @param type Recipient type - * @param addresses Addresses - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @see #getRecipients - */ - public void setRecipients(Message.RecipientType type, Address[] addresses) - throws MessagingException { - if (type == RecipientType.NEWSGROUPS) { - if (addresses == null || addresses.length == 0) - removeHeader("Newsgroups"); - else - setHeader("Newsgroups", NewsAddress.toString(addresses)); - } else - setAddressHeader(getHeaderName(type), addresses); - } - - /** - * Set the specified recipient type to the given addresses. - * If the address parameter is null, the corresponding - * recipient field is removed. - * - * @param type Recipient type - * @param addresses Addresses - * @exception AddressException if the attempt to parse the - * addresses String fails - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @see #getRecipients - * @since JavaMail 1.2 - */ - public void setRecipients(Message.RecipientType type, String addresses) - throws MessagingException { - if (type == RecipientType.NEWSGROUPS) { - if (addresses == null || addresses.length() == 0) - removeHeader("Newsgroups"); - else - setHeader("Newsgroups", addresses); - } else - setAddressHeader(getHeaderName(type), - addresses == null ? null : InternetAddress.parse(addresses)); - } - - /** - * Add the given addresses to the specified recipient type. - * - * @param type Recipient type - * @param addresses Addresses - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void addRecipients(Message.RecipientType type, Address[] addresses) - throws MessagingException { - if (type == RecipientType.NEWSGROUPS) { - String s = NewsAddress.toString(addresses); - if (s != null) - addHeader("Newsgroups", s); - } else - addAddressHeader(getHeaderName(type), addresses); - } - - /** - * Add the given addresses to the specified recipient type. - * - * @param type Recipient type - * @param addresses Addresses - * @exception AddressException if the attempt to parse the - * addresses String fails - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - * @since JavaMail 1.2 - */ - public void addRecipients(Message.RecipientType type, String addresses) - throws MessagingException { - if (type == RecipientType.NEWSGROUPS) { - if (addresses != null && addresses.length() != 0) - addHeader("Newsgroups", addresses); - } else - addAddressHeader(getHeaderName(type), InternetAddress.parse(addresses)); - } - - /** - * Return the value of the RFC 822 "Reply-To" header field. If - * this header is unavailable or its value is absent, then - * the getFrom method is called and its value is returned. - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @exception MessagingException - * @see #headers - */ - public Address[] getReplyTo() throws MessagingException { - Address[] a = getAddressHeader("Reply-To"); - if (a == null || a.length == 0) - a = getFrom(); - return a; - } - - /** - * Set the RFC 822 "Reply-To" header field. If the address - * parameter is null, this header is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setReplyTo(Address[] addresses) throws MessagingException { - setAddressHeader("Reply-To", addresses); - } - - // Convenience method to get addresses - private Address[] getAddressHeader(String name) - throws MessagingException { - String s = getHeader(name, ","); - return (s == null) ? null : InternetAddress.parseHeader(s, strict); - } - - // Convenience method to set addresses - private void setAddressHeader(String name, Address[] addresses) - throws MessagingException { - String s = InternetAddress.toString(addresses); - if (s == null) - removeHeader(name); - else - setHeader(name, s); - } - - private void addAddressHeader(String name, Address[] addresses) - throws MessagingException { - if (addresses == null || addresses.length == 0) - return; - Address[] a = getAddressHeader(name); - Address[] anew; - if (a == null || a.length == 0) - anew = addresses; - else { - anew = new Address[a.length + addresses.length]; - System.arraycopy(a, 0, anew, 0, a.length); - System.arraycopy(addresses, 0, anew, a.length, addresses.length); - } - String s = InternetAddress.toString(anew); - if (s == null) - return; - setHeader(name, s); - } - - /** - * Returns the value of the "Subject" header field. Returns null - * if the subject field is unavailable or its value is absent.

      - * - * If the subject is encoded as per RFC 2047, it is decoded and - * converted into Unicode. If the decoding or conversion fails, the - * raw data is returned as is.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return Subject - * @exception MessagingException - * @see #headers - */ - public String getSubject() throws MessagingException { - String rawvalue = getHeader("Subject", null); - - if (rawvalue == null) - return null; - - try { - return MimeUtility.decodeText(MimeUtility.unfold(rawvalue)); - } catch (UnsupportedEncodingException ex) { - return rawvalue; - } - } - - /** - * Set the "Subject" header field. If the subject contains - * non US-ASCII characters, it will be encoded using the - * platform's default charset. If the subject contains only - * US-ASCII characters, no encoding is done and it is used - * as-is. If the subject is null, the existing "Subject" field - * is removed.

      - * - * The application must ensure that the subject does not contain - * any line breaks.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param subject The subject - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException. An - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setSubject(String subject) throws MessagingException { - setSubject(subject, null); - } - - /** - * Set the "Subject" header field. If the subject contains non - * US-ASCII characters, it will be encoded using the specified - * charset. If the subject contains only US-ASCII characters, no - * encoding is done and it is used as-is. If the subject is null, - * the existing "Subject" header field is removed.

      - * - * The application must ensure that the subject does not contain - * any line breaks.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param subject The subject - * @param charset The charset - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException. An - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setSubject(String subject, String charset) - throws MessagingException { - if (subject == null) { - removeHeader("Subject"); - } else { - try { - setHeader("Subject", MimeUtility.fold(9, - MimeUtility.encodeText(subject, charset, null))); - } catch (UnsupportedEncodingException uex) { - throw new MessagingException("Encoding error", uex); - } - } - } - - /** - * Returns the value of the RFC 822 "Date" field. This is the date - * on which this message was sent. Returns null if this field is - * unavailable or its value is absent.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return The sent Date - * @exception MessagingException - */ - public Date getSentDate() throws MessagingException { - String s = getHeader("Date", null); - if (s != null) { - try { - synchronized (mailDateFormat) { - return mailDateFormat.parse(s); - } - } catch (ParseException pex) { - return null; - } - } - - return null; - } - - /** - * Set the RFC 822 "Date" header field. This is the date on which the - * creator of the message indicates that the message is complete - * and ready for delivery. If the date parameter is - * null, the existing "Date" field is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setSentDate(Date d) throws MessagingException { - if (d == null) - removeHeader("Date"); - else { - synchronized (mailDateFormat) { - setHeader("Date", mailDateFormat.format(d)); - } - } - } - - /** - * Returns the Date on this message was received. Returns - * null if this date cannot be obtained.

      - * - * Note that RFC 822 does not define a field for the received - * date. Hence only implementations that can provide this date - * need return a valid value.

      - * - * This implementation returns null. - * - * @return the date this message was received - * @exception MessagingException - */ - public Date getReceivedDate() throws MessagingException { - return null; - } - - /** - * Return the size of the content of this message in bytes. - * Return -1 if the size cannot be determined.

      - * - * Note that this number may not be an exact measure of the - * content size and may or may not account for any transfer - * encoding of the content.

      - * - * This implementation returns the size of the content - * array (if not null), or, if contentStream is not - * null, and the available method returns a positive - * number, it returns that number as the size. Otherwise, it returns - * -1. - * - * @return size of content in bytes - * @exception MessagingException - */ - public int getSize() throws MessagingException { - if (content != null) - return content.length; - if (contentStream != null) { - try { - int size = contentStream.available(); - // only believe the size if it's greater than zero, since zero - // is the default returned by the InputStream class itself - if (size > 0) - return size; - } catch (IOException ex) { - // ignore it - } - } - return -1; - } - - /** - * Return the number of lines for the content of this message. - * Return -1 if this number cannot be determined.

      - * - * Note that this number may not be an exact measure of the - * content length and may or may not account for any transfer - * encoding of the content.

      - * - * This implementation returns -1. - * - * @return number of lines in the content. - * @exception MessagingException - */ - public int getLineCount() throws MessagingException { - return -1; - } - - /** - * Returns the value of the RFC 822 "Content-Type" header field. - * This represents the content-type of the content of this - * message. This value must not be null. If this field is - * unavailable, "text/plain" should be returned.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return The ContentType of this part - * @exception MessagingException - * @see javax.activation.DataHandler - */ - public String getContentType() throws MessagingException { - String s = getHeader("Content-Type", null); - s = MimeUtil.cleanContentType(this, s); - if (s == null) - return "text/plain"; - return s; - } - - /** - * Is this Part of the specified MIME type? This method - * compares only the primaryType and - * subType. - * The parameters of the content types are ignored.

      - * - * For example, this method will return true when - * comparing a Part of content type "text/plain" - * with "text/plain; charset=foobar".

      - * - * If the subType of mimeType is the - * special character '*', then the subtype is ignored during the - * comparison. - */ - public boolean isMimeType(String mimeType) throws MessagingException { - return MimeBodyPart.isMimeType(this, mimeType); - } - - /** - * Returns the value of the "Content-Disposition" header field. - * This represents the disposition of this part. The disposition - * describes how the part should be presented to the user.

      - * - * If the Content-Disposition field is unavailable, - * null is returned.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return disposition of this part, or null if unknown - * @exception MessagingException - */ - public String getDisposition() throws MessagingException { - return MimeBodyPart.getDisposition(this); - } - - /** - * Set the "Content-Disposition" header field of this Message. - * If disposition is null, any existing "Content-Disposition" - * header field is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setDisposition(String disposition) throws MessagingException { - MimeBodyPart.setDisposition(this, disposition); - } - - /** - * Returns the content transfer encoding from the - * "Content-Transfer-Encoding" header - * field. Returns null if the header is unavailable - * or its value is absent.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return content-transfer-encoding - * @exception MessagingException - */ - public String getEncoding() throws MessagingException { - return MimeBodyPart.getEncoding(this); - } - - /** - * Returns the value of the "Content-ID" header field. Returns - * null if the field is unavailable or its value is - * absent.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return content-ID - * @exception MessagingException - */ - public String getContentID() throws MessagingException { - return getHeader("Content-Id", null); - } - - /** - * Set the "Content-ID" header field of this Message. - * If the cid parameter is null, any existing - * "Content-ID" is removed. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setContentID(String cid) throws MessagingException { - if (cid == null) - removeHeader("Content-ID"); - else - setHeader("Content-ID", cid); - } - - /** - * Return the value of the "Content-MD5" header field. Returns - * null if this field is unavailable or its value - * is absent.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return content-MD5 - * @exception MessagingException - */ - public String getContentMD5() throws MessagingException { - return getHeader("Content-MD5", null); - } - - /** - * Set the "Content-MD5" header field of this Message. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setContentMD5(String md5) throws MessagingException { - setHeader("Content-MD5", md5); - } - - /** - * Returns the "Content-Description" header field of this Message. - * This typically associates some descriptive information with - * this part. Returns null if this field is unavailable or its - * value is absent.

      - * - * If the Content-Description field is encoded as per RFC 2047, - * it is decoded and converted into Unicode. If the decoding or - * conversion fails, the raw data is returned as-is

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return content-description - * @exception MessagingException - */ - public String getDescription() throws MessagingException { - return MimeBodyPart.getDescription(this); - } - - /** - * Set the "Content-Description" header field for this Message. - * If the description parameter is null, then any - * existing "Content-Description" fields are removed.

      - * - * If the description contains non US-ASCII characters, it will - * be encoded using the platform's default charset. If the - * description contains only US-ASCII characters, no encoding - * is done and it is used as-is.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param description content-description - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException. An - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setDescription(String description) throws MessagingException { - setDescription(description, null); - } - - /** - * Set the "Content-Description" header field for this Message. - * If the description parameter is null, then any - * existing "Content-Description" fields are removed.

      - * - * If the description contains non US-ASCII characters, it will - * be encoded using the specified charset. If the description - * contains only US-ASCII characters, no encoding is done and - * it is used as-is.

      - * - * Note that if the charset encoding process fails, a - * MessagingException is thrown, and an UnsupportedEncodingException - * is included in the chain of nested exceptions within the - * MessagingException. - * - * @param description Description - * @param charset Charset for encoding - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException. An - * UnsupportedEncodingException may be included - * in the exception chain if the charset - * conversion fails. - */ - public void setDescription(String description, String charset) - throws MessagingException { - MimeBodyPart.setDescription(this, description, charset); - } - - /** - * Get the languages specified in the "Content-Language" header - * field of this message. The Content-Language header is defined by - * RFC 1766. Returns null if this field is unavailable - * or its value is absent.

      - * - * This implementation uses the getHeader method - * to obtain the requisite header field. - * - * @return value of content-language header. - * @exception MessagingException - */ - public String[] getContentLanguage() throws MessagingException { - return MimeBodyPart.getContentLanguage(this); - } - - /** - * Set the "Content-Language" header of this MimePart. The - * Content-Language header is defined by RFC 1766. - * - * @param languages array of language tags - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setContentLanguage(String[] languages) - throws MessagingException { - MimeBodyPart.setContentLanguage(this, languages); - } - - /** - * Returns the value of the "Message-ID" header field. Returns - * null if this field is unavailable or its value is absent.

      - * - * The default implementation provided here uses the - * getHeader method to return the value of the - * "Message-ID" field. - * - * @return Message-ID - * @exception MessagingException if the retrieval of this field - * causes any exception. - * @see javax.mail.search.MessageIDTerm - * @since JavaMail 1.1 - */ - public String getMessageID() throws MessagingException { - return getHeader("Message-ID", null); - } - - /** - * Get the filename associated with this Message.

      - * - * Returns the value of the "filename" parameter from the - * "Content-Disposition" header field of this message. If it's - * not available, returns the value of the "name" parameter from - * the "Content-Type" header field of this BodyPart. - * Returns null if both are absent.

      - * - * If the mail.mime.encodefilename System property - * is set to true, the {@link MimeUtility#decodeText - * MimeUtility.decodeText} method will be used to decode the - * filename. While such encoding is not supported by the MIME - * spec, many mailers use this technique to support non-ASCII - * characters in filenames. The default value of this property - * is false. - * - * @return filename - * @exception MessagingException - */ - public String getFileName() throws MessagingException { - return MimeBodyPart.getFileName(this); - } - - /** - * Set the filename associated with this part, if possible.

      - * - * Sets the "filename" parameter of the "Content-Disposition" - * header field of this message.

      - * - * If the mail.mime.encodefilename System property - * is set to true, the {@link MimeUtility#encodeText - * MimeUtility.encodeText} method will be used to encode the - * filename. While such encoding is not supported by the MIME - * spec, many mailers use this technique to support non-ASCII - * characters in filenames. The default value of this property - * is false. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setFileName(String filename) throws MessagingException { - MimeBodyPart.setFileName(this, filename); - } - - private String getHeaderName(Message.RecipientType type) - throws MessagingException { - String headerName; - - if (type == Message.RecipientType.TO) - headerName = "To"; - else if (type == Message.RecipientType.CC) - headerName = "Cc"; - else if (type == Message.RecipientType.BCC) - headerName = "Bcc"; - else if (type == MimeMessage.RecipientType.NEWSGROUPS) - headerName = "Newsgroups"; - else - throw new MessagingException("Invalid Recipient Type"); - return headerName; - } - - - /** - * Return a decoded input stream for this Message's "content".

      - * - * This implementation obtains the input stream from the DataHandler, - * that is, it invokes getDataHandler().getInputStream(). - * - * @return an InputStream - * @exception MessagingException - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - * - * @see #getContentStream - * @see javax.activation.DataHandler#getInputStream - */ - public InputStream getInputStream() - throws IOException, MessagingException { - return getDataHandler().getInputStream(); - } - - /** - * Produce the raw bytes of the content. This method is used during - * parsing, to create a DataHandler object for the content. Subclasses - * that can provide a separate input stream for just the message - * content might want to override this method.

      - * - * This implementation returns a SharedInputStream, if - * contentStream is not null. Otherwise, it - * returns a ByteArrayInputStream constructed - * out of the content byte array. - * - * @see #content - */ - protected InputStream getContentStream() throws MessagingException { - if (contentStream != null) - return ((SharedInputStream)contentStream).newStream(0, -1); - if (content != null) - return new SharedByteArrayInputStream(content); - - throw new MessagingException("No MimeMessage content"); - } - - /** - * Return an InputStream to the raw data with any Content-Transfer-Encoding - * intact. This method is useful if the "Content-Transfer-Encoding" - * header is incorrect or corrupt, which would prevent the - * getInputStream method or getContent method - * from returning the correct data. In such a case the application may - * use this method and attempt to decode the raw data itself.

      - * - * This implementation simply calls the getContentStream - * method. - * - * @see #getInputStream - * @see #getContentStream - * @since JavaMail 1.2 - */ - public InputStream getRawInputStream() throws MessagingException { - return getContentStream(); - } - - /** - * Return a DataHandler for this Message's content.

      - * - * The implementation provided here works as follows. Note the use of - * the getContentStream method to - * generate the byte stream for the content. Also note that - * any transfer-decoding is done automatically within this method.

      - * - *

      -     *  getDataHandler() {
      -     *      if (dh == null) {
      -     *          dh = new DataHandler(new MimePartDataSource(this));
      -     *      }
      -     *      return dh;
      -     *  }
      -     *  

      - * class MimePartDataSource implements DataSource { - * public getInputStream() { - * return MimeUtility.decode( - * getContentStream(), getEncoding()); - * } - * - * .... - * } - *

      - * - * @exception MessagingException - */ - public synchronized DataHandler getDataHandler() - throws MessagingException { - if (dh == null) - dh = new MimeBodyPart.MimePartDataHandler( - new MimePartDataSource(this)); - return dh; - } - - /** - * Return the content as a Java object. The type of this - * object is dependent on the content itself. For - * example, the native format of a "text/plain" content - * is usually a String object. The native format for a "multipart" - * message is always a Multipart subclass. For content types that are - * unknown to the DataHandler system, an input stream is returned - * as the content.

      - * - * This implementation obtains the content from the DataHandler, - * that is, it invokes getDataHandler().getContent(). - * If the content is a Multipart or Message object and was created by - * parsing a stream, the object is cached and returned in subsequent - * calls so that modifications to the content will not be lost. - * - * @return Object - * @see javax.mail.Part - * @see javax.activation.DataHandler#getContent - * @exception MessagingException - * @exception IOException this is typically thrown by the - * DataHandler. Refer to the documentation for - * javax.activation.DataHandler for more details. - */ - public Object getContent() throws IOException, MessagingException { - if (cachedContent != null) - return cachedContent; - Object c; - try { - c = getDataHandler().getContent(); - } catch (FolderClosedIOException fex) { - throw new FolderClosedException(fex.getFolder(), fex.getMessage()); - } catch (MessageRemovedIOException mex) { - throw new MessageRemovedException(mex.getMessage()); - } - if (MimeBodyPart.cacheMultipart && - (c instanceof Multipart || c instanceof Message) && - (content != null || contentStream != null)) { - cachedContent = c; - /* - * We may abandon the input stream so make sure - * the MimeMultipart has consumed the stream. - */ - if (c instanceof MimeMultipart) - ((MimeMultipart)c).parse(); - } - return c; - } - - /** - * This method provides the mechanism to set this part's content. - * The given DataHandler object should wrap the actual content. - * - * @param dh The DataHandler for the content. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public synchronized void setDataHandler(DataHandler dh) - throws MessagingException { - this.dh = dh; - cachedContent = null; - MimeBodyPart.invalidateContentHeaders(this); - } - - /** - * A convenience method for setting this Message's content.

      - * - * The content is wrapped in a DataHandler object. Note that a - * DataContentHandler class for the specified type should be - * available to the JavaMail implementation for this to work right. - * i.e., to do setContent(foobar, "application/x-foobar"), - * a DataContentHandler for "application/x-foobar" should be installed. - * Refer to the Java Activation Framework for more information. - * - * @param o the content object - * @param type Mime type of the object - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setContent(Object o, String type) - throws MessagingException { - if (o instanceof Multipart) - setContent((Multipart)o); - else - setDataHandler(new DataHandler(o, type)); - } - - /** - * Convenience method that sets the given String as this - * part's content, with a MIME type of "text/plain". If the - * string contains non US-ASCII characters. it will be encoded - * using the platform's default charset. The charset is also - * used to set the "charset" parameter.

      - * - * Note that there may be a performance penalty if - * text is large, since this method may have - * to scan all the characters to determine what charset to - * use.

      - * - * If the charset is already known, use the - * setText method that takes the charset parameter. - * - * @param text the text content to set - * @exception MessagingException if an error occurs - * @see #setText(String text, String charset) - */ - public void setText(String text) throws MessagingException { - setText(text, null); - } - - /** - * Convenience method that sets the given String as this part's - * content, with a MIME type of "text/plain" and the specified - * charset. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * the "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @exception MessagingException if an error occurs - */ - public void setText(String text, String charset) - throws MessagingException { - MimeBodyPart.setText(this, text, charset, "plain"); - } - - /** - * Convenience method that sets the given String as this part's - * content, with a primary MIME type of "text" and the specified - * MIME subtype. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * the "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @param subtype the MIME subtype to use (e.g., "html") - * @exception MessagingException if an error occurs - * @since JavaMail 1.4 - */ - public void setText(String text, String charset, String subtype) - throws MessagingException { - MimeBodyPart.setText(this, text, charset, subtype); - } - - /** - * This method sets the Message's content to a Multipart object. - * - * @param mp The multipart object that is the Message's content - * @exception IllegalWriteException if the underlying - * implementation does not support modification of - * existing values - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setContent(Multipart mp) throws MessagingException { - setDataHandler(new DataHandler(mp, mp.getContentType())); - mp.setParent(this); - } - - /** - * Get a new Message suitable for a reply to this message. - * The new Message will have its attributes and headers - * set up appropriately. Note that this new message object - * will be empty, i.e., it will not have a "content". - * These will have to be suitably filled in by the client.

      - * - * If replyToAll is set, the new Message will be addressed - * to all recipients of this message. Otherwise, the reply will be - * addressed to only the sender of this message (using the value - * of the getReplyTo method).

      - * - * The "Subject" field is filled in with the original subject - * prefixed with "Re:" (unless it already starts with "Re:"). - * The "In-Reply-To" header is set in the new message if this - * message has a "Message-Id" header. The ANSWERED - * flag is set in this message. - * - * The current implementation also sets the "References" header - * in the new message to include the contents of the "References" - * header (or, if missing, the "In-Reply-To" header) in this message, - * plus the contents of the "Message-Id" header of this message, - * as described in RFC 2822. - * - * @param replyToAll reply should be sent to all recipients - * of this message - * @return the reply Message - * @exception MessagingException - */ - public Message reply(boolean replyToAll) throws MessagingException { - return reply(replyToAll, true); - } - - /** - * Get a new Message suitable for a reply to this message. - * The new Message will have its attributes and headers - * set up appropriately. Note that this new message object - * will be empty, i.e., it will not have a "content". - * These will have to be suitably filled in by the client.

      - * - * If replyToAll is set, the new Message will be addressed - * to all recipients of this message. Otherwise, the reply will be - * addressed to only the sender of this message (using the value - * of the getReplyTo method).

      - * - * If setAnswered is set, the - * {@link javax.mail.Flags.Flag#ANSWERED ANSWERED} flag is set - * in this message.

      - * - * The "Subject" field is filled in with the original subject - * prefixed with "Re:" (unless it already starts with "Re:"). - * The "In-Reply-To" header is set in the new message if this - * message has a "Message-Id" header. - * - * The current implementation also sets the "References" header - * in the new message to include the contents of the "References" - * header (or, if missing, the "In-Reply-To" header) in this message, - * plus the contents of the "Message-Id" header of this message, - * as described in RFC 2822. - * - * @param replyToAll reply should be sent to all recipients - * of this message - * @param setAnswered set the ANSWERED flag in this message? - * @return the reply Message - * @exception MessagingException - * @since JavaMail 1.5 - */ - public Message reply(boolean replyToAll, boolean setAnswered) - throws MessagingException { - MimeMessage reply = createMimeMessage(session); - /* - * Have to manipulate the raw Subject header so that we don't lose - * any encoding information. This is safe because "Re:" isn't - * internationalized and (generally) isn't encoded. If the entire - * Subject header is encoded, prefixing it with "Re: " still leaves - * a valid and correct encoded header. - */ - String subject = getHeader("Subject", null); - if (subject != null) { - if (!subject.regionMatches(true, 0, "Re: ", 0, 4)) - subject = "Re: " + subject; - reply.setHeader("Subject", subject); - } - Address a[] = getReplyTo(); - reply.setRecipients(Message.RecipientType.TO, a); - if (replyToAll) { - Vector v = new Vector(); - // add my own address to list - InternetAddress me = InternetAddress.getLocalAddress(session); - if (me != null) - v.addElement(me); - // add any alternate names I'm known by - String alternates = null; - if (session != null) - alternates = session.getProperty("mail.alternates"); - if (alternates != null) - eliminateDuplicates(v, - InternetAddress.parse(alternates, false)); - // should we Cc all other original recipients? - String replyallccStr = null; - boolean replyallcc = false; - if (session != null) - replyallcc = PropUtil.getBooleanSessionProperty(session, - "mail.replyallcc", false); - // add the recipients from the To field so far - eliminateDuplicates(v, a); - a = getRecipients(Message.RecipientType.TO); - a = eliminateDuplicates(v, a); - if (a != null && a.length > 0) { - if (replyallcc) - reply.addRecipients(Message.RecipientType.CC, a); - else - reply.addRecipients(Message.RecipientType.TO, a); - } - a = getRecipients(Message.RecipientType.CC); - a = eliminateDuplicates(v, a); - if (a != null && a.length > 0) - reply.addRecipients(Message.RecipientType.CC, a); - // don't eliminate duplicate newsgroups - a = getRecipients(RecipientType.NEWSGROUPS); - if (a != null && a.length > 0) - reply.setRecipients(RecipientType.NEWSGROUPS, a); - } - - String msgId = getHeader("Message-Id", null); - if (msgId != null) - reply.setHeader("In-Reply-To", msgId); - - /* - * Set the References header as described in RFC 2822: - * - * The "References:" field will contain the contents of the parent's - * "References:" field (if any) followed by the contents of the parent's - * "Message-ID:" field (if any). If the parent message does not contain - * a "References:" field but does have an "In-Reply-To:" field - * containing a single message identifier, then the "References:" field - * will contain the contents of the parent's "In-Reply-To:" field - * followed by the contents of the parent's "Message-ID:" field (if - * any). If the parent has none of the "References:", "In-Reply-To:", - * or "Message-ID:" fields, then the new message will have no - * "References:" field. - */ - String refs = getHeader("References", " "); - if (refs == null) { - // XXX - should only use if it contains a single message identifier - refs = getHeader("In-Reply-To", " "); - } - if (msgId != null) { - if (refs != null) - refs = MimeUtility.unfold(refs) + " " + msgId; - else - refs = msgId; - } - if (refs != null) - reply.setHeader("References", MimeUtility.fold(12, refs)); - - if (setAnswered) { - try { - setFlags(answeredFlag, true); - } catch (MessagingException mex) { - // ignore it - } - } - return reply; - } - - // used above in reply() - private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED); - - /** - * Check addrs for any duplicates that may already be in v. - * Return a new array without the duplicates. Add any new - * addresses to v. Note that the input array may be modified. - */ - private Address[] eliminateDuplicates(Vector v, Address[] addrs) { - if (addrs == null) - return null; - int gone = 0; - for (int i = 0; i < addrs.length; i++) { - boolean found = false; - // search the vector for this address - for (int j = 0; j < v.size(); j++) { - if (((InternetAddress)v.elementAt(j)).equals(addrs[i])) { - // found it; count it and remove it from the input array - found = true; - gone++; - addrs[i] = null; - break; - } - } - if (!found) - v.addElement(addrs[i]); // add new address to vector - } - // if we found any duplicates, squish the array - if (gone != 0) { - Address[] a; - // new array should be same type as original array - // XXX - there must be a better way, perhaps reflection? - if (addrs instanceof InternetAddress[]) - a = new InternetAddress[addrs.length - gone]; - else - a = new Address[addrs.length - gone]; - for (int i = 0, j = 0; i < addrs.length; i++) - if (addrs[i] != null) - a[j++] = addrs[i]; - addrs = a; - } - return addrs; - } - - /** - * Output the message as an RFC 822 format stream.

      - * - * Note that, depending on how the messag was constructed, it may - * use a variety of line termination conventions. Generally the - * output should be sent through an appropriate FilterOutputStream - * that converts the line terminators to the desired form, either - * CRLF for MIME compatibility and for use in Internet protocols, - * or the local platform's line terminator for storage in a local - * text file.

      - * - * This implementation calls the writeTo(OutputStream, - * String[]) method with a null ignore list. - * - * @exception IOException if an error occurs writing to the stream - * or if an error is generated by the - * javax.activation layer. - * @exception MessagingException - * @see javax.activation.DataHandler#writeTo - */ - public void writeTo(OutputStream os) - throws IOException, MessagingException { - writeTo(os, null); - } - - /** - * Output the message as an RFC 822 format stream, without - * specified headers. If the saved flag is not set, - * the saveChanges method is called. - * If the modified flag is not - * set and the content array is not null, the - * content array is written directly, after - * writing the appropriate message headers. - * - * @exception javax.mail.MessagingException - * @exception IOException if an error occurs writing to the stream - * or if an error is generated by the - * javax.activation layer. - * @see javax.activation.DataHandler#writeTo - */ - public void writeTo(OutputStream os, String[] ignoreList) - throws IOException, MessagingException { - if (!saved) - saveChanges(); - - if (modified) { - MimeBodyPart.writeTo(this, os, ignoreList); - return; - } - - // Else, the content is untouched, so we can just output it - // First, write out the header - Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList); - LineOutputStream los = new LineOutputStream(os); - while (hdrLines.hasMoreElements()) - los.writeln((String)hdrLines.nextElement()); - - // The CRLF separator between header and content - los.writeln(); - - // Finally, the content. - if (content == null) { - // call getContentStream to give subclass a chance to - // provide the data on demand - InputStream is = null; - byte[] buf = new byte[8192]; - try { - is = getContentStream(); - // now copy the data to the output stream - int len; - while ((len = is.read(buf)) > 0) - os.write(buf, 0, len); - } finally { - if (is != null) - is.close(); - buf = null; - } - } else { - os.write(content); - } - os.flush(); - } - - /** - * Get all the headers for this header_name. Note that certain - * headers may be encoded as per RFC 2047 if they contain - * non US-ASCII characters and these should be decoded.

      - * - * This implementation obtains the headers from the - * headers InternetHeaders object. - * - * @param name name of header - * @return array of headers - * @exception MessagingException - * @see javax.mail.internet.MimeUtility - */ - public String[] getHeader(String name) - throws MessagingException { - return headers.getHeader(name); - } - - /** - * Get all the headers for this header name, returned as a single - * String, with headers separated by the delimiter. If the - * delimiter is null, only the first header is - * returned. - * - * @param name the name of this header - * @param delimiter separator between values - * @return the value fields for all headers with - * this name - * @exception MessagingException - */ - public String getHeader(String name, String delimiter) - throws MessagingException { - return headers.getHeader(name, delimiter); - } - - /** - * Set the value for this header_name. Replaces all existing - * header values with this new value. Note that RFC 822 headers - * must contain only US-ASCII characters, so a header that - * contains non US-ASCII characters must have been encoded by the - * caller as per the rules of RFC 2047. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void setHeader(String name, String value) - throws MessagingException { - headers.setHeader(name, value); - } - - /** - * Add this value to the existing values for this header_name. - * Note that RFC 822 headers must contain only US-ASCII - * characters, so a header that contains non US-ASCII characters - * must have been encoded as per the rules of RFC 2047. - * - * @param name header name - * @param value header value - * @see javax.mail.internet.MimeUtility - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void addHeader(String name, String value) - throws MessagingException { - headers.addHeader(name, value); - } - - /** - * Remove all headers with this name. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void removeHeader(String name) - throws MessagingException { - headers.removeHeader(name); - } - - /** - * Return all the headers from this Message as an enumeration - * of Header objects.

      - * - * Note that certain headers may be encoded as per RFC 2047 - * if they contain non US-ASCII characters and these should - * be decoded.

      - * - * This implementation obtains the headers from the - * headers InternetHeaders object. - * - * @return array of header objects - * @exception MessagingException - * @see javax.mail.internet.MimeUtility - */ - public Enumeration getAllHeaders() throws MessagingException { - return headers.getAllHeaders(); - } - - /** - * Return matching headers from this Message as an Enumeration of - * Header objects. This implementation obtains the headers from - * the headers InternetHeaders object. - * - * @exception MessagingException - */ - public Enumeration getMatchingHeaders(String[] names) - throws MessagingException { - return headers.getMatchingHeaders(names); - } - - /** - * Return non-matching headers from this Message as an - * Enumeration of Header objects. This implementation - * obtains the header from the headers InternetHeaders object. - * - * @exception MessagingException - */ - public Enumeration getNonMatchingHeaders(String[] names) - throws MessagingException { - return headers.getNonMatchingHeaders(names); - } - - /** - * Add a raw RFC 822 header-line. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void addHeaderLine(String line) throws MessagingException { - headers.addHeaderLine(line); - } - - /** - * Get all header lines as an Enumeration of Strings. A Header - * line is a raw RFC 822 header-line, containing both the "name" - * and "value" field. - * - * @exception MessagingException - */ - public Enumeration getAllHeaderLines() throws MessagingException { - return headers.getAllHeaderLines(); - } - - /** - * Get matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC 822 header-line, containing both - * the "name" and "value" field. - * - * @exception MessagingException - */ - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException { - return headers.getMatchingHeaderLines(names); - } - - /** - * Get non-matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC 822 header-line, containing both - * the "name" and "value" field. - * - * @exception MessagingException - */ - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException { - return headers.getNonMatchingHeaderLines(names); - } - - /** - * Return a Flags object containing the flags for - * this message.

      - * - * Note that a clone of the internal Flags object is returned, so - * modifying the returned Flags object will not affect the flags - * of this message. - * - * @return Flags object containing the flags for this message - * @exception MessagingException - * @see javax.mail.Flags - */ - public synchronized Flags getFlags() throws MessagingException { - return (Flags)flags.clone(); - } - - /** - * Check whether the flag specified in the flag - * argument is set in this message.

      - * - * This implementation checks this message's internal - * flags object. - * - * @param flag the flag - * @return value of the specified flag for this message - * @see javax.mail.Flags.Flag - * @see javax.mail.Flags.Flag#ANSWERED - * @see javax.mail.Flags.Flag#DELETED - * @see javax.mail.Flags.Flag#DRAFT - * @see javax.mail.Flags.Flag#FLAGGED - * @see javax.mail.Flags.Flag#RECENT - * @see javax.mail.Flags.Flag#SEEN - * @exception MessagingException - */ - public synchronized boolean isSet(Flags.Flag flag) - throws MessagingException { - return (flags.contains(flag)); - } - - /** - * Set the flags for this message.

      - * - * This implementation modifies the flags field. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public synchronized void setFlags(Flags flag, boolean set) - throws MessagingException { - if (set) - flags.add(flag); - else - flags.remove(flag); - } - - /** - * Updates the appropriate header fields of this message to be - * consistent with the message's contents. If this message is - * contained in a Folder, any changes made to this message are - * committed to the containing folder.

      - * - * If any part of a message's headers or contents are changed, - * saveChanges must be called to ensure that those - * changes are permanent. Otherwise, any such modifications may or - * may not be saved, depending on the folder implementation.

      - * - * Messages obtained from folders opened READ_ONLY should not be - * modified and saveChanges should not be called on such messages.

      - * - * This method sets the modified flag to true, the - * save flag to true, and then calls the - * updateHeaders method. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - public void saveChanges() throws MessagingException { - modified = true; - saved = true; - updateHeaders(); - } - - /** - * Update the Message-ID header. This method is called - * by the updateHeaders and allows a subclass - * to override only the algorithm for choosing a Message-ID. - * - * @since JavaMail 1.4 - */ - protected void updateMessageID() throws MessagingException { - setHeader("Message-ID", - "<" + UniqueValue.getUniqueMessageIDValue(session) + ">"); - - } - - /** - * Called by the saveChanges method to actually - * update the MIME headers. The implementation here sets the - * Content-Transfer-Encoding header (if needed - * and not already set), the MIME-Version header - * and the Message-ID header. Also, if the content - * of this message is a MimeMultipart, its - * updateHeaders method is called.

      - * - * If the {@link #cachedContent} field is not null (that is, - * it references a Multipart or Message object), then - * that object is used to set a new DataHandler, any - * stream data used to create this object is discarded, - * and the {@link #cachedContent} field is cleared. - * - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this message is - * obtained from a READ_ONLY folder. - * @exception MessagingException - */ - protected synchronized void updateHeaders() throws MessagingException { - MimeBodyPart.updateHeaders(this); - setHeader("MIME-Version", "1.0"); - updateMessageID(); - - if (cachedContent != null) { - dh = new DataHandler(cachedContent, getContentType()); - cachedContent = null; - content = null; - if (contentStream != null) { - try { - contentStream.close(); - } catch (IOException ioex) { } // nothing to do - } - contentStream = null; - } - } - - /** - * Create and return an InternetHeaders object that loads the - * headers from the given InputStream. Subclasses can override - * this method to return a subclass of InternetHeaders, if - * necessary. This implementation simply constructs and returns - * an InternetHeaders object. - * - * @param is the InputStream to read the headers from - * @exception MessagingException - * @since JavaMail 1.2 - */ - protected InternetHeaders createInternetHeaders(InputStream is) - throws MessagingException { - return new InternetHeaders(is); - } - - /** - * Create and return a MimeMessage object. The reply method - * uses this method to create the MimeMessage object that it - * will return. Subclasses can override this method to return - * a subclass of MimeMessage. This implementation simply constructs - * and returns a MimeMessage object using the supplied Session. - * - * @param session the Session to use for the new message - * @return the new MimeMessage object - * @since JavaMail 1.4 - */ - protected MimeMessage createMimeMessage(Session session) - throws MessagingException { - return new MimeMessage(session); - } -} diff --git a/src/main/java/javax/mail/internet/MimeMultipart.java b/src/main/java/javax/mail/internet/MimeMultipart.java deleted file mode 100644 index f253ff1a..00000000 --- a/src/main/java/javax/mail/internet/MimeMultipart.java +++ /dev/null @@ -1,1016 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import javax.activation.*; -import java.util.*; -import java.io.*; -import com.sun.mail.util.LineOutputStream; -import com.sun.mail.util.LineInputStream; -import com.sun.mail.util.ASCIIUtility; -import com.sun.mail.util.PropUtil; - -/** - * The MimeMultipart class is an implementation of the abstract Multipart - * class that uses MIME conventions for the multipart data.

      - * - * A MimeMultipart is obtained from a MimePart whose primary type - * is "multipart" (by invoking the part's getContent() method) - * or it can be created by a client as part of creating a new MimeMessage.

      - * - * The default multipart subtype is "mixed". The other multipart - * subtypes, such as "alternative", "related", and so on, can be - * implemented as subclasses of MimeMultipart with additional methods - * to implement the additional semantics of that type of multipart - * content. The intent is that service providers, mail JavaBean writers - * and mail clients will write many such subclasses and their Command - * Beans, and will install them into the JavaBeans Activation - * Framework, so that any JavaMail implementation and its clients can - * transparently find and use these classes. Thus, a MIME multipart - * handler is treated just like any other type handler, thereby - * decoupling the process of providing multipart handlers from the - * JavaMail API. Lacking these additional MimeMultipart subclasses, - * all subtypes of MIME multipart data appear as MimeMultipart objects.

      - * - * An application can directly construct a MIME multipart object of any - * subtype by using the MimeMultipart(String subtype) - * constructor. For example, to create a "multipart/alternative" object, - * use new MimeMultipart("alternative").

      - * - * The mail.mime.multipart.ignoremissingendboundary - * property may be set to false to cause a - * MessagingException to be thrown if the multipart - * data does not end with the required end boundary line. If this - * property is set to true or not set, missing end - * boundaries are not considered an error and the final body part - * ends at the end of the data.

      - * - * The mail.mime.multipart.ignoremissingboundaryparameter - * System property may be set to false to cause a - * MessagingException to be thrown if the Content-Type - * of the MimeMultipart does not include a boundary parameter. - * If this property is set to true or not set, the multipart - * parsing code will look for a line that looks like a bounary line and - * use that as the boundary separating the parts.

      - * - * The mail.mime.multipart.ignoreexistingboundaryparameter - * System property may be set to true to cause any boundary - * to be ignored and instead search for a boundary line in the message - * as with mail.mime.multipart.ignoremissingboundaryparameter.

      - * - * Normally, when writing out a MimeMultipart that contains no body - * parts, or when trying to parse a multipart message with no body parts, - * a MessagingException is thrown. The MIME spec does not allow - * multipart content with no body parts. The - * mail.mime.multipart.allowempty System property may be set to - * true to override this behavior. - * When writing out such a MimeMultipart, a single empty part will be - * included. When reading such a multipart, a MimeMultipart will be created - * with no body parts. - * - * @author John Mani - * @author Bill Shannon - * @author Max Spivak - */ - -public class MimeMultipart extends Multipart { - - /** - * The DataSource supplying our InputStream. - */ - protected DataSource ds = null; - - /** - * Have we parsed the data from our InputStream yet? - * Defaults to true; set to false when our constructor is - * given a DataSource with an InputStream that we need to - * parse. - */ - protected boolean parsed = true; - - /** - * Have we seen the final bounary line? - * - * @since JavaMail 1.5 - */ - protected boolean complete = true; - - /** - * The MIME multipart preamble text, the text that - * occurs before the first boundary line. - * - * @since JavaMail 1.5 - */ - protected String preamble = null; - - /** - * Flag corresponding to the "mail.mime.multipart.ignoremissingendboundary" - * property, set in the {@link #initializeProperties} method called from - * constructors and the parse method. - * - * @since JavaMail 1.5 - */ - protected boolean ignoreMissingEndBoundary = true; - - /** - * Flag corresponding to the - * "mail.mime.multipart.ignoremissingboundaryparameter" - * property, set in the {@link #initializeProperties} method called from - * constructors and the parse method. - * - * @since JavaMail 1.5 - */ - protected boolean ignoreMissingBoundaryParameter = true; - - /** - * Flag corresponding to the - * "mail.mime.multipart.ignoreexistingboundaryparameter" - * property, set in the {@link #initializeProperties} method called from - * constructors and the parse method. - * - * @since JavaMail 1.5 - */ - protected boolean ignoreExistingBoundaryParameter = false; - - /** - * Flag corresponding to the "mail.mime.multipart.allowempty" - * property, set in the {@link #initializeProperties} method called from - * constructors and the parse method. - * - * @since JavaMail 1.5 - */ - protected boolean allowEmpty = false; - - /** - * Default constructor. An empty MimeMultipart object - * is created. Its content type is set to "multipart/mixed". - * A unique boundary string is generated and this string is - * setup as the "boundary" parameter for the - * contentType field.

      - * - * MimeBodyParts may be added later. - */ - public MimeMultipart() { - this("mixed"); - } - - /** - * Construct a MimeMultipart object of the given subtype. - * A unique boundary string is generated and this string is - * setup as the "boundary" parameter for the - * contentType field. - * Calls the {@link #initializeProperties} method.

      - * - * MimeBodyParts may be added later. - */ - public MimeMultipart(String subtype) { - super(); - /* - * Compute a boundary string. - */ - String boundary = UniqueValue.getUniqueBoundaryValue(); - ContentType cType = new ContentType("multipart", subtype, null); - cType.setParameter("boundary", boundary); - contentType = cType.toString(); - initializeProperties(); - } - - /** - * Construct a MimeMultipart object of the default "mixed" subtype, - * and with the given body parts. More body parts may be added later. - * - * @since JavaMail 1.5 - */ - public MimeMultipart(BodyPart... parts) throws MessagingException { - this(); - for (BodyPart bp : parts) - super.addBodyPart(bp); - } - - /** - * Construct a MimeMultipart object of the given subtype - * and with the given body parts. More body parts may be added later. - * - * @since JavaMail 1.5 - */ - public MimeMultipart(String subtype, BodyPart... parts) - throws MessagingException { - this(subtype); - for (BodyPart bp : parts) - super.addBodyPart(bp); - } - - /** - * Constructs a MimeMultipart object and its bodyparts from the - * given DataSource.

      - * - * This constructor handles as a special case the situation where the - * given DataSource is a MultipartDataSource object. In this case, this - * method just invokes the superclass (i.e., Multipart) constructor - * that takes a MultipartDataSource object.

      - * - * Otherwise, the DataSource is assumed to provide a MIME multipart - * byte stream. The parsed flag is set to false. When - * the data for the body parts are needed, the parser extracts the - * "boundary" parameter from the content type of this DataSource, - * skips the 'preamble' and reads bytes till the terminating - * boundary and creates MimeBodyParts for each part of the stream. - * - * @param ds DataSource, can be a MultipartDataSource - */ - public MimeMultipart(DataSource ds) throws MessagingException { - super(); - - if (ds instanceof MessageAware) { - MessageContext mc = ((MessageAware)ds).getMessageContext(); - setParent(mc.getPart()); - } - - if (ds instanceof MultipartDataSource) { - // ask super to do this for us. - setMultipartDataSource((MultipartDataSource)ds); - return; - } - - // 'ds' was not a MultipartDataSource, we have - // to parse this ourself. - parsed = false; - this.ds = ds; - contentType = ds.getContentType(); - } - - /** - * Initialize flags that control parsing behavior, - * based on System properties described above in - * the class documentation. - * - * @since JavaMail 1.5 - */ - protected void initializeProperties() { - // read properties that control parsing - - // default to true - ignoreMissingEndBoundary = PropUtil.getBooleanSystemProperty( - "mail.mime.multipart.ignoremissingendboundary", true); - // default to true - ignoreMissingBoundaryParameter = PropUtil.getBooleanSystemProperty( - "mail.mime.multipart.ignoremissingboundaryparameter", true); - // default to false - ignoreExistingBoundaryParameter = PropUtil.getBooleanSystemProperty( - "mail.mime.multipart.ignoreexistingboundaryparameter", false); - // default to false - allowEmpty = PropUtil.getBooleanSystemProperty( - "mail.mime.multipart.allowempty", false); - } - - /** - * Set the subtype. This method should be invoked only on a new - * MimeMultipart object created by the client. The default subtype - * of such a multipart object is "mixed".

      - * - * @param subtype Subtype - */ - public synchronized void setSubType(String subtype) - throws MessagingException { - ContentType cType = new ContentType(contentType); - cType.setSubType(subtype); - contentType = cType.toString(); - } - - /** - * Return the number of enclosed BodyPart objects. - * - * @return number of parts - */ - public synchronized int getCount() throws MessagingException { - parse(); - return super.getCount(); - } - - /** - * Get the specified BodyPart. BodyParts are numbered starting at 0. - * - * @param index the index of the desired BodyPart - * @return the Part - * @exception MessagingException if no such BodyPart exists - */ - public synchronized BodyPart getBodyPart(int index) - throws MessagingException { - parse(); - return super.getBodyPart(index); - } - - /** - * Get the MimeBodyPart referred to by the given ContentID (CID). - * Returns null if the part is not found. - * - * @param CID the ContentID of the desired part - * @return the Part - */ - public synchronized BodyPart getBodyPart(String CID) - throws MessagingException { - parse(); - - int count = getCount(); - for (int i = 0; i < count; i++) { - MimeBodyPart part = (MimeBodyPart)getBodyPart(i); - String s = part.getContentID(); - if (s != null && s.equals(CID)) - return part; - } - return null; - } - - /** - * Remove the specified part from the multipart message. - * Shifts all the parts after the removed part down one. - * - * @param part The part to remove - * @return true if part removed, false otherwise - * @exception MessagingException if no such Part exists - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public boolean removeBodyPart(BodyPart part) throws MessagingException { - parse(); - return super.removeBodyPart(part); - } - - /** - * Remove the part at specified location (starting from 0). - * Shifts all the parts after the removed part down one. - * - * @param index Index of the part to remove - * @exception MessagingException - * @exception IndexOutOfBoundsException if the given index - * is out of range. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public void removeBodyPart(int index) throws MessagingException { - parse(); - super.removeBodyPart(index); - } - - /** - * Adds a Part to the multipart. The BodyPart is appended to - * the list of existing Parts. - * - * @param part The Part to be appended - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized void addBodyPart(BodyPart part) - throws MessagingException { - parse(); - super.addBodyPart(part); - } - - /** - * Adds a BodyPart at position index. - * If index is not the last one in the list, - * the subsequent parts are shifted up. If index - * is larger than the number of parts present, the - * BodyPart is appended to the end. - * - * @param part The BodyPart to be inserted - * @param index Location where to insert the part - * @exception MessagingException - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * of existing values - */ - public synchronized void addBodyPart(BodyPart part, int index) - throws MessagingException { - parse(); - super.addBodyPart(part, index); - } - - /** - * Return true if the final boundary line for this - * multipart was seen. When parsing multipart content, - * this class will (by default) terminate parsing with - * no error if the end of input is reached before seeing - * the final multipart boundary line. In such a case, - * this method will return false. (If the System property - * "mail.mime.multipart.ignoremissingendboundary" is set to - * false, parsing such a message will instead throw a - * MessagingException.) - * - * @return true if the final boundary line was seen - * @since JavaMail 1.4 - */ - public synchronized boolean isComplete() throws MessagingException { - parse(); - return complete; - } - - /** - * Get the preamble text, if any, that appears before the - * first body part of this multipart. Some protocols, - * such as IMAP, will not allow access to the preamble text. - * - * @return the preamble text, or null if no preamble - * @since JavaMail 1.4 - */ - public synchronized String getPreamble() throws MessagingException { - parse(); - return preamble; - } - - /** - * Set the preamble text to be included before the first - * body part. Applications should generally not include - * any preamble text. In some cases it may be helpful to - * include preamble text with instructions for users of - * pre-MIME software. The preamble text should be complete - * lines, including newlines. - * - * @param preamble the preamble text - * @since JavaMail 1.4 - */ - public synchronized void setPreamble(String preamble) - throws MessagingException { - this.preamble = preamble; - } - - /** - * Update headers. The default implementation here just - * calls the updateHeaders method on each of its - * children BodyParts.

      - * - * Note that the boundary parameter is already set up when - * a new and empty MimeMultipart object is created.

      - * - * This method is called when the saveChanges - * method is invoked on the Message object containing this - * Multipart. This is typically done as part of the Message - * send process, however note that a client is free to call - * it any number of times. So if the header updating process is - * expensive for a specific MimeMultipart subclass, then it - * might itself want to track whether its internal state actually - * did change, and do the header updating only if necessary. - */ - protected synchronized void updateHeaders() throws MessagingException { - parse(); - for (int i = 0; i < parts.size(); i++) - ((MimeBodyPart)parts.elementAt(i)).updateHeaders(); - } - - /** - * Iterates through all the parts and outputs each MIME part - * separated by a boundary. - */ - public synchronized void writeTo(OutputStream os) - throws IOException, MessagingException { - parse(); - - String boundary = "--" + - (new ContentType(contentType)).getParameter("boundary"); - LineOutputStream los = new LineOutputStream(os); - - // if there's a preamble, write it out - if (preamble != null) { - byte[] pb = ASCIIUtility.getBytes(preamble); - los.write(pb); - // make sure it ends with a newline - if (pb.length > 0 && - !(pb[pb.length-1] == '\r' || pb[pb.length-1] == '\n')) { - los.writeln(); - } - // XXX - could force a blank line before start boundary - } - - if (parts.size() == 0) { - if (allowEmpty) { - // write out a single empty body part - los.writeln(boundary); // put out boundary - los.writeln(); // put out empty line - } else { - throw new MessagingException("Empty multipart: " + contentType); - } - } else { - for (int i = 0; i < parts.size(); i++) { - los.writeln(boundary); // put out boundary - ((MimeBodyPart)parts.elementAt(i)).writeTo(os); - los.writeln(); // put out empty line - } - } - - // put out last boundary - los.writeln(boundary + "--"); - } - - /** - * Parse the InputStream from our DataSource, constructing the - * appropriate MimeBodyParts. The parsed flag is - * set to true, and if true on entry nothing is done. This - * method is called by all other methods that need data for - * the body parts, to make sure the data has been parsed. - * The {@link #initializeProperties} method is called before - * parsing the data. - * - * @since JavaMail 1.2 - */ - protected synchronized void parse() throws MessagingException { - if (parsed) - return; - - initializeProperties(); - - InputStream in = null; - SharedInputStream sin = null; - long start = 0, end = 0; - - try { - in = ds.getInputStream(); - if (!(in instanceof ByteArrayInputStream) && - !(in instanceof BufferedInputStream) && - !(in instanceof SharedInputStream)) - in = new BufferedInputStream(in); - } catch (Exception ex) { - throw new MessagingException("No inputstream from datasource", ex); - } - if (in instanceof SharedInputStream) - sin = (SharedInputStream)in; - - ContentType cType = new ContentType(contentType); - String boundary = null; - if (!ignoreExistingBoundaryParameter) { - String bp = cType.getParameter("boundary"); - if (bp != null) - boundary = "--" + bp; - } - if (boundary == null && !ignoreMissingBoundaryParameter && - !ignoreExistingBoundaryParameter) - throw new MessagingException("Missing boundary parameter"); - - try { - // Skip and save the preamble - LineInputStream lin = new LineInputStream(in); - StringBuffer preamblesb = null; - String line; - String lineSeparator = null; - while ((line = lin.readLine()) != null) { - /* - * Strip trailing whitespace. Can't use trim method - * because it's too aggressive. Some bogus MIME - * messages will include control characters in the - * boundary string. - */ - int i; - for (i = line.length() - 1; i >= 0; i--) { - char c = line.charAt(i); - if (!(c == ' ' || c == '\t')) - break; - } - line = line.substring(0, i + 1); - if (boundary != null) { - if (line.equals(boundary)) - break; - if (line.length() == boundary.length() + 2 && - line.startsWith(boundary) && line.endsWith("--")) { - line = null; // signal end of multipart - break; - } - } else { - /* - * Boundary hasn't been defined, does this line - * look like a boundary? If so, assume it is - * the boundary and save it. - */ - if (line.length() > 2 && line.startsWith("--")) { - if (line.length() > 4 && allDashes(line)) { - /* - * The first boundary-like line we find is - * probably *not* the end-of-multipart boundary - * line. More likely it's a line full of dashes - * in the preamble text. Just keep reading. - */ - } else { - boundary = line; - break; - } - } - } - - // save the preamble after skipping blank lines - if (line.length() > 0) { - // if we haven't figured out what the line separator - // is, do it now - if (lineSeparator == null) { - try { - lineSeparator = - System.getProperty("line.separator", "\n"); - } catch (SecurityException ex) { - lineSeparator = "\n"; - } - } - // accumulate the preamble - if (preamblesb == null) - preamblesb = new StringBuffer(line.length() + 2); - preamblesb.append(line).append(lineSeparator); - } - } - - if (preamblesb != null) - preamble = preamblesb.toString(); - - if (line == null) { - if (allowEmpty) - return; - else - throw new MessagingException("Missing start boundary"); - } - - // save individual boundary bytes for comparison later - byte[] bndbytes = ASCIIUtility.getBytes(boundary); - int bl = bndbytes.length; - - /* - * Compile Boyer-Moore parsing tables. - */ - - // initialize Bad Character Shift table - int[] bcs = new int[256]; - for (int i = 0; i < bl; i++) - bcs[bndbytes[i] & 0xff] = i + 1; - - // initialize Good Suffix Shift table - int[] gss = new int[bl]; - NEXT: - for (int i = bl; i > 0; i--) { - int j; // the beginning index of the suffix being considered - for (j = bl - 1; j >= i; j--) { - // Testing for good suffix - if (bndbytes[j] == bndbytes[j - i]) { - // bndbytes[j..len] is a good suffix - gss[j - 1] = i; - } else { - // No match. The array has already been - // filled up with correct values before. - continue NEXT; - } - } - while (j > 0) - gss[--j] = i; - } - gss[bl - 1] = 1; - - /* - * Read and process body parts until we see the - * terminating boundary line (or EOF). - */ - boolean done = false; - getparts: - while (!done) { - InternetHeaders headers = null; - if (sin != null) { - start = sin.getPosition(); - // skip headers - while ((line = lin.readLine()) != null && line.length() > 0) - ; - if (line == null) { - if (!ignoreMissingEndBoundary) - throw new MessagingException( - "missing multipart end boundary"); - // assume there's just a missing end boundary - complete = false; - break getparts; - } - } else { - // collect the headers for this body part - headers = createInternetHeaders(in); - } - - if (!in.markSupported()) - throw new MessagingException("Stream doesn't support mark"); - - ByteArrayOutputStream buf = null; - // if we don't have a shared input stream, we copy the data - if (sin == null) - buf = new ByteArrayOutputStream(); - else - end = sin.getPosition(); - int b; - - /* - * These buffers contain the bytes we're checking - * for a match. inbuf is the current buffer and - * previnbuf is the previous buffer. We need the - * previous buffer to check that we're preceeded - * by an EOL. - */ - // XXX - a smarter algorithm would use a sliding window - // over a larger buffer - byte[] inbuf = new byte[bl]; - byte[] previnbuf = new byte[bl]; - int inSize = 0; // number of valid bytes in inbuf - int prevSize = 0; // number of valid bytes in previnbuf - int eolLen; - boolean first = true; - - /* - * Read and save the content bytes in buf. - */ - for (;;) { - in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP - eolLen = 0; - inSize = readFully(in, inbuf, 0, bl); - if (inSize < bl) { - // hit EOF - if (!ignoreMissingEndBoundary) - throw new MessagingException( - "missing multipart end boundary"); - if (sin != null) - end = sin.getPosition(); - complete = false; - done = true; - break; - } - // check whether inbuf contains a boundary string - int i; - for (i = bl - 1; i >= 0; i--) { - if (inbuf[i] != bndbytes[i]) - break; - } - if (i < 0) { // matched all bytes - eolLen = 0; - if (!first) { - // working backwards, find out if we were preceeded - // by an EOL, and if so find its length - b = previnbuf[prevSize - 1]; - if (b == '\r' || b == '\n') { - eolLen = 1; - if (b == '\n' && prevSize >= 2) { - b = previnbuf[prevSize - 2]; - if (b == '\r') - eolLen = 2; - } - } - } - if (first || eolLen > 0) { // yes, preceed by EOL - if (sin != null) { - // update "end", in case this really is - // a valid boundary - end = sin.getPosition() - bl - eolLen; - } - // matched the boundary, check for last boundary - int b2 = in.read(); - if (b2 == '-') { - if (in.read() == '-') { - complete = true; - done = true; - break; // ignore trailing text - } - } - // skip linear whitespace - while (b2 == ' ' || b2 == '\t') - b2 = in.read(); - // check for end of line - if (b2 == '\n') - break; // got it! break out of the loop - if (b2 == '\r') { - in.mark(1); - if (in.read() != '\n') - in.reset(); - break; // got it! break out of the loop - } - } - i = 0; - } - - /* - * Get here if boundary didn't match, - * wasn't preceeded by EOL, or wasn't - * followed by whitespace or EOL. - */ - - // compute how many bytes we can skip - int skip = Math.max(i + 1 - bcs[inbuf[i] & 0x7f], gss[i]); - // want to keep at least two characters - if (skip < 2) { - // only skipping one byte, save one byte - // from previous buffer as well - // first, write out bytes we're done with - if (sin == null && prevSize > 1) - buf.write(previnbuf, 0, prevSize - 1); - in.reset(); - skipFully(in, 1); - if (prevSize >= 1) { // is there a byte to save? - // yes, save one from previous and one from current - previnbuf[0] = previnbuf[prevSize - 1]; - previnbuf[1] = inbuf[0]; - prevSize = 2; - } else { - // no previous bytes to save, can only save current - previnbuf[0] = inbuf[0]; - prevSize = 1; - } - } else { - // first, write out data from previous buffer before - // we dump it - if (prevSize > 0 && sin == null) - buf.write(previnbuf, 0, prevSize); - // all the bytes we're skipping are saved in previnbuf - prevSize = skip; - in.reset(); - skipFully(in, prevSize); - // swap buffers - byte[] tmp = inbuf; - inbuf = previnbuf; - previnbuf = tmp; - } - first = false; - } - - /* - * Create a MimeBody element to represent this body part. - */ - MimeBodyPart part; - if (sin != null) { - part = createMimeBodyPartIs(sin.newStream(start, end)); - } else { - // write out data from previous buffer, not including EOL - if (prevSize - eolLen > 0) - buf.write(previnbuf, 0, prevSize - eolLen); - // if we didn't find a trailing boundary, - // the current buffer has data we need too - if (!complete && inSize > 0) - buf.write(inbuf, 0, inSize); - part = createMimeBodyPart(headers, buf.toByteArray()); - } - super.addBodyPart(part); - } - } catch (IOException ioex) { - throw new MessagingException("IO Error", ioex); - } finally { - try { - in.close(); - } catch (IOException cex) { - // ignore - } - } - - parsed = true; - } - - /** - * Is the string all dashes ('-')? - */ - private static boolean allDashes(String s) { - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) != '-') - return false; - } - return true; - } - - /** - * Read data from the input stream to fill the buffer starting - * at the specified offset with the specified number of bytes. - * If len is zero, return zero. If at EOF, return -1. Otherwise, - * return the number of bytes read. Call the read method on the - * input stream as many times as necessary to read len bytes. - * - * @param in InputStream to read from - * @param buf buffer to read into - * @param off offset in the buffer for first byte - * @param len number of bytes to read - * @return -1 on EOF, otherwise number of bytes read - * @exception IOException on I/O errors - */ - private static int readFully(InputStream in, byte[] buf, int off, int len) - throws IOException { - if (len == 0) - return 0; - int total = 0; - while (len > 0) { - int bsize = in.read(buf, off, len); - if (bsize <= 0) // should never be zero - break; - off += bsize; - total += bsize; - len -= bsize; - } - return total > 0 ? total : -1; - } - - /** - * Skip the specified number of bytes, repeatedly calling - * the skip method as necessary. - */ - private void skipFully(InputStream in, long offset) throws IOException { - while (offset > 0) { - long cur = in.skip(offset); - if (cur <= 0) - throw new EOFException("can't skip"); - offset -= cur; - } - } - - /** - * Create and return an InternetHeaders object that loads the - * headers from the given InputStream. Subclasses can override - * this method to return a subclass of InternetHeaders, if - * necessary. This implementation simply constructs and returns - * an InternetHeaders object. - * - * @param is the InputStream to read the headers from - * @exception MessagingException - * @since JavaMail 1.2 - */ - protected InternetHeaders createInternetHeaders(InputStream is) - throws MessagingException { - return new InternetHeaders(is); - } - - /** - * Create and return a MimeBodyPart object to represent a - * body part parsed from the InputStream. Subclasses can override - * this method to return a subclass of MimeBodyPart, if - * necessary. This implementation simply constructs and returns - * a MimeBodyPart object. - * - * @param headers the headers for the body part - * @param content the content of the body part - * @exception MessagingException - * @since JavaMail 1.2 - */ - protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, - byte[] content) throws MessagingException { - return new MimeBodyPart(headers, content); - } - - /** - * Create and return a MimeBodyPart object to represent a - * body part parsed from the InputStream. Subclasses can override - * this method to return a subclass of MimeBodyPart, if - * necessary. This implementation simply constructs and returns - * a MimeBodyPart object. - * - * @param is InputStream containing the body part - * @exception MessagingException - * @since JavaMail 1.2 - */ - protected MimeBodyPart createMimeBodyPart(InputStream is) - throws MessagingException { - return new MimeBodyPart(is); - } - - private MimeBodyPart createMimeBodyPartIs(InputStream is) - throws MessagingException { - try { - return createMimeBodyPart(is); - } finally { - try { - is.close(); - } catch (IOException ex) { - // ignore it - } - } - } -} diff --git a/src/main/java/javax/mail/internet/MimePart.java b/src/main/java/javax/mail/internet/MimePart.java deleted file mode 100644 index f2b4af30..00000000 --- a/src/main/java/javax/mail/internet/MimePart.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import java.io.*; -import java.util.Enumeration; - -/** - * The MimePart interface models an Entity as defined - * by MIME (RFC2045, Section 2.4).

      - * - * MimePart extends the Part interface to add additional RFC822 and MIME - * specific semantics and attributes. It provides the base interface for - * the MimeMessage and MimeBodyPart classes - * - *


      A note on RFC822 and MIME headers

      - * - * RFC822 and MIME header fields must contain only - * US-ASCII characters. If a header contains non US-ASCII characters, - * it must be encoded as per the rules in RFC 2047. The MimeUtility - * class provided in this package can be used to to achieve this. - * Callers of the setHeader, addHeader, and - * addHeaderLine methods are responsible for enforcing - * the MIME requirements for the specified headers. In addition, these - * header fields must be folded (wrapped) before being sent if they - * exceed the line length limitation for the transport (1000 bytes for - * SMTP). Received headers may have been folded. The application is - * responsible for folding and unfolding headers as appropriate.

      - * - * @see MimeUtility - * @see javax.mail.Part - * @author John Mani - */ - -public interface MimePart extends Part { - - /** - * Get the values of all header fields available for this header, - * returned as a single String, with the values separated by the - * delimiter. If the delimiter is null, only the - * first value is returned. - * - * @param name the name of this header - * @param delimiter delimiter between fields in returned string - * @return the value fields for all headers with - * this name - * @exception MessagingException - */ - public String getHeader(String name, String delimiter) - throws MessagingException; - - /** - * Add a raw RFC822 header-line. - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void addHeaderLine(String line) throws MessagingException; - - /** - * Get all header lines as an Enumeration of Strings. A Header - * line is a raw RFC822 header-line, containing both the "name" - * and "value" field. - */ - public Enumeration getAllHeaderLines() throws MessagingException; - - /** - * Get matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC822 header-line, containing both - * the "name" and "value" field. - */ - public Enumeration getMatchingHeaderLines(String[] names) - throws MessagingException; - - /** - * Get non-matching header lines as an Enumeration of Strings. - * A Header line is a raw RFC822 header-line, containing both - * the "name" and "value" field. - */ - public Enumeration getNonMatchingHeaderLines(String[] names) - throws MessagingException; - - /** - * Get the transfer encoding of this part. - * - * @return content-transfer-encoding - * @exception MessagingException - */ - public String getEncoding() throws MessagingException; - - /** - * Get the Content-ID of this part. Returns null if none present. - * - * @return content-ID - */ - public String getContentID() throws MessagingException; - - /** - * Get the Content-MD5 digest of this part. Returns null if - * none present. - * - * @return content-MD5 - */ - public String getContentMD5() throws MessagingException; - - /** - * Set the Content-MD5 of this part. - * - * @param md5 the MD5 value - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void setContentMD5(String md5) throws MessagingException; - - /** - * Get the language tags specified in the Content-Language header - * of this MimePart. The Content-Language header is defined by - * RFC 1766. Returns null if this header is not - * available. - */ - public String[] getContentLanguage() throws MessagingException; - - /** - * Set the Content-Language header of this MimePart. The - * Content-Language header is defined by RFC1766. - * - * @param languages array of language tags - * @exception IllegalWriteException if the underlying - * implementation does not support modification - * @exception IllegalStateException if this Part is - * obtained from a READ_ONLY folder - */ - public void setContentLanguage(String[] languages) - throws MessagingException; - - /** - * Convenience method that sets the given String as this - * part's content, with a MIME type of "text/plain". If the - * string contains non US-ASCII characters. it will be encoded - * using the platform's default charset. The charset is also - * used to set the "charset" parameter.

      - * - * Note that there may be a performance penalty if - * text is large, since this method may have - * to scan all the characters to determine what charset to - * use.

      - * - * If the charset is already known, use the - * setText method that takes the charset parameter. - * - * @param text the text content to set - * @exception MessagingException if an error occurs - * @see #setText(String text, String charset) - */ - public void setText(String text) throws MessagingException; - - /** - * Convenience method that sets the given String as this part's - * content, with a MIME type of "text/plain" and the specified - * charset. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @exception MessagingException if an error occurs - */ - public void setText(String text, String charset) - throws MessagingException; - - /** - * Convenience method that sets the given String as this part's - * content, with a primary MIME type of "text" and the specified - * MIME subtype. The given Unicode string will be charset-encoded - * using the specified charset. The charset is also used to set - * the "charset" parameter. - * - * @param text the text content to set - * @param charset the charset to use for the text - * @param subtype the MIME subtype to use (e.g., "html") - * @exception MessagingException if an error occurs - * @since JavaMail 1.4 - */ - public void setText(String text, String charset, String subtype) - throws MessagingException; -} diff --git a/src/main/java/javax/mail/internet/MimePartDataSource.java b/src/main/java/javax/mail/internet/MimePartDataSource.java deleted file mode 100644 index 7eea01c0..00000000 --- a/src/main/java/javax/mail/internet/MimePartDataSource.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.*; -import javax.activation.*; -import java.io.*; -import java.net.UnknownServiceException; -import com.sun.mail.util.PropUtil; -import com.sun.mail.util.FolderClosedIOException; - -/** - * A utility class that implements a DataSource out of - * a MimePart. This class is primarily meant for service providers. - * - * @see javax.mail.internet.MimePart - * @see javax.activation.DataSource - * @author John Mani - */ - -public class MimePartDataSource implements DataSource, MessageAware { - /** - * The MimePart that provides the data for this DataSource. - * - * @since JavaMail 1.4 - */ - protected MimePart part; - - private MessageContext context; - - /** - * Constructor, that constructs a DataSource from a MimePart. - */ - public MimePartDataSource(MimePart part) { - this.part = part; - } - - /** - * Returns an input stream from this MimePart.

      - * - * This method applies the appropriate transfer-decoding, based - * on the Content-Transfer-Encoding attribute of this MimePart. - * Thus the returned input stream is a decoded stream of bytes.

      - * - * This implementation obtains the raw content from the Part - * using the getContentStream() method and decodes - * it using the MimeUtility.decode() method. - * - * @see javax.mail.internet.MimeMessage#getContentStream - * @see javax.mail.internet.MimeBodyPart#getContentStream - * @see javax.mail.internet.MimeUtility#decode - * @return decoded input stream - */ - public InputStream getInputStream() throws IOException { - InputStream is; - - try { - if (part instanceof MimeBodyPart) - is = ((MimeBodyPart)part).getContentStream(); - else if (part instanceof MimeMessage) - is = ((MimeMessage)part).getContentStream(); - else - throw new MessagingException("Unknown part"); - - String encoding = - MimeBodyPart.restrictEncoding(part, part.getEncoding()); - if (encoding != null) - return MimeUtility.decode(is, encoding); - else - return is; - } catch (FolderClosedException fex) { - throw new FolderClosedIOException(fex.getFolder(), - fex.getMessage()); - } catch (MessagingException mex) { - throw new IOException(mex.getMessage()); - } - } - - /** - * DataSource method to return an output stream.

      - * - * This implementation throws the UnknownServiceException. - */ - public OutputStream getOutputStream() throws IOException { - throw new UnknownServiceException("Writing not supported"); - } - - /** - * Returns the content-type of this DataSource.

      - * - * This implementation just invokes the getContentType - * method on the MimePart. - */ - public String getContentType() { - try { - return part.getContentType(); - } catch (MessagingException mex) { - // would like to be able to reflect the exception to the - // application, but since we can't do that we return a - // generic "unknown" value here and hope for another - // exception later. - return "application/octet-stream"; - } - } - - /** - * DataSource method to return a name.

      - * - * This implementation just returns an empty string. - */ - public String getName() { - try { - if (part instanceof MimeBodyPart) - return ((MimeBodyPart)part).getFileName(); - } catch (MessagingException mex) { - // ignore it - } - return ""; - } - - /** - * Return the MessageContext for the current part. - * @since JavaMail 1.1 - */ - public synchronized MessageContext getMessageContext() { - if (context == null) - context = new MessageContext(part); - return context; - } -} diff --git a/src/main/java/javax/mail/internet/MimeUtility.java b/src/main/java/javax/mail/internet/MimeUtility.java deleted file mode 100644 index 8d811f38..00000000 --- a/src/main/java/javax/mail/internet/MimeUtility.java +++ /dev/null @@ -1,1652 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.MessagingException; -import javax.mail.EncodingAware; -import javax.activation.*; -import java.util.*; -import java.io.*; -import com.sun.mail.util.*; - -/** - * This is a utility class that provides various MIME related - * functionality.

      - * - * There are a set of methods to encode and decode MIME headers as - * per RFC 2047. Note that, in general, these methods are - * not needed when using methods such as - * setSubject and setRecipients; JavaMail - * will automatically encode and decode data when using these "higher - * level" methods. The methods below are only needed when maniuplating - * raw MIME headers using setHeader and getHeader - * methods. A brief description on handling such headers is given below:

      - * - * RFC 822 mail headers must contain only US-ASCII - * characters. Headers that contain non US-ASCII characters must be - * encoded so that they contain only US-ASCII characters. Basically, - * this process involves using either BASE64 or QP to encode certain - * characters. RFC 2047 describes this in detail.

      - * - * In Java, Strings contain (16 bit) Unicode characters. ASCII is a - * subset of Unicode (and occupies the range 0 - 127). A String - * that contains only ASCII characters is already mail-safe. If the - * String contains non US-ASCII characters, it must be encoded. An - * additional complexity in this step is that since Unicode is not - * yet a widely used charset, one might want to first charset-encode - * the String into another charset and then do the transfer-encoding. - *

      - * Note that to get the actual bytes of a mail-safe String (say, - * for sending over SMTP), one must do - *

      - *
      - *	byte[] bytes = string.getBytes("iso-8859-1");	
      - *
      - * 

      - * - * The setHeader and addHeader methods - * on MimeMessage and MimeBodyPart assume that the given header values - * are Unicode strings that contain only US-ASCII characters. Hence - * the callers of those methods must insure that the values they pass - * do not contain non US-ASCII characters. The methods in this class - * help do this.

      - * - * The getHeader family of methods on MimeMessage and - * MimeBodyPart return the raw header value. These might be encoded - * as per RFC 2047, and if so, must be decoded into Unicode Strings. - * The methods in this class help to do this.

      - * - * Several System properties control strict conformance to the MIME - * spec. Note that these are not session properties but must be set - * globally as System properties.

      - * - * The mail.mime.decodetext.strict property controls - * decoding of MIME encoded words. The MIME spec requires that encoded - * words start at the beginning of a whitespace separated word. Some - * mailers incorrectly include encoded words in the middle of a word. - * If the mail.mime.decodetext.strict System property is - * set to "false", an attempt will be made to decode these - * illegal encoded words. The default is true.

      - * - * The mail.mime.encodeeol.strict property controls the - * choice of Content-Transfer-Encoding for MIME parts that are not of - * type "text". Often such parts will contain textual data for which - * an encoding that allows normal end of line conventions is appropriate. - * In rare cases, such a part will appear to contain entirely textual - * data, but will require an encoding that preserves CR and LF characters - * without change. If the mail.mime.encodeeol.strict - * System property is set to "true", such an encoding will - * be used when necessary. The default is false.

      - * - * In addition, the mail.mime.charset System property can - * be used to specify the default MIME charset to use for encoded words - * and text parts that don't otherwise specify a charset. Normally, the - * default MIME charset is derived from the default Java charset, as - * specified in the file.encoding System property. Most - * applications will have no need to explicitly set the default MIME - * charset. In cases where the default MIME charset to be used for - * mail messages is different than the charset used for files stored on - * the system, this property should be set.

      - * - * The current implementation also supports the following System property. - *

      - * The mail.mime.ignoreunknownencoding property controls - * whether unknown values in the Content-Transfer-Encoding - * header, as passed to the decode method, cause an exception. - * If set to "true", unknown values are ignored and 8bit - * encoding is assumed. Otherwise, unknown values cause a MessagingException - * to be thrown. - * - * @author John Mani - * @author Bill Shannon - */ - -public class MimeUtility { - - // This class cannot be instantiated - private MimeUtility() { } - - public static final int ALL = -1; - - // cached map of whether a charset is compatible with ASCII - // Map - private static final Map nonAsciiCharsetMap = new HashMap(); - - private static final boolean decodeStrict = - PropUtil.getBooleanSystemProperty("mail.mime.decodetext.strict", true); - private static final boolean encodeEolStrict = - PropUtil.getBooleanSystemProperty("mail.mime.encodeeol.strict", false); - private static final boolean ignoreUnknownEncoding = - PropUtil.getBooleanSystemProperty( - "mail.mime.ignoreunknownencoding", false); - /* - * The following two properties allow disabling the fold() - * and unfold() methods and reverting to the previous behavior. - * They should never need to be changed and are here only because - * of my paranoid concern with compatibility. - */ - private static final boolean foldEncodedWords = - PropUtil.getBooleanSystemProperty("mail.mime.foldencodedwords", false); - private static final boolean foldText = - PropUtil.getBooleanSystemProperty("mail.mime.foldtext", true); - - - /** - * Get the Content-Transfer-Encoding that should be applied - * to the input stream of this DataSource, to make it mail-safe.

      - * - * The algorithm used here is:
      - *

        - *
      • - * If the DataSource implements {@link EncodingAware}, ask it - * what encoding to use. If it returns non-null, return that value. - *
      • - * If the primary type of this datasource is "text" and if all - * the bytes in its input stream are US-ASCII, then the encoding - * is "7bit". If more than half of the bytes are non-US-ASCII, then - * the encoding is "base64". If less than half of the bytes are - * non-US-ASCII, then the encoding is "quoted-printable". - *
      • - * If the primary type of this datasource is not "text", then if - * all the bytes of its input stream are US-ASCII, the encoding - * is "7bit". If there is even one non-US-ASCII character, the - * encoding is "base64". - *
      - * - * @param ds the DataSource - * @return the encoding. This is either "7bit", - * "quoted-printable" or "base64" - */ - public static String getEncoding(DataSource ds) { - ContentType cType = null; - InputStream is = null; - String encoding = null; - - if (ds instanceof EncodingAware) { - encoding = ((EncodingAware)ds).getEncoding(); - if (encoding != null) - return encoding; - } - try { - cType = new ContentType(ds.getContentType()); - is = ds.getInputStream(); - - boolean isText = cType.match("text/*"); - // if not text, stop processing when we see non-ASCII - int i = checkAscii(is, ALL, !isText); - switch (i) { - case ALL_ASCII: - encoding = "7bit"; // all ASCII - break; - case MOSTLY_ASCII: - if (isText && nonAsciiCharset(cType)) - encoding = "base64"; // charset isn't compatible with ASCII - else - encoding = "quoted-printable"; // mostly ASCII - break; - default: - encoding = "base64"; // mostly binary - break; - } - - } catch (Exception ex) { - return "base64"; // what else ?! - } finally { - // Close the input stream - try { - if (is != null) - is.close(); - } catch (IOException ioex) { } - } - - return encoding; - } - - /** - * Determine whether the charset in the Content-Type is compatible - * with ASCII or not. A charset is compatible with ASCII if the - * encoded byte stream representing the Unicode string "\r\n" is - * the ASCII characters CR and LF. For example, the utf-16be - * charset is not compatible with ASCII. - * - * For performance, we keep a static map that caches the results. - */ - private static boolean nonAsciiCharset(ContentType ct) { - String charset = ct.getParameter("charset"); - if (charset == null) - return false; - charset = charset.toLowerCase(Locale.ENGLISH); - Boolean bool; - synchronized (nonAsciiCharsetMap) { - bool = (Boolean)nonAsciiCharsetMap.get(charset); - } - if (bool == null) { - try { - byte[] b = "\r\n".getBytes(charset); - bool = Boolean.valueOf( - b.length != 2 || b[0] != 015 || b[1] != 012); - } catch (UnsupportedEncodingException uex) { - bool = Boolean.FALSE; // a guess - } catch (RuntimeException ex) { - bool = Boolean.TRUE; // one of the weird ones? - } - synchronized (nonAsciiCharsetMap) { - nonAsciiCharsetMap.put(charset, bool); - } - } - return bool.booleanValue(); - } - - /** - * Same as getEncoding(DataSource) except that instead - * of reading the data from an InputStream it uses the - * writeTo method to examine the data. This is more - * efficient in the common case of a DataHandler - * created with an object and a MIME type (for example, a - * "text/plain" String) because all the I/O is done in this - * thread. In the case requiring an InputStream the - * DataHandler uses a thread, a pair of pipe streams, - * and the writeTo method to produce the data.

      - * - * @since JavaMail 1.2 - */ - public static String getEncoding(DataHandler dh) { - ContentType cType = null; - String encoding = null; - - /* - * Try to pick the most efficient means of determining the - * encoding. If this DataHandler was created using a DataSource, - * the getEncoding(DataSource) method is typically faster. If - * the DataHandler was created with an object, this method is - * much faster. To distinguish the two cases, we use a heuristic. - * A DataHandler created with an object will always have a null name. - * A DataHandler created with a DataSource will usually have a - * non-null name. - * - * XXX - This is actually quite a disgusting hack, but it makes - * a common case run over twice as fast. - */ - if (dh.getName() != null) - return getEncoding(dh.getDataSource()); - - try { - cType = new ContentType(dh.getContentType()); - } catch (Exception ex) { - return "base64"; // what else ?! - } - - if (cType.match("text/*")) { - // Check all of the available bytes - AsciiOutputStream aos = new AsciiOutputStream(false, false); - try { - dh.writeTo(aos); - } catch (IOException ex) { - // ignore it, can't happen - } - switch (aos.getAscii()) { - case ALL_ASCII: - encoding = "7bit"; // all ascii - break; - case MOSTLY_ASCII: - encoding = "quoted-printable"; // mostly ascii - break; - default: - encoding = "base64"; // mostly binary - break; - } - } else { // not "text" - // Check all of available bytes, break out if we find - // at least one non-US-ASCII character - AsciiOutputStream aos = - new AsciiOutputStream(true, encodeEolStrict); - try { - dh.writeTo(aos); - } catch (IOException ex) { } // ignore it - if (aos.getAscii() == ALL_ASCII) // all ascii - encoding = "7bit"; - else // found atleast one non-ascii character, use b64 - encoding = "base64"; - } - - return encoding; - } - - /** - * Decode the given input stream. The Input stream returned is - * the decoded input stream. All the encodings defined in RFC 2045 - * are supported here. They include "base64", "quoted-printable", - * "7bit", "8bit", and "binary". In addition, "uuencode" is also - * supported.

      - * - * In the current implementation, if the - * mail.mime.ignoreunknownencoding system property is set to - * "true", unknown encoding values are ignored and the - * original InputStream is returned. - * - * @param is input stream - * @param encoding the encoding of the stream. - * @return decoded input stream. - * @exception MessagingException if the encoding is unknown - */ - public static InputStream decode(InputStream is, String encoding) - throws MessagingException { - if (encoding.equalsIgnoreCase("base64")) - return new BASE64DecoderStream(is); - else if (encoding.equalsIgnoreCase("quoted-printable")) - return new QPDecoderStream(is); - else if (encoding.equalsIgnoreCase("uuencode") || - encoding.equalsIgnoreCase("x-uuencode") || - encoding.equalsIgnoreCase("x-uue")) - return new UUDecoderStream(is); - else if (encoding.equalsIgnoreCase("binary") || - encoding.equalsIgnoreCase("7bit") || - encoding.equalsIgnoreCase("8bit")) - return is; - else { - if (!ignoreUnknownEncoding) - throw new MessagingException("Unknown encoding: " + encoding); - return is; - } - } - - /** - * Wrap an encoder around the given output stream. - * All the encodings defined in RFC 2045 are supported here. - * They include "base64", "quoted-printable", "7bit", "8bit" and - * "binary". In addition, "uuencode" is also supported. - * - * @param os output stream - * @param encoding the encoding of the stream. - * @return output stream that applies the - * specified encoding. - * @exception MessagingException if the encoding is unknown - */ - public static OutputStream encode(OutputStream os, String encoding) - throws MessagingException { - if (encoding == null) - return os; - else if (encoding.equalsIgnoreCase("base64")) - return new BASE64EncoderStream(os); - else if (encoding.equalsIgnoreCase("quoted-printable")) - return new QPEncoderStream(os); - else if (encoding.equalsIgnoreCase("uuencode") || - encoding.equalsIgnoreCase("x-uuencode") || - encoding.equalsIgnoreCase("x-uue")) - return new UUEncoderStream(os); - else if (encoding.equalsIgnoreCase("binary") || - encoding.equalsIgnoreCase("7bit") || - encoding.equalsIgnoreCase("8bit")) - return os; - else - throw new MessagingException("Unknown encoding: " +encoding); - } - - /** - * Wrap an encoder around the given output stream. - * All the encodings defined in RFC 2045 are supported here. - * They include "base64", "quoted-printable", "7bit", "8bit" and - * "binary". In addition, "uuencode" is also supported. - * The filename parameter is used with the "uuencode" - * encoding and is included in the encoded output. - * - * @param os output stream - * @param encoding the encoding of the stream. - * @param filename name for the file being encoded (only used - * with uuencode) - * @return output stream that applies the - * specified encoding. - * @since JavaMail 1.2 - */ - public static OutputStream encode(OutputStream os, String encoding, - String filename) - throws MessagingException { - if (encoding == null) - return os; - else if (encoding.equalsIgnoreCase("base64")) - return new BASE64EncoderStream(os); - else if (encoding.equalsIgnoreCase("quoted-printable")) - return new QPEncoderStream(os); - else if (encoding.equalsIgnoreCase("uuencode") || - encoding.equalsIgnoreCase("x-uuencode") || - encoding.equalsIgnoreCase("x-uue")) - return new UUEncoderStream(os, filename); - else if (encoding.equalsIgnoreCase("binary") || - encoding.equalsIgnoreCase("7bit") || - encoding.equalsIgnoreCase("8bit")) - return os; - else - throw new MessagingException("Unknown encoding: " +encoding); - } - - /** - * Encode a RFC 822 "text" token into mail-safe form as per - * RFC 2047.

      - * - * The given Unicode string is examined for non US-ASCII - * characters. If the string contains only US-ASCII characters, - * it is returned as-is. If the string contains non US-ASCII - * characters, it is first character-encoded using the platform's - * default charset, then transfer-encoded using either the B or - * Q encoding. The resulting bytes are then returned as a Unicode - * string containing only ASCII characters.

      - * - * Note that this method should be used to encode only - * "unstructured" RFC 822 headers.

      - * - * Example of usage: - *

      -     *
      -     *  MimePart part = ...
      -     *  String rawvalue = "FooBar Mailer, Japanese version 1.1"
      -     *  try {
      -     *    // If we know for sure that rawvalue contains only US-ASCII 
      -     *    // characters, we can skip the encoding part
      -     *    part.setHeader("X-mailer", MimeUtility.encodeText(rawvalue));
      -     *  } catch (UnsupportedEncodingException e) {
      -     *    // encoding failure
      -     *  } catch (MessagingException me) {
      -     *   // setHeader() failure
      -     *  }
      -     *
      -     * 

      - * - * @param text Unicode string - * @return Unicode string containing only US-ASCII characters - * @exception UnsupportedEncodingException if the encoding fails - */ - public static String encodeText(String text) - throws UnsupportedEncodingException { - return encodeText(text, null, null); - } - - /** - * Encode a RFC 822 "text" token into mail-safe form as per - * RFC 2047.

      - * - * The given Unicode string is examined for non US-ASCII - * characters. If the string contains only US-ASCII characters, - * it is returned as-is. If the string contains non US-ASCII - * characters, it is first character-encoded using the specified - * charset, then transfer-encoded using either the B or Q encoding. - * The resulting bytes are then returned as a Unicode string - * containing only ASCII characters.

      - * - * Note that this method should be used to encode only - * "unstructured" RFC 822 headers. - * - * @param text the header value - * @param charset the charset. If this parameter is null, the - * platform's default chatset is used. - * @param encoding the encoding to be used. Currently supported - * values are "B" and "Q". If this parameter is null, then - * the "Q" encoding is used if most of characters to be - * encoded are in the ASCII charset, otherwise "B" encoding - * is used. - * @return Unicode string containing only US-ASCII characters - */ - public static String encodeText(String text, String charset, - String encoding) - throws UnsupportedEncodingException { - return encodeWord(text, charset, encoding, false); - } - - /** - * Decode "unstructured" headers, that is, headers that are defined - * as '*text' as per RFC 822.

      - * - * The string is decoded using the algorithm specified in - * RFC 2047, Section 6.1. If the charset-conversion fails - * for any sequence, an UnsupportedEncodingException is thrown. - * If the String is not an RFC 2047 style encoded header, it is - * returned as-is

      - * - * Example of usage: - *

      -     *
      -     *  MimePart part = ...
      -     *  String rawvalue = null;
      -     *  String  value = null;
      -     *  try {
      -     *    if ((rawvalue = part.getHeader("X-mailer")[0]) != null)
      -     *      value = MimeUtility.decodeText(rawvalue);
      -     *  } catch (UnsupportedEncodingException e) {
      -     *      // Don't care
      -     *      value = rawvalue;
      -     *  } catch (MessagingException me) { }
      -     *
      -     *  return value;
      -     *
      -     * 

      - * - * @param etext the possibly encoded value - * @exception UnsupportedEncodingException if the charset - * conversion failed. - */ - public static String decodeText(String etext) - throws UnsupportedEncodingException { - /* - * We look for sequences separated by "linear-white-space". - * (as per RFC 2047, Section 6.1) - * RFC 822 defines "linear-white-space" as SPACE | HT | CR | NL. - */ - String lwsp = " \t\n\r"; - StringTokenizer st; - - /* - * First, lets do a quick run thru the string and check - * whether the sequence "=?" exists at all. If none exists, - * we know there are no encoded-words in here and we can just - * return the string as-is, without suffering thru the later - * decoding logic. - * This handles the most common case of unencoded headers - * efficiently. - */ - if (etext.indexOf("=?") == -1) - return etext; - - // Encoded words found. Start decoding ... - - st = new StringTokenizer(etext, lwsp, true); - StringBuffer sb = new StringBuffer(); // decode buffer - StringBuffer wsb = new StringBuffer(); // white space buffer - boolean prevWasEncoded = false; - - while (st.hasMoreTokens()) { - char c; - String s = st.nextToken(); - // If whitespace, append it to the whitespace buffer - if (((c = s.charAt(0)) == ' ') || (c == '\t') || - (c == '\r') || (c == '\n')) - wsb.append(c); - else { - // Check if token is an 'encoded-word' .. - String word; - try { - word = decodeWord(s); - // Yes, this IS an 'encoded-word'. - if (!prevWasEncoded && wsb.length() > 0) { - // if the previous word was also encoded, we - // should ignore the collected whitespace. Else - // we include the whitespace as well. - sb.append(wsb); - } - prevWasEncoded = true; - } catch (ParseException pex) { - // This is NOT an 'encoded-word'. - word = s; - // possibly decode inner encoded words - if (!decodeStrict) { - String dword = decodeInnerWords(word); - if (dword != word) { - // if a different String object was returned, - // decoding was done. - if (prevWasEncoded && word.startsWith("=?")) { - // encoded followed by encoded, - // throw away whitespace between - } else { - // include collected whitespace .. - if (wsb.length() > 0) - sb.append(wsb); - } - // did original end with encoded? - prevWasEncoded = word.endsWith("?="); - word = dword; - } else { - // include collected whitespace .. - if (wsb.length() > 0) - sb.append(wsb); - prevWasEncoded = false; - } - } else { - // include collected whitespace .. - if (wsb.length() > 0) - sb.append(wsb); - prevWasEncoded = false; - } - } - sb.append(word); // append the actual word - wsb.setLength(0); // reset wsb for reuse - } - } - sb.append(wsb); // append trailing whitespace - return sb.toString(); - } - - /** - * Encode a RFC 822 "word" token into mail-safe form as per - * RFC 2047.

      - * - * The given Unicode string is examined for non US-ASCII - * characters. If the string contains only US-ASCII characters, - * it is returned as-is. If the string contains non US-ASCII - * characters, it is first character-encoded using the platform's - * default charset, then transfer-encoded using either the B or - * Q encoding. The resulting bytes are then returned as a Unicode - * string containing only ASCII characters.

      - * - * This method is meant to be used when creating RFC 822 "phrases". - * The InternetAddress class, for example, uses this to encode - * it's 'phrase' component. - * - * @param word Unicode string - * @return Array of Unicode strings containing only US-ASCII - * characters. - * @exception UnsupportedEncodingException if the encoding fails - */ - public static String encodeWord(String word) - throws UnsupportedEncodingException { - return encodeWord(word, null, null); - } - - /** - * Encode a RFC 822 "word" token into mail-safe form as per - * RFC 2047.

      - * - * The given Unicode string is examined for non US-ASCII - * characters. If the string contains only US-ASCII characters, - * it is returned as-is. If the string contains non US-ASCII - * characters, it is first character-encoded using the specified - * charset, then transfer-encoded using either the B or Q encoding. - * The resulting bytes are then returned as a Unicode string - * containing only ASCII characters.

      - * - * @param word Unicode string - * @param charset the MIME charset - * @param encoding the encoding to be used. Currently supported - * values are "B" and "Q". If this parameter is null, then - * the "Q" encoding is used if most of characters to be - * encoded are in the ASCII charset, otherwise "B" encoding - * is used. - * @return Unicode string containing only US-ASCII characters - * @exception UnsupportedEncodingException if the encoding fails - */ - public static String encodeWord(String word, String charset, - String encoding) - throws UnsupportedEncodingException { - return encodeWord(word, charset, encoding, true); - } - - /* - * Encode the given string. The parameter 'encodingWord' should - * be true if a RFC 822 "word" token is being encoded and false if a - * RFC 822 "text" token is being encoded. This is because the - * "Q" encoding defined in RFC 2047 has more restrictions when - * encoding "word" tokens. (Sigh) - */ - private static String encodeWord(String string, String charset, - String encoding, boolean encodingWord) - throws UnsupportedEncodingException { - - // If 'string' contains only US-ASCII characters, just - // return it. - int ascii = checkAscii(string); - if (ascii == ALL_ASCII) - return string; - - // Else, apply the specified charset conversion. - String jcharset; - if (charset == null) { // use default charset - jcharset = getDefaultJavaCharset(); // the java charset - charset = getDefaultMIMECharset(); // the MIME equivalent - } else // MIME charset -> java charset - jcharset = javaCharset(charset); - - // If no transfer-encoding is specified, figure one out. - if (encoding == null) { - if (ascii != MOSTLY_NONASCII) - encoding = "Q"; - else - encoding = "B"; - } - - boolean b64; - if (encoding.equalsIgnoreCase("B")) - b64 = true; - else if (encoding.equalsIgnoreCase("Q")) - b64 = false; - else - throw new UnsupportedEncodingException( - "Unknown transfer encoding: " + encoding); - - StringBuffer outb = new StringBuffer(); // the output buffer - doEncode(string, b64, jcharset, - // As per RFC 2047, size of an encoded string should not - // exceed 75 bytes. - // 7 = size of "=?", '?', 'B'/'Q', '?', "?=" - 75 - 7 - charset.length(), // the available space - "=?" + charset + "?" + encoding + "?", // prefix - true, encodingWord, outb); - - return outb.toString(); - } - - private static void doEncode(String string, boolean b64, - String jcharset, int avail, String prefix, - boolean first, boolean encodingWord, StringBuffer buf) - throws UnsupportedEncodingException { - - // First find out what the length of the encoded version of - // 'string' would be. - byte[] bytes = string.getBytes(jcharset); - int len; - if (b64) // "B" encoding - len = BEncoderStream.encodedLength(bytes); - else // "Q" - len = QEncoderStream.encodedLength(bytes, encodingWord); - - int size; - if ((len > avail) && ((size = string.length()) > 1)) { - // If the length is greater than 'avail', split 'string' - // into two and recurse. - doEncode(string.substring(0, size/2), b64, jcharset, - avail, prefix, first, encodingWord, buf); - doEncode(string.substring(size/2, size), b64, jcharset, - avail, prefix, false, encodingWord, buf); - } else { - // length <= than 'avail'. Encode the given string - ByteArrayOutputStream os = new ByteArrayOutputStream(); - OutputStream eos; // the encoder - if (b64) // "B" encoding - eos = new BEncoderStream(os); - else // "Q" encoding - eos = new QEncoderStream(os, encodingWord); - - try { // do the encoding - eos.write(bytes); - eos.close(); - } catch (IOException ioex) { } - - byte[] encodedBytes = os.toByteArray(); // the encoded stuff - // Now write out the encoded (all ASCII) bytes into our - // StringBuffer - if (!first) // not the first line of this sequence - if (foldEncodedWords) - buf.append("\r\n "); // start a continuation line - else - buf.append(" "); // line will be folded later - - buf.append(prefix); - for (int i = 0; i < encodedBytes.length; i++) - buf.append((char)encodedBytes[i]); - buf.append("?="); // terminate the current sequence - } - } - - /** - * The string is parsed using the rules in RFC 2047 and RFC 2231 for - * parsing an "encoded-word". If the parse fails, a ParseException is - * thrown. Otherwise, it is transfer-decoded, and then - * charset-converted into Unicode. If the charset-conversion - * fails, an UnsupportedEncodingException is thrown.

      - * - * @param eword the encoded value - * @exception ParseException if the string is not an - * encoded-word as per RFC 2047 and RFC 2231. - * @exception UnsupportedEncodingException if the charset - * conversion failed. - */ - public static String decodeWord(String eword) - throws ParseException, UnsupportedEncodingException { - - if (!eword.startsWith("=?")) // not an encoded word - throw new ParseException( - "encoded word does not start with \"=?\": " + eword); - - // get charset - int start = 2; int pos; - if ((pos = eword.indexOf('?', start)) == -1) - throw new ParseException( - "encoded word does not include charset: " + eword); - String charset = eword.substring(start, pos); - int lpos = charset.indexOf('*'); // RFC 2231 language specified? - if (lpos >= 0) // yes, throw it away - charset = charset.substring(0, lpos); - charset = javaCharset(charset); - - // get encoding - start = pos+1; - if ((pos = eword.indexOf('?', start)) == -1) - throw new ParseException( - "encoded word does not include encoding: " + eword); - String encoding = eword.substring(start, pos); - - // get encoded-sequence - start = pos+1; - if ((pos = eword.indexOf("?=", start)) == -1) - throw new ParseException( - "encoded word does not end with \"?=\": " + eword); - /* - * XXX - should include this, but leaving it out for compatibility... - * - if (decodeStrict && pos != eword.length() - 2) - throw new ParseException( - "encoded word does not end with \"?=\": " + eword);); - */ - String word = eword.substring(start, pos); - - try { - String decodedWord; - if (word.length() > 0) { - // Extract the bytes from word - ByteArrayInputStream bis = - new ByteArrayInputStream(ASCIIUtility.getBytes(word)); - - // Get the appropriate decoder - InputStream is; - if (encoding.equalsIgnoreCase("B")) - is = new BASE64DecoderStream(bis); - else if (encoding.equalsIgnoreCase("Q")) - is = new QDecoderStream(bis); - else - throw new UnsupportedEncodingException( - "unknown encoding: " + encoding); - - // For b64 & q, size of decoded word <= size of word. So - // the decoded bytes must fit into the 'bytes' array. This - // is certainly more efficient than writing bytes into a - // ByteArrayOutputStream and then pulling out the byte[] - // from it. - int count = bis.available(); - byte[] bytes = new byte[count]; - // count is set to the actual number of decoded bytes - count = is.read(bytes, 0, count); - - // Finally, convert the decoded bytes into a String using - // the specified charset - decodedWord = count <= 0 ? "" : - new String(bytes, 0, count, charset); - } else { - // no characters to decode, return empty string - decodedWord = ""; - } - if (pos + 2 < eword.length()) { - // there's still more text in the string - String rest = eword.substring(pos + 2); - if (!decodeStrict) - rest = decodeInnerWords(rest); - decodedWord += rest; - } - return decodedWord; - } catch (UnsupportedEncodingException uex) { - // explicitly catch and rethrow this exception, otherwise - // the below IOException catch will swallow this up! - throw uex; - } catch (IOException ioex) { - // Shouldn't happen. - throw new ParseException(ioex.toString()); - } catch (IllegalArgumentException iex) { - /* An unknown charset of the form ISO-XXX-XXX, will cause - * the JDK to throw an IllegalArgumentException ... Since the - * JDK will attempt to create a classname using this string, - * but valid classnames must not contain the character '-', - * and this results in an IllegalArgumentException, rather than - * the expected UnsupportedEncodingException. Yikes - */ - throw new UnsupportedEncodingException(charset); - } - } - - /** - * Look for encoded words within a word. The MIME spec doesn't - * allow this, but many broken mailers, especially Japanese mailers, - * produce such incorrect encodings. - */ - private static String decodeInnerWords(String word) - throws UnsupportedEncodingException { - int start = 0, i; - StringBuffer buf = new StringBuffer(); - while ((i = word.indexOf("=?", start)) >= 0) { - buf.append(word.substring(start, i)); - // find first '?' after opening '=?' - end of charset - int end = word.indexOf('?', i + 2); - if (end < 0) - break; - // find next '?' after that - end of encoding - end = word.indexOf('?', end + 1); - if (end < 0) - break; - // find terminating '?=' - end = word.indexOf("?=", end + 1); - if (end < 0) - break; - String s = word.substring(i, end + 2); - try { - s = decodeWord(s); - } catch (ParseException pex) { - // ignore it, just use the original string - } - buf.append(s); - start = end + 2; - } - if (start == 0) - return word; - if (start < word.length()) - buf.append(word.substring(start)); - return buf.toString(); - } - - /** - * A utility method to quote a word, if the word contains any - * characters from the specified 'specials' list.

      - * - * The HeaderTokenizer class defines two special - * sets of delimiters - MIME and RFC 822.

      - * - * This method is typically used during the generation of - * RFC 822 and MIME header fields. - * - * @param word word to be quoted - * @param specials the set of special characters - * @return the possibly quoted word - * @see javax.mail.internet.HeaderTokenizer#MIME - * @see javax.mail.internet.HeaderTokenizer#RFC822 - */ - public static String quote(String word, String specials) { - int len = word.length(); - if (len == 0) - return "\"\""; // an empty string is handled specially - - /* - * Look for any "bad" characters, Escape and - * quote the entire string if necessary. - */ - boolean needQuoting = false; - for (int i = 0; i < len; i++) { - char c = word.charAt(i); - if (c == '"' || c == '\\' || c == '\r' || c == '\n') { - // need to escape them and then quote the whole string - StringBuffer sb = new StringBuffer(len + 3); - sb.append('"'); - sb.append(word.substring(0, i)); - int lastc = 0; - for (int j = i; j < len; j++) { - char cc = word.charAt(j); - if ((cc == '"') || (cc == '\\') || - (cc == '\r') || (cc == '\n')) - if (cc == '\n' && lastc == '\r') - ; // do nothing, CR was already escaped - else - sb.append('\\'); // Escape the character - sb.append(cc); - lastc = cc; - } - sb.append('"'); - return sb.toString(); - } else if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0) - // These characters cause the string to be quoted - needQuoting = true; - } - - if (needQuoting) { - StringBuffer sb = new StringBuffer(len + 2); - sb.append('"').append(word).append('"'); - return sb.toString(); - } else - return word; - } - - /** - * Fold a string at linear whitespace so that each line is no longer - * than 76 characters, if possible. If there are more than 76 - * non-whitespace characters consecutively, the string is folded at - * the first whitespace after that sequence. The parameter - * used indicates how many characters have been used in - * the current line; it is usually the length of the header name.

      - * - * Note that line breaks in the string aren't escaped; they probably - * should be. - * - * @param used characters used in line so far - * @param s the string to fold - * @return the folded string - * @since JavaMail 1.4 - */ - public static String fold(int used, String s) { - if (!foldText) - return s; - - int end; - char c; - // Strip trailing spaces and newlines - for (end = s.length() - 1; end >= 0; end--) { - c = s.charAt(end); - if (c != ' ' && c != '\t' && c != '\r' && c != '\n') - break; - } - if (end != s.length() - 1) - s = s.substring(0, end + 1); - - // if the string fits now, just return it - if (used + s.length() <= 76) - return s; - - // have to actually fold the string - StringBuffer sb = new StringBuffer(s.length() + 4); - char lastc = 0; - while (used + s.length() > 76) { - int lastspace = -1; - for (int i = 0; i < s.length(); i++) { - if (lastspace != -1 && used + i > 76) - break; - c = s.charAt(i); - if (c == ' ' || c == '\t') - if (!(lastc == ' ' || lastc == '\t')) - lastspace = i; - lastc = c; - } - if (lastspace == -1) { - // no space, use the whole thing - sb.append(s); - s = ""; - used = 0; - break; - } - sb.append(s.substring(0, lastspace)); - sb.append("\r\n"); - lastc = s.charAt(lastspace); - sb.append(lastc); - s = s.substring(lastspace + 1); - used = 1; - } - sb.append(s); - return sb.toString(); - } - - /** - * Unfold a folded header. Any line breaks that aren't escaped and - * are followed by whitespace are removed. - * - * @param s the string to unfold - * @return the unfolded string - * @since JavaMail 1.4 - */ - public static String unfold(String s) { - if (!foldText) - return s; - - StringBuffer sb = null; - int i; - while ((i = indexOfAny(s, "\r\n")) >= 0) { - int start = i; - int l = s.length(); - i++; // skip CR or NL - if (i < l && s.charAt(i - 1) == '\r' && s.charAt(i) == '\n') - i++; // skip LF - if (start == 0 || s.charAt(start - 1) != '\\') { - char c; - // if next line starts with whitespace, skip all of it - // XXX - always has to be true? - if (i < l && ((c = s.charAt(i)) == ' ' || c == '\t')) { - i++; // skip whitespace - while (i < l && ((c = s.charAt(i)) == ' ' || c == '\t')) - i++; - if (sb == null) - sb = new StringBuffer(s.length()); - if (start != 0) { - sb.append(s.substring(0, start)); - sb.append(' '); - } - s = s.substring(i); - continue; - } - // it's not a continuation line, just leave it in - if (sb == null) - sb = new StringBuffer(s.length()); - sb.append(s.substring(0, i)); - s = s.substring(i); - } else { - // there's a backslash at "start - 1" - // strip it out, but leave in the line break - if (sb == null) - sb = new StringBuffer(s.length()); - sb.append(s.substring(0, start - 1)); - sb.append(s.substring(start, i)); - s = s.substring(i); - } - } - if (sb != null) { - sb.append(s); - return sb.toString(); - } else - return s; - } - - /** - * Return the first index of any of the characters in "any" in "s", - * or -1 if none are found. - * - * This should be a method on String. - */ - private static int indexOfAny(String s, String any) { - return indexOfAny(s, any, 0); - } - - private static int indexOfAny(String s, String any, int start) { - try { - int len = s.length(); - for (int i = start; i < len; i++) { - if (any.indexOf(s.charAt(i)) >= 0) - return i; - } - return -1; - } catch (StringIndexOutOfBoundsException e) { - return -1; - } - } - - /** - * Convert a MIME charset name into a valid Java charset name.

      - * - * @param charset the MIME charset name - * @return the Java charset equivalent. If a suitable mapping is - * not available, the passed in charset is itself returned. - */ - public static String javaCharset(String charset) { - if (mime2java == null || charset == null) - // no mapping table, or charset parameter is null - return charset; - - String alias = - (String)mime2java.get(charset.toLowerCase(Locale.ENGLISH)); - return alias == null ? charset : alias; - } - - /** - * Convert a java charset into its MIME charset name.

      - * - * Note that a future version of JDK (post 1.2) might provide - * this functionality, in which case, we may deprecate this - * method then. - * - * @param charset the JDK charset - * @return the MIME/IANA equivalent. If a mapping - * is not possible, the passed in charset itself - * is returned. - * @since JavaMail 1.1 - */ - public static String mimeCharset(String charset) { - if (java2mime == null || charset == null) - // no mapping table or charset param is null - return charset; - - String alias = - (String)java2mime.get(charset.toLowerCase(Locale.ENGLISH)); - return alias == null ? charset : alias; - } - - private static String defaultJavaCharset; - private static String defaultMIMECharset; - - /** - * Get the default charset corresponding to the system's current - * default locale. If the System property mail.mime.charset - * is set, a system charset corresponding to this MIME charset will be - * returned.

      - * - * @return the default charset of the system's default locale, - * as a Java charset. (NOT a MIME charset) - * @since JavaMail 1.1 - */ - public static String getDefaultJavaCharset() { - if (defaultJavaCharset == null) { - /* - * If mail.mime.charset is set, it controls the default - * Java charset as well. - */ - String mimecs = null; - try { - mimecs = System.getProperty("mail.mime.charset"); - } catch (SecurityException ex) { } // ignore it - if (mimecs != null && mimecs.length() > 0) { - defaultJavaCharset = javaCharset(mimecs); - return defaultJavaCharset; - } - - try { - defaultJavaCharset = System.getProperty("file.encoding", - "8859_1"); - } catch (SecurityException sex) { - - class NullInputStream extends InputStream { - public int read() { - return 0; - } - } - InputStreamReader reader = - new InputStreamReader(new NullInputStream()); - defaultJavaCharset = reader.getEncoding(); - if (defaultJavaCharset == null) - defaultJavaCharset = "8859_1"; - } - } - - return defaultJavaCharset; - } - - /* - * Get the default MIME charset for this locale. - */ - static String getDefaultMIMECharset() { - if (defaultMIMECharset == null) { - try { - defaultMIMECharset = System.getProperty("mail.mime.charset"); - } catch (SecurityException ex) { } // ignore it - } - if (defaultMIMECharset == null) - defaultMIMECharset = mimeCharset(getDefaultJavaCharset()); - return defaultMIMECharset; - } - - // Tables to map MIME charset names to Java names and vice versa. - // XXX - Should eventually use J2SE 1.4 java.nio.charset.Charset - private static Hashtable mime2java; - private static Hashtable java2mime; - - static { - java2mime = new Hashtable(40); - mime2java = new Hashtable(10); - - try { - // Use this class's classloader to load the mapping file - // XXX - we should use SecuritySupport, but it's in another package - InputStream is = - javax.mail.internet.MimeUtility.class.getResourceAsStream( - "/META-INF/javamail.charset.map"); - - if (is != null) { - try { - is = new LineInputStream(is); - - // Load the JDK-to-MIME charset mapping table - loadMappings((LineInputStream)is, java2mime); - - // Load the MIME-to-JDK charset mapping table - loadMappings((LineInputStream)is, mime2java); - } finally { - try { - is.close(); - } catch (Exception cex) { - // ignore - } - } - } - } catch (Exception ex) { } - - // If we didn't load the tables, e.g., because we didn't have - // permission, load them manually. The entries here should be - // the same as the default javamail.charset.map. - if (java2mime.isEmpty()) { - java2mime.put("8859_1", "ISO-8859-1"); - java2mime.put("iso8859_1", "ISO-8859-1"); - java2mime.put("iso8859-1", "ISO-8859-1"); - - java2mime.put("8859_2", "ISO-8859-2"); - java2mime.put("iso8859_2", "ISO-8859-2"); - java2mime.put("iso8859-2", "ISO-8859-2"); - - java2mime.put("8859_3", "ISO-8859-3"); - java2mime.put("iso8859_3", "ISO-8859-3"); - java2mime.put("iso8859-3", "ISO-8859-3"); - - java2mime.put("8859_4", "ISO-8859-4"); - java2mime.put("iso8859_4", "ISO-8859-4"); - java2mime.put("iso8859-4", "ISO-8859-4"); - - java2mime.put("8859_5", "ISO-8859-5"); - java2mime.put("iso8859_5", "ISO-8859-5"); - java2mime.put("iso8859-5", "ISO-8859-5"); - - java2mime.put("8859_6", "ISO-8859-6"); - java2mime.put("iso8859_6", "ISO-8859-6"); - java2mime.put("iso8859-6", "ISO-8859-6"); - - java2mime.put("8859_7", "ISO-8859-7"); - java2mime.put("iso8859_7", "ISO-8859-7"); - java2mime.put("iso8859-7", "ISO-8859-7"); - - java2mime.put("8859_8", "ISO-8859-8"); - java2mime.put("iso8859_8", "ISO-8859-8"); - java2mime.put("iso8859-8", "ISO-8859-8"); - - java2mime.put("8859_9", "ISO-8859-9"); - java2mime.put("iso8859_9", "ISO-8859-9"); - java2mime.put("iso8859-9", "ISO-8859-9"); - - java2mime.put("sjis", "Shift_JIS"); - java2mime.put("jis", "ISO-2022-JP"); - java2mime.put("iso2022jp", "ISO-2022-JP"); - java2mime.put("euc_jp", "euc-jp"); - java2mime.put("koi8_r", "koi8-r"); - java2mime.put("euc_cn", "euc-cn"); - java2mime.put("euc_tw", "euc-tw"); - java2mime.put("euc_kr", "euc-kr"); - } - if (mime2java.isEmpty()) { - mime2java.put("iso-2022-cn", "ISO2022CN"); - mime2java.put("iso-2022-kr", "ISO2022KR"); - mime2java.put("utf-8", "UTF8"); - mime2java.put("utf8", "UTF8"); - mime2java.put("ja_jp.iso2022-7", "ISO2022JP"); - mime2java.put("ja_jp.eucjp", "EUCJIS"); - mime2java.put("euc-kr", "KSC5601"); - mime2java.put("euckr", "KSC5601"); - mime2java.put("us-ascii", "ISO-8859-1"); - mime2java.put("x-us-ascii", "ISO-8859-1"); - } - } - - private static void loadMappings(LineInputStream is, Hashtable table) { - String currLine; - - while (true) { - try { - currLine = is.readLine(); - } catch (IOException ioex) { - break; // error in reading, stop - } - - if (currLine == null) // end of file, stop - break; - if (currLine.startsWith("--") && currLine.endsWith("--")) - // end of this table - break; - - // ignore empty lines and comments - if (currLine.trim().length() == 0 || currLine.startsWith("#")) - continue; - - // A valid entry is of the form - // where, := SPACE | HT. Parse this - StringTokenizer tk = new StringTokenizer(currLine, " \t"); - try { - String key = tk.nextToken(); - String value = tk.nextToken(); - table.put(key.toLowerCase(Locale.ENGLISH), value); - } catch (NoSuchElementException nex) { } - } - } - - static final int ALL_ASCII = 1; - static final int MOSTLY_ASCII = 2; - static final int MOSTLY_NONASCII = 3; - - /** - * Check if the given string contains non US-ASCII characters. - * @param s string - * @return ALL_ASCII if all characters in the string - * belong to the US-ASCII charset. MOSTLY_ASCII - * if more than half of the available characters - * are US-ASCII characters. Else MOSTLY_NONASCII. - */ - static int checkAscii(String s) { - int ascii = 0, non_ascii = 0; - int l = s.length(); - - for (int i = 0; i < l; i++) { - if (nonascii((int)s.charAt(i))) // non-ascii - non_ascii++; - else - ascii++; - } - - if (non_ascii == 0) - return ALL_ASCII; - if (ascii > non_ascii) - return MOSTLY_ASCII; - - return MOSTLY_NONASCII; - } - - /** - * Check if the given byte array contains non US-ASCII characters. - * @param b byte array - * @return ALL_ASCII if all characters in the string - * belong to the US-ASCII charset. MOSTLY_ASCII - * if more than half of the available characters - * are US-ASCII characters. Else MOSTLY_NONASCII. - * - * XXX - this method is no longer used - */ - static int checkAscii(byte[] b) { - int ascii = 0, non_ascii = 0; - - for (int i=0; i < b.length; i++) { - // The '&' operator automatically causes b[i] to be promoted - // to an int, and we mask out the higher bytes in the int - // so that the resulting value is not a negative integer. - if (nonascii(b[i] & 0xff)) // non-ascii - non_ascii++; - else - ascii++; - } - - if (non_ascii == 0) - return ALL_ASCII; - if (ascii > non_ascii) - return MOSTLY_ASCII; - - return MOSTLY_NONASCII; - } - - /** - * Check if the given input stream contains non US-ASCII characters. - * Upto max bytes are checked. If max is - * set to ALL, then all the bytes available in this - * input stream are checked. If breakOnNonAscii is true - * the check terminates when the first non-US-ASCII character is - * found and MOSTLY_NONASCII is returned. Else, the check continues - * till max bytes or till the end of stream. - * - * @param is the input stream - * @param max maximum bytes to check for. The special value - * ALL indicates that all the bytes in this input - * stream must be checked. - * @param breakOnNonAscii if true, then terminate the - * the check when the first non-US-ASCII character - * is found. - * @return ALL_ASCII if all characters in the string - * belong to the US-ASCII charset. MOSTLY_ASCII - * if more than half of the available characters - * are US-ASCII characters. Else MOSTLY_NONASCII. - */ - static int checkAscii(InputStream is, int max, boolean breakOnNonAscii) { - int ascii = 0, non_ascii = 0; - int len; - int block = 4096; - int linelen = 0; - boolean longLine = false, badEOL = false; - boolean checkEOL = encodeEolStrict && breakOnNonAscii; - byte buf[] = null; - if (max != 0) { - block = (max == ALL) ? 4096 : Math.min(max, 4096); - buf = new byte[block]; - } - while (max != 0) { - try { - if ((len = is.read(buf, 0, block)) == -1) - break; - int lastb = 0; - for (int i = 0; i < len; i++) { - // The '&' operator automatically causes b[i] to - // be promoted to an int, and we mask out the higher - // bytes in the int so that the resulting value is - // not a negative integer. - int b = buf[i] & 0xff; - if (checkEOL && - ((lastb == '\r' && b != '\n') || - (lastb != '\r' && b == '\n'))) - badEOL = true; - if (b == '\r' || b == '\n') - linelen = 0; - else { - linelen++; - if (linelen > 998) // 1000 - CRLF - longLine = true; - } - if (nonascii(b)) { // non-ascii - if (breakOnNonAscii) // we are done - return MOSTLY_NONASCII; - else - non_ascii++; - } else - ascii++; - lastb = b; - } - } catch (IOException ioex) { - break; - } - if (max != ALL) - max -= len; - } - - if (max == 0 && breakOnNonAscii) - // We have been told to break on the first non-ascii character. - // We haven't got any non-ascii character yet, but then we - // have not checked all of the available bytes either. So we - // cannot say for sure that this input stream is ALL_ASCII, - // and hence we must play safe and return MOSTLY_NONASCII - - return MOSTLY_NONASCII; - - if (non_ascii == 0) { // no non-us-ascii characters so far - // If we're looking at non-text data, and we saw CR without LF - // or vice versa, consider this mostly non-ASCII so that it - // will be base64 encoded (since the quoted-printable encoder - // doesn't encode this case properly). - if (badEOL) - return MOSTLY_NONASCII; - // if we've seen a long line, we degrade to mostly ascii - else if (longLine) - return MOSTLY_ASCII; - else - return ALL_ASCII; - } - if (ascii > non_ascii) // mostly ascii - return MOSTLY_ASCII; - return MOSTLY_NONASCII; - } - - static final boolean nonascii(int b) { - return b >= 0177 || (b < 040 && b != '\r' && b != '\n' && b != '\t'); - } -} - -/** - * An OutputStream that determines whether the data written to - * it is all ASCII, mostly ASCII, or mostly non-ASCII. - */ -class AsciiOutputStream extends OutputStream { - private boolean breakOnNonAscii; - private int ascii = 0, non_ascii = 0; - private int linelen = 0; - private boolean longLine = false; - private boolean badEOL = false; - private boolean checkEOL = false; - private int lastb = 0; - private int ret = 0; - - public AsciiOutputStream(boolean breakOnNonAscii, boolean encodeEolStrict) { - this.breakOnNonAscii = breakOnNonAscii; - checkEOL = encodeEolStrict && breakOnNonAscii; - } - - public void write(int b) throws IOException { - check(b); - } - - public void write(byte b[]) throws IOException { - write(b, 0, b.length); - } - - public void write(byte b[], int off, int len) throws IOException { - len += off; - for (int i = off; i < len ; i++) - check(b[i]); - } - - private final void check(int b) throws IOException { - b &= 0xff; - if (checkEOL && - ((lastb == '\r' && b != '\n') || (lastb != '\r' && b == '\n'))) - badEOL = true; - if (b == '\r' || b == '\n') - linelen = 0; - else { - linelen++; - if (linelen > 998) // 1000 - CRLF - longLine = true; - } - if (MimeUtility.nonascii(b)) { // non-ascii - non_ascii++; - if (breakOnNonAscii) { // we are done - ret = MimeUtility.MOSTLY_NONASCII; - throw new EOFException(); - } - } else - ascii++; - lastb = b; - } - - /** - * Return ASCII-ness of data stream. - */ - public int getAscii() { - if (ret != 0) - return ret; - // If we're looking at non-text data, and we saw CR without LF - // or vice versa, consider this mostly non-ASCII so that it - // will be base64 encoded (since the quoted-printable encoder - // doesn't encode this case properly). - if (badEOL) - return MimeUtility.MOSTLY_NONASCII; - else if (non_ascii == 0) { // no non-us-ascii characters so far - // if we've seen a long line, we degrade to mostly ascii - if (longLine) - return MimeUtility.MOSTLY_ASCII; - else - return MimeUtility.ALL_ASCII; - } - if (ascii > non_ascii) // mostly ascii - return MimeUtility.MOSTLY_ASCII; - return MimeUtility.MOSTLY_NONASCII; - } -} diff --git a/src/main/java/javax/mail/internet/NewsAddress.java b/src/main/java/javax/mail/internet/NewsAddress.java deleted file mode 100644 index 200c504c..00000000 --- a/src/main/java/javax/mail/internet/NewsAddress.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.util.Vector; -import java.util.StringTokenizer; -import java.util.Locale; -import javax.mail.*; - -/** - * This class models an RFC1036 newsgroup address. - * - * @author Bill Shannon - * @author John Mani - */ - -public class NewsAddress extends Address { - - protected String newsgroup; - protected String host; // may be null - - private static final long serialVersionUID = -4203797299824684143L; - - /** - * Default constructor. - */ - public NewsAddress() { } - - /** - * Construct a NewsAddress with the given newsgroup. - * - * @param newsgroup the newsgroup - */ - public NewsAddress(String newsgroup) { - this(newsgroup, null); - } - - /** - * Construct a NewsAddress with the given newsgroup and host. - * - * @param newsgroup the newsgroup - * @param host the host - */ - public NewsAddress(String newsgroup, String host) { - this.newsgroup = newsgroup; - this.host = host; - } - - /** - * Return the type of this address. The type of a NewsAddress - * is "news". - */ - public String getType() { - return "news"; - } - - /** - * Set the newsgroup. - * - * @param newsgroup the newsgroup - */ - public void setNewsgroup(String newsgroup) { - this.newsgroup = newsgroup; - } - - /** - * Get the newsgroup. - * - * @return newsgroup - */ - public String getNewsgroup() { - return newsgroup; - } - - /** - * Set the host. - * - * @param host the host - */ - public void setHost(String host) { - this.host = host; - } - - /** - * Get the host. - * - * @return host - */ - public String getHost() { - return host; - } - - /** - * Convert this address into a RFC 1036 address. - * - * @return newsgroup - */ - public String toString() { - return newsgroup; - } - - /** - * The equality operator. - */ - public boolean equals(Object a) { - if (!(a instanceof NewsAddress)) - return false; - - NewsAddress s = (NewsAddress)a; - return newsgroup.equals(s.newsgroup) && - ((host == null && s.host == null) || - (host != null && s.host != null && host.equalsIgnoreCase(s.host))); - } - - /** - * Compute a hash code for the address. - */ - public int hashCode() { - int hash = 0; - if (newsgroup != null) - hash += newsgroup.hashCode(); - if (host != null) - hash += host.toLowerCase(Locale.ENGLISH).hashCode(); - return hash; - } - - /** - * Convert the given array of NewsAddress objects into - * a comma separated sequence of address strings. The - * resulting string contains only US-ASCII characters, and - * hence is mail-safe. - * - * @param addresses array of NewsAddress objects - * @exception ClassCastException, if any address object in the - * given array is not a NewsAddress objects. Note - * that this is a RuntimeException. - * @return comma separated address strings - */ - public static String toString(Address[] addresses) { - if (addresses == null || addresses.length == 0) - return null; - - StringBuffer s = - new StringBuffer(((NewsAddress)addresses[0]).toString()); - for (int i = 1; i < addresses.length; i++) - s.append(",").append(((NewsAddress)addresses[i]).toString()); - - return s.toString(); - } - - /** - * Parse the given comma separated sequence of newsgroup into - * NewsAddress objects. - * - * @param newsgroups comma separated newsgroup string - * @return array of NewsAddress objects - * @exception AddressException if the parse failed - */ - public static NewsAddress[] parse(String newsgroups) - throws AddressException { - // XXX - verify format of newsgroup name? - StringTokenizer st = new StringTokenizer(newsgroups, ","); - Vector nglist = new Vector(); - while (st.hasMoreTokens()) { - String ng = st.nextToken(); - nglist.addElement(new NewsAddress(ng)); - } - int size = nglist.size(); - NewsAddress[] na = new NewsAddress[size]; - if (size > 0) - nglist.copyInto(na); - return na; - } -} diff --git a/src/main/java/javax/mail/internet/ParameterList.java b/src/main/java/javax/mail/internet/ParameterList.java deleted file mode 100644 index 29bd9876..00000000 --- a/src/main/java/javax/mail/internet/ParameterList.java +++ /dev/null @@ -1,828 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.util.*; -import java.io.*; -import com.sun.mail.util.PropUtil; -import com.sun.mail.util.ASCIIUtility; - -/** - * This class holds MIME parameters (attribute-value pairs). - * The mail.mime.encodeparameters and - * mail.mime.decodeparameters System properties - * control whether encoded parameters, as specified by - * RFC 2231, - * are supported. By default, such encoded parameters are - * supported.

      - * - * Also, in the current implementation, setting the System property - * mail.mime.decodeparameters.strict to "true" - * will cause a ParseException to be thrown for errors - * detected while decoding encoded parameters. By default, if any - * decoding errors occur, the original (undecoded) string is used.

      - * - * The current implementation supports the System property - * mail.mime.parameters.strict, which if set to false - * when parsing a parameter list allows parameter values - * to contain whitespace and other special characters without - * being quoted; the parameter value ends at the next semicolon. - * If set to true (the default), parameter values are required to conform - * to the MIME specification and must be quoted if they contain whitespace - * or special characters. - * - * @author John Mani - * @author Bill Shannon - */ - -public class ParameterList { - - /** - * The map of name, value pairs. - * The value object is either a String, for unencoded - * values, or a Value object, for encoded values, - * or a MultiValue object, for multi-segment parameters. - * - * We use a LinkedHashMap so that parameters are (as much as - * possible) kept in the original order. Note however that - * multi-segment parameters (see below) will appear in the - * position of the first seen segment and orphan segments - * will all move to the end. - */ - private Map list = new LinkedHashMap(); // keep parameters in order - - /** - * A set of names for multi-segment parameters that we - * haven't processed yet. Normally such names are accumulated - * during the inital parse and processed at the end of the parse, - * but such names can also be set via the set method when the - * IMAP provider accumulates pre-parsed pieces of a parameter list. - * (A special call to the set method tells us when the IMAP provider - * is done setting parameters.) - * - * A multi-segment parameter is defined by RFC 2231. For example, - * "title*0=part1; title*1=part2", which represents a parameter - * named "title" with value "part1part2". - * - * Note also that each segment of the value might or might not be - * encoded, indicated by a trailing "*" on the parameter name. - * If any segment is encoded, the first segment must be encoded. - * Only the first segment contains the charset and language - * information needed to decode any encoded segments. - * - * RFC 2231 introduces many possible failure modes, which we try - * to handle as gracefully as possible. Generally, a failure to - * decode a parameter value causes the non-decoded parameter value - * to be used instead. Missing segments cause all later segments - * to be appear as independent parameters with names that include - * the segment number. For example, "title*0=part1; title*1=part2; - * title*3=part4" appears as two parameters named "title" and "title*3". - */ - private Set multisegmentNames; - - /** - * A map containing the segments for all not-yet-processed - * multi-segment parameters. The map is indexed by "name*seg". - * The value object is either a String or a Value object. - * The Value object is not decoded during the initial parse - * because the segments may appear in any order and until the - * first segment appears we don't know what charset to use to - * decode the encoded segments. The segments are hex decoded - * in order, combined into a single byte array, and converted - * to a String using the specified charset in the - * combineMultisegmentNames method. - */ - private Map slist; - - /** - * MWB 3BView: The name of the last parameter added to the map. - * Used for the AppleMail hack. - */ - private String lastName = null; - - private static final boolean encodeParameters = - PropUtil.getBooleanSystemProperty("mail.mime.encodeparameters", true); - private static final boolean decodeParameters = - PropUtil.getBooleanSystemProperty("mail.mime.decodeparameters", true); - private static final boolean decodeParametersStrict = - PropUtil.getBooleanSystemProperty( - "mail.mime.decodeparameters.strict", false); - private static final boolean applehack = - PropUtil.getBooleanSystemProperty("mail.mime.applefilenames", false); - private static final boolean windowshack = - PropUtil.getBooleanSystemProperty("mail.mime.windowsfilenames", false); - private static final boolean parametersStrict = - PropUtil.getBooleanSystemProperty("mail.mime.parameters.strict", true); - - - /** - * A struct to hold an encoded value. - * A parsed encoded value is stored as both the - * decoded value and the original encoded value - * (so that toString will produce the same result). - * An encoded value that is set explicitly is stored - * as the original value and the encoded value, to - * ensure that get will return the same value that - * was set. - */ - private static class Value { - String value; - String charset; - String encodedValue; - } - - /** - * A struct for a multi-segment parameter. Each entry in the - * List is either a String or a Value object. When all the - * segments are present and combined in the combineMultisegmentNames - * method, the value field contains the combined and decoded value. - * Until then the value field contains an empty string as a placeholder. - */ - private static class MultiValue extends ArrayList { - String value; - } - - /** - * Map the LinkedHashMap's keySet iterator to an Enumeration. - */ - private static class ParamEnum implements Enumeration { - private Iterator it; - - ParamEnum(Iterator it) { - this.it = it; - } - - public boolean hasMoreElements() { - return it.hasNext(); - } - - public Object nextElement() { - return it.next(); - } - } - - /** - * No-arg Constructor. - */ - public ParameterList() { - // initialize other collections only if they'll be needed - if (decodeParameters) { - multisegmentNames = new HashSet(); - slist = new HashMap(); - } - } - - /** - * Constructor that takes a parameter-list string. The String - * is parsed and the parameters are collected and stored internally. - * A ParseException is thrown if the parse fails. - * Note that an empty parameter-list string is valid and will be - * parsed into an empty ParameterList. - * - * @param s the parameter-list string. - * @exception ParseException if the parse fails. - */ - public ParameterList(String s) throws ParseException { - this(); - - HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); - for (;;) { - HeaderTokenizer.Token tk = h.next(); - int type = tk.getType(); - String name, value; - - if (type == HeaderTokenizer.Token.EOF) // done - break; - - if ((char)type == ';') { - // expect parameter name - tk = h.next(); - // tolerate trailing semicolon, even though it violates the spec - if (tk.getType() == HeaderTokenizer.Token.EOF) - break; - // parameter name must be a MIME Atom - if (tk.getType() != HeaderTokenizer.Token.ATOM) - throw new ParseException("Expected parameter name, " + - "got \"" + tk.getValue() + "\""); - name = tk.getValue().toLowerCase(Locale.ENGLISH); - - // expect '=' - tk = h.next(); - if ((char)tk.getType() != '=') - throw new ParseException("Expected '=', " + - "got \"" + tk.getValue() + "\""); - - // expect parameter value - if (windowshack && - (name.equals("name") || name.equals("filename"))) - tk = h.next(';', true); - else if (parametersStrict) - tk = h.next(); - else - tk = h.next(';'); - type = tk.getType(); - // parameter value must be a MIME Atom or Quoted String - if (type != HeaderTokenizer.Token.ATOM && - type != HeaderTokenizer.Token.QUOTEDSTRING) - throw new ParseException("Expected parameter value, " + - "got \"" + tk.getValue() + "\""); - - value = tk.getValue(); - lastName = name; - if (decodeParameters) - putEncodedName(name, value); - else - list.put(name, value); - } else { - // MWB 3BView new code to add in filenames generated by - // AppleMail. - // Note - one space is assumed between name elements. - // This may not be correct but it shouldn't matter too much. - // Note: AppleMail encodes filenames with non-ascii characters - // correctly, so we don't need to worry about the name* subkeys. - if (type == HeaderTokenizer.Token.ATOM && lastName != null && - ((applehack && - (lastName.equals("name") || - lastName.equals("filename"))) || - !parametersStrict) - ) { - // Add value to previous value - String lastValue = (String)list.get(lastName); - value = lastValue + " " + tk.getValue(); - list.put(lastName, value); - } else { - throw new ParseException("Expected ';', " + - "got \"" + tk.getValue() + "\""); - } - } - } - - if (decodeParameters) { - /* - * After parsing all the parameters, combine all the - * multi-segment parameter values together. - */ - combineMultisegmentNames(false); - } - } - - /** - * Normal users of this class will use simple parameter names. - * In some cases, for example, when processing IMAP protocol - * messages, individual segments of a multi-segment name - * (specified by RFC 2231) will be encountered and passed to - * the {@link #set} method. After all these segments are added - * to this ParameterList, they need to be combined to represent - * the logical parameter name and value. This method will combine - * all segments of multi-segment names.

      - * - * Normal users should never need to call this method. - * - * @since JavaMail 1.5 - */ - public void combineSegments() { - /* - * If we've accumulated any multi-segment names from calls to - * the set method from (e.g.) the IMAP provider, combine the pieces. - * Ignore any parse errors (e.g., from decoding the values) - * because it's too late to report them. - */ - if (decodeParameters && multisegmentNames.size() > 0) { - try { - combineMultisegmentNames(true); - } catch (ParseException pex) { - // too late to do anything about it - } - } - } - - /** - * If the name is an encoded or multi-segment name (or both) - * handle it appropriately, storing the appropriate String - * or Value object. Multi-segment names are stored in the - * main parameter list as an emtpy string as a placeholder, - * replaced later in combineMultisegmentNames with a MultiValue - * object. This causes all pieces of the multi-segment parameter - * to appear in the position of the first seen segment of the - * parameter. - */ - private void putEncodedName(String name, String value) - throws ParseException { - int star = name.indexOf('*'); - if (star < 0) { - // single parameter, unencoded value - list.put(name, value); - } else if (star == name.length() - 1) { - // single parameter, encoded value - name = name.substring(0, star); - Value v = extractCharset(value); - try { - v.value = decodeBytes(v.value, v.charset); - } catch (UnsupportedEncodingException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } - list.put(name, v); - } else { - // multiple segments - String rname = name.substring(0, star); - multisegmentNames.add(rname); - list.put(rname, ""); - - Object v; - if (name.endsWith("*")) { - // encoded value - if (name.endsWith("*0*")) { // first segment - v = extractCharset(value); - } else { - v = new Value(); - ((Value)v).encodedValue = value; - ((Value)v).value = value; // default; decoded later - } - name = name.substring(0, name.length() - 1); - } else { - // unencoded value - v = value; - } - slist.put(name, v); - } - } - - /** - * Iterate through the saved set of names of multi-segment parameters, - * for each parameter find all segments stored in the slist map, - * decode each segment as needed, combine the segments together into - * a single decoded value, and save all segments in a MultiValue object - * in the main list indexed by the parameter name. - */ - private void combineMultisegmentNames(boolean keepConsistentOnFailure) - throws ParseException { - boolean success = false; - try { - Iterator it = multisegmentNames.iterator(); - while (it.hasNext()) { - String name = (String)it.next(); - StringBuffer sb = new StringBuffer(); - MultiValue mv = new MultiValue(); - /* - * Now find all the segments for this name and - * decode each segment as needed. - */ - String charset = null; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - int segment; - for (segment = 0; ; segment++) { - String sname = name + "*" + segment; - Object v = slist.get(sname); - if (v == null) // out of segments - break; - mv.add(v); - try { - if (v instanceof Value) { - Value vv = (Value)v; - if (segment == 0) { - // the first segment specifies the charset - // for all other encoded segments - charset = vv.charset; - } else { - if (charset == null) { - // should never happen - multisegmentNames.remove(name); - break; - } - } - decodeBytes(vv.value, bos); - } else { - bos.write(ASCIIUtility.getBytes((String)v)); - } - } catch (IOException ex) { - // XXX - should never happen - } - slist.remove(sname); - } - if (segment == 0) { - // didn't find any segments at all - list.remove(name); - } else { - try { - if (charset != null) - mv.value = bos.toString(charset); - else - mv.value = bos.toString(); - } catch (UnsupportedEncodingException uex) { - if (decodeParametersStrict) - throw new ParseException(uex.toString()); - // convert as if ASCII - mv.value = bos.toString(0); - } - list.put(name, mv); - } - } - success = true; - } finally { - /* - * If we get here because of an exception that's going to - * be thrown (success == false) from the constructor - * (keepConsistentOnFailure == false), this is all wasted effort. - */ - if (keepConsistentOnFailure || success) { - // we should never end up with anything in slist, - // but if we do, add it all to list - if (slist.size() > 0) { - // first, decode any values that we'll add to the list - Iterator sit = slist.values().iterator(); - while (sit.hasNext()) { - Object v = sit.next(); - if (v instanceof Value) { - Value vv = (Value)v; - try { - vv.value = - decodeBytes(vv.value, vv.charset); - } catch (UnsupportedEncodingException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } - } - } - list.putAll(slist); - } - - // clear out the set of names and segments - multisegmentNames.clear(); - slist.clear(); - } - } - } - - /** - * Return the number of parameters in this list. - * - * @return number of parameters. - */ - public int size() { - return list.size(); - } - - /** - * Returns the value of the specified parameter. Note that - * parameter names are case-insensitive. - * - * @param name parameter name. - * @return Value of the parameter. Returns - * null if the parameter is not - * present. - */ - public String get(String name) { - String value; - Object v = list.get(name.trim().toLowerCase(Locale.ENGLISH)); - if (v instanceof MultiValue) - value = ((MultiValue)v).value; - else if (v instanceof Value) - value = ((Value)v).value; - else - value = (String)v; - return value; - } - - /** - * Set a parameter. If this parameter already exists, it is - * replaced by this new value. - * - * @param name name of the parameter. - * @param value value of the parameter. - */ - public void set(String name, String value) { - name = name.trim().toLowerCase(Locale.ENGLISH); - if (decodeParameters) { - try { - putEncodedName(name, value); - } catch (ParseException pex) { - // ignore it - list.put(name, value); - } - } else - list.put(name, value); - } - - /** - * Set a parameter. If this parameter already exists, it is - * replaced by this new value. If the - * mail.mime.encodeparameters System property - * is true, and the parameter value is non-ASCII, it will be - * encoded with the specified charset, as specified by RFC 2231. - * - * @param name name of the parameter. - * @param value value of the parameter. - * @param charset charset of the parameter value. - * @since JavaMail 1.4 - */ - public void set(String name, String value, String charset) { - if (encodeParameters) { - Value ev = encodeValue(value, charset); - // was it actually encoded? - if (ev != null) - list.put(name.trim().toLowerCase(Locale.ENGLISH), ev); - else - set(name, value); - } else - set(name, value); - } - - /** - * Removes the specified parameter from this ParameterList. - * This method does nothing if the parameter is not present. - * - * @param name name of the parameter. - */ - public void remove(String name) { - list.remove(name.trim().toLowerCase(Locale.ENGLISH)); - } - - /** - * Return an enumeration of the names of all parameters in this - * list. - * - * @return Enumeration of all parameter names in this list. - */ - public Enumeration getNames() { - return new ParamEnum(list.keySet().iterator()); - } - - /** - * Convert this ParameterList into a MIME String. If this is - * an empty list, an empty string is returned. - * - * @return String - */ - public String toString() { - return toString(0); - } - - /** - * Convert this ParameterList into a MIME String. If this is - * an empty list, an empty string is returned. - * - * The 'used' parameter specifies the number of character positions - * already taken up in the field into which the resulting parameter - * list is to be inserted. It's used to determine where to fold the - * resulting parameter list. - * - * @param used number of character positions already used, in - * the field into which the parameter list is to - * be inserted. - * @return String - */ - public String toString(int used) { - ToStringBuffer sb = new ToStringBuffer(used); - Iterator e = list.keySet().iterator(); - - while (e.hasNext()) { - String name = (String)e.next(); - Object v = list.get(name); - if (v instanceof MultiValue) { - MultiValue vv = (MultiValue)v; - String ns = name + "*"; - for (int i = 0; i < vv.size(); i++) { - Object va = vv.get(i); - if (va instanceof Value) - sb.addNV(ns + i + "*", ((Value)va).encodedValue); - else - sb.addNV(ns + i, (String)va); - } - } else if (v instanceof Value) - sb.addNV(name + "*", ((Value)v).encodedValue); - else - sb.addNV(name, (String)v); - } - return sb.toString(); - } - - /** - * A special wrapper for a StringBuffer that keeps track of the - * number of characters used in a line, wrapping to a new line - * as necessary; for use by the toString method. - */ - private static class ToStringBuffer { - private int used; // keep track of how much used on current line - private StringBuffer sb = new StringBuffer(); - - public ToStringBuffer(int used) { - this.used = used; - } - - public void addNV(String name, String value) { - value = quote(value); - sb.append("; "); - used += 2; - int len = name.length() + value.length() + 1; - if (used + len > 76) { // overflows ... - sb.append("\r\n\t"); // .. start new continuation line - used = 8; // account for the starting char - } - sb.append(name).append('='); - used += name.length() + 1; - if (used + value.length() > 76) { // still overflows ... - // have to fold value - String s = MimeUtility.fold(used, value); - sb.append(s); - int lastlf = s.lastIndexOf('\n'); - if (lastlf >= 0) // always true - used += s.length() - lastlf - 1; - else - used += s.length(); - } else { - sb.append(value); - used += value.length(); - } - } - - public String toString() { - return sb.toString(); - } - } - - // Quote a parameter value token if required. - private static String quote(String value) { - return MimeUtility.quote(value, HeaderTokenizer.MIME); - } - - private static final char hex[] = { - '0','1', '2', '3', '4', '5', '6', '7', - '8','9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - - /** - * Encode a parameter value, if necessary. - * If the value is encoded, a Value object is returned. - * Otherwise, null is returned. - * XXX - Could return a MultiValue object if parameter value is too long. - */ - private static Value encodeValue(String value, String charset) { - if (MimeUtility.checkAscii(value) == MimeUtility.ALL_ASCII) - return null; // no need to encode it - - byte[] b; // charset encoded bytes from the string - try { - b = value.getBytes(MimeUtility.javaCharset(charset)); - } catch (UnsupportedEncodingException ex) { - return null; - } - StringBuffer sb = new StringBuffer(b.length + charset.length() + 2); - sb.append(charset).append("''"); - for (int i = 0; i < b.length; i++) { - char c = (char)(b[i] & 0xff); - // do we need to encode this character? - if (c <= ' ' || c >= 0x7f || c == '*' || c == '\'' || c == '%' || - HeaderTokenizer.MIME.indexOf(c) >= 0) { - sb.append('%').append(hex[c>>4]).append(hex[c&0xf]); - } else - sb.append(c); - } - Value v = new Value(); - v.charset = charset; - v.value = value; - v.encodedValue = sb.toString(); - return v; - } - - /** - * Extract charset and encoded value. - * Value will be decoded later. - */ - private static Value extractCharset(String value) throws ParseException { - Value v = new Value(); - v.value = v.encodedValue = value; - try { - int i = value.indexOf('\''); - if (i <= 0) { - if (decodeParametersStrict) - throw new ParseException( - "Missing charset in encoded value: " + value); - return v; // not encoded correctly? return as is. - } - String charset = value.substring(0, i); - int li = value.indexOf('\'', i + 1); - if (li < 0) { - if (decodeParametersStrict) - throw new ParseException( - "Missing language in encoded value: " + value); - return v; // not encoded correctly? return as is. - } - String lang = value.substring(i + 1, li); - v.value = value.substring(li + 1); - v.charset = charset; - } catch (NumberFormatException nex) { - if (decodeParametersStrict) - throw new ParseException(nex.toString()); - } catch (StringIndexOutOfBoundsException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } - return v; - } - - /** - * Decode the encoded bytes in value using the specified charset. - */ - private static String decodeBytes(String value, String charset) - throws ParseException, UnsupportedEncodingException { - /* - * Decode the ASCII characters in value - * into an array of bytes, and then convert - * the bytes to a String using the specified - * charset. We'll never need more bytes than - * encoded characters, so use that to size the - * array. - */ - byte[] b = new byte[value.length()]; - int i, bi; - for (i = 0, bi = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '%') { - try { - String hex = value.substring(i + 1, i + 3); - c = (char)Integer.parseInt(hex, 16); - i += 2; - } catch (NumberFormatException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } catch (StringIndexOutOfBoundsException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } - } - b[bi++] = (byte)c; - } - charset = MimeUtility.javaCharset(charset); - if (charset == null) - charset = MimeUtility.getDefaultJavaCharset(); - return new String(b, 0, bi, charset); - } - - /** - * Decode the encoded bytes in value and write them to the OutputStream. - */ - private static void decodeBytes(String value, OutputStream os) - throws ParseException, IOException { - /* - * Decode the ASCII characters in value - * and write them to the stream. - */ - int i; - for (i = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '%') { - try { - String hex = value.substring(i + 1, i + 3); - c = (char)Integer.parseInt(hex, 16); - i += 2; - } catch (NumberFormatException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } catch (StringIndexOutOfBoundsException ex) { - if (decodeParametersStrict) - throw new ParseException(ex.toString()); - } - } - os.write((byte)c); - } - } -} diff --git a/src/main/java/javax/mail/internet/ParseException.java b/src/main/java/javax/mail/internet/ParseException.java deleted file mode 100644 index d7e42beb..00000000 --- a/src/main/java/javax/mail/internet/ParseException.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import javax.mail.MessagingException; - -/** - * The exception thrown due to an error in parsing RFC822 - * or MIME headers - * - * @author John Mani - */ - -public class ParseException extends MessagingException { - - private static final long serialVersionUID = 7649991205183658089L; - - /** - * Constructs a ParseException with no detail message. - */ - public ParseException() { - super(); - } - - /** - * Constructs a ParseException with the specified detail message. - * @param s the detail message - */ - public ParseException(String s) { - super(s); - } -} diff --git a/src/main/java/javax/mail/internet/PreencodedMimeBodyPart.java b/src/main/java/javax/mail/internet/PreencodedMimeBodyPart.java deleted file mode 100644 index 68d3fa21..00000000 --- a/src/main/java/javax/mail/internet/PreencodedMimeBodyPart.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.io.*; -import java.util.Enumeration; -import javax.mail.*; - -import com.sun.mail.util.LineOutputStream; - -/** - * A MimeBodyPart that handles data that has already been encoded. - * This class is useful when constructing a message and attaching - * data that has already been encoded (for example, using base64 - * encoding). The data may have been encoded by the application, - * or may have been stored in a file or database in encoded form. - * The encoding is supplied when this object is created. The data - * is attached to this object in the usual fashion, by using the - * setText, setContent, or - * setDataHandler methods. - * - * @since JavaMail 1.4 - */ - -public class PreencodedMimeBodyPart extends MimeBodyPart { - private String encoding; - - /** - * Create a PreencodedMimeBodyPart that assumes the data is - * encoded using the specified encoding. The encoding must - * be a MIME supported Content-Transfer-Encoding. - */ - public PreencodedMimeBodyPart(String encoding) { - this.encoding = encoding; - } - - /** - * Returns the content transfer encoding specified when - * this object was created. - */ - public String getEncoding() throws MessagingException { - return encoding; - } - - /** - * Output the body part as an RFC 822 format stream. - * - * @exception MessagingException - * @exception IOException if an error occurs writing to the - * stream or if an error is generated - * by the javax.activation layer. - * @see javax.activation.DataHandler#writeTo - */ - public void writeTo(OutputStream os) - throws IOException, MessagingException { - - // see if we already have a LOS - LineOutputStream los = null; - if (os instanceof LineOutputStream) { - los = (LineOutputStream) os; - } else { - los = new LineOutputStream(os); - } - - // First, write out the header - Enumeration hdrLines = getAllHeaderLines(); - while (hdrLines.hasMoreElements()) - los.writeln((String)hdrLines.nextElement()); - - // The CRLF separator between header and content - los.writeln(); - - // Finally, the content, already encoded. - getDataHandler().writeTo(os); - os.flush(); - } - - /** - * Force the Content-Transfer-Encoding header to use - * the encoding that was specified when this object was created. - */ - protected void updateHeaders() throws MessagingException { - super.updateHeaders(); - MimeBodyPart.setEncoding(this, encoding); - } -} diff --git a/src/main/java/javax/mail/internet/SharedInputStream.java b/src/main/java/javax/mail/internet/SharedInputStream.java deleted file mode 100644 index c9f26b8d..00000000 --- a/src/main/java/javax/mail/internet/SharedInputStream.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.io.*; - -/** - * An InputStream that is backed by data that can be shared by multiple - * readers may implement this interface. This allows users of such an - * InputStream to determine the current position in the InputStream, and - * to create new InputStreams representing a subset of the data in the - * original InputStream. The new InputStream will access the same - * underlying data as the original, without copying the data.

      - * - * Note that implementations of this interface must ensure that the - * close method does not close any underlying stream - * that might be shared by multiple instances of SharedInputStream - * until all shared instances have been closed. - * - * @author Bill Shannon - * @since JavaMail 1.2 - */ - -public interface SharedInputStream { - /** - * Return the current position in the InputStream, as an - * offset from the beginning of the InputStream. - * - * @return the current position - */ - public long getPosition(); - - /** - * Return a new InputStream representing a subset of the data - * from this InputStream, starting at start (inclusive) - * up to end (exclusive). start must be - * non-negative. If end is -1, the new stream ends - * at the same place as this stream. The returned InputStream - * will also implement the SharedInputStream interface. - * - * @param start the starting position - * @param end the ending position + 1 - * @return the new stream - */ - public InputStream newStream(long start, long end); -} diff --git a/src/main/java/javax/mail/internet/UniqueValue.java b/src/main/java/javax/mail/internet/UniqueValue.java deleted file mode 100644 index 93e35a5b..00000000 --- a/src/main/java/javax/mail/internet/UniqueValue.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.internet; - -import java.net.*; -import javax.mail.Session; - -/** - * This is a utility class that generates unique values. The generated - * String contains only US-ASCII characters and hence is safe for use - * in RFC822 headers.

      - * - * This is a package private class. - * - * @author John Mani - * @author Max Spivak - * @author Bill Shannon - */ - -class UniqueValue { - /** - * A global unique number, to ensure uniqueness of generated strings. - */ - private static int id = 0; - - /** - * Get a unique value for use in a multipart boundary string. - * - * This implementation generates it by concatenating a global - * part number, a newly created object's hashCode(), - * and the current time (in milliseconds). - */ - public static String getUniqueBoundaryValue() { - StringBuffer s = new StringBuffer(); - - // Unique string is ----=_Part__. - s.append("----=_Part_").append(getUniqueId()).append("_"). - append(s.hashCode()).append('.'). - append(System.currentTimeMillis()); - return s.toString(); - } - - /** - * Get a unique value for use in a Message-ID. - * - * This implementation generates it by concatenating a newly - * created object's hashCode(), a global ID - * (incremented on every use), the current - * time (in milliseconds), the string "JavaMail", and - * this user's local address generated by - * InternetAddress.getLocalAddress(). - * (The address defaults to "javamailuser@localhost" if - * getLocalAddress() returns null.) - * - * @param ssn Session object used to get the local address - * @see javax.mail.internet.InternetAddress - */ - public static String getUniqueMessageIDValue(Session ssn) { - String suffix = null; - - InternetAddress addr = InternetAddress.getLocalAddress(ssn); - if (addr != null) - suffix = addr.getAddress(); - else { - suffix = "javamailuser@localhost"; // worst-case default - } - - StringBuffer s = new StringBuffer(); - - // Unique string is ...JavaMail. - s.append(s.hashCode()).append('.').append(getUniqueId()).append('.'). - append(System.currentTimeMillis()).append('.'). - append("JavaMail."). - append(suffix); - return s.toString(); - } - - /** - * Ensure ID is unique by synchronizing access. - * XXX - Could use AtomicInteger.getAndIncrement() in J2SE 5.0. - */ - private static synchronized int getUniqueId() { - return id++; - } -} diff --git a/src/main/java/javax/mail/internet/package.html b/src/main/java/javax/mail/internet/package.html deleted file mode 100644 index e48acf6f..00000000 --- a/src/main/java/javax/mail/internet/package.html +++ /dev/null @@ -1,502 +0,0 @@ - - - - - - - - -Classes specific to Internet mail systems. -This package supports features that are specific to Internet mail systems -based on the MIME standard -(RFC 2045, -RFC 2046, and -RFC 2047). -The IMAP, SMTP, and POP3 protocols use -{@link javax.mail.internet.MimeMessage MimeMessages}. -

      -The JavaMail API specification requires support for the following properties, -which must be set in the System properties. -The properties are always set as strings; the Type column describes -how the string is interpreted. For example, use (in J2SE 1.2 and newer) -

      -	System.setProperty("mail.mime.address.strict", "false");
      -
      -to set the mail.mime.address.strict property, -which is of type boolean. -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameTypeDescription
      mail.mime.address.strictboolean -The mail.mime.address.strict session property controls -the parsing of address headers. By default, strict parsing of address -headers is done. If this property is set to "false", -strict parsing is not done and many illegal addresses that sometimes -occur in real messages are allowed. See the InternetAddress -class for details. -
      mail.mime.charsetString -The mail.mime.charset System property can -be used to specify the default MIME charset to use for encoded words -and text parts that don't otherwise specify a charset. Normally, the -default MIME charset is derived from the default Java charset, as -specified in the file.encoding System property. Most -applications will have no need to explicitly set the default MIME -charset. In cases where the default MIME charset to be used for -mail messages is different than the charset used for files stored on -the system, this property should be set. -
      mail.mime.decodetext.strictboolean -The mail.mime.decodetext.strict property controls -decoding of MIME encoded words. The MIME spec requires that encoded -words start at the beginning of a whitespace separated word. Some -mailers incorrectly include encoded words in the middle of a word. -If the mail.mime.decodetext.strict System property is -set to "false", an attempt will be made to decode these -illegal encoded words. The default is true. -
      mail.mime.encodeeol.strictboolean -The mail.mime.encodeeol.strict property controls the -choice of Content-Transfer-Encoding for MIME parts that are not of -type "text". Often such parts will contain textual data for which -an encoding that allows normal end of line conventions is appropriate. -In rare cases, such a part will appear to contain entirely textual -data, but will require an encoding that preserves CR and LF characters -without change. If the mail.mime.encodeeol.strict -System property is set to "true", such an encoding will -be used when necessary. The default is false. -
      mail.mime.decodefilenameboolean -If set to "true", the getFileName method -uses the MimeUtility -method decodeText to decode any -non-ASCII characters in the filename. Note that this decoding -violates the MIME specification, but is useful for interoperating -with some mail clients that use this convention. -The default is false. -
      mail.mime.encodefilenameboolean -If set to "true", the setFileName method -uses the MimeUtility -method encodeText to encode any -non-ASCII characters in the filename. Note that this encoding -violates the MIME specification, but is useful for interoperating -with some mail clients that use this convention. -The default is false. -
      mail.mime.decodeparametersboolean -If set to "false", non-ASCII parameters in a -ParameterList, e.g., in a Content-Type header, -will not be decoded as specified by -RFC 2231. -The default is true. -
      mail.mime.encodeparametersboolean -If set to "false", non-ASCII parameters in a -ParameterList, e.g., in a Content-Type header, -will not be encoded as specified by -RFC 2231. -The default is true. -
      mail.mime.multipart. ignoremissingendboundaryboolean -Normally, when parsing a multipart MIME message, a message that is -missing the final end boundary line is not considered an error. -The data simply ends at the end of the input. Note that messages -of this form violate the MIME specification. If the property -mail.mime.multipart.ignoremissingendboundary is set -to false, such messages are considered an error and a -MesagingException will be thrown when parsing such a -message. -
      mail.mime.multipart. ignoremissingboundaryparameterboolean -If the Content-Type header for a multipart content does not have -a boundary parameter, the multipart parsing code -will look for the first line in the content that looks like a -boundary line and extract the boundary parameter from the line. -If this property is set to "false", a -MessagingException will be thrown if the Content-Type -header doesn't specify a boundary parameter. -The default is true. -
      mail.mime.multipart. ignoreexistingboundaryparameterboolean -Normally the boundary parameter in the Content-Type header of a multipart -body part is used to specify the separator between parts of the multipart -body. This System property may be set to "true" to cause -the parser to look for a line in the multipart body that looks like a -boundary line and use that value as the separator between subsequent parts. -This may be useful in cases where a broken anti-virus product has rewritten -the message incorrectly such that the boundary parameter and the actual -boundary value no longer match. -The default value of this property is false. -
      mail.mime.multipart. allowemptyboolean -Normally, when writing out a MimeMultipart that contains no body -parts, or when trying to parse a multipart message with no body parts, -a MessagingException is thrown. The MIME spec does not allow -multipart content with no body parts. This -System property may be set to "true" to override this behavior. -When writing out such a MimeMultipart, a single empty part will be -included. When reading such a multipart, a MimeMultipart will be created -with no body parts. -The default value of this property is false. -
      - - - -

      -The following properties are supported by Sun's implementation of -JavaMail, but are not currently a required part of the specification. -As above, these must be set as System properties. -The names, types, defaults, and semantics of these properties may -change in future releases. -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameTypeDescription
      mail.mime.base64.ignoreerrorsboolean -If set to "true", the BASE64 decoder will ignore errors -in the encoded data, returning EOF. This may be useful when dealing -with improperly encoded messages that contain extraneous data at the -end of the encoded stream. Note however that errors anywhere in the -stream will cause the decoder to stop decoding so this should be used -with extreme caution. The default is false. -
      mail.mime.foldtextboolean -If set to "true", header fields containing just text -such as the Subject and Content-Description -header fields, and long parameter values in structured headers such -as Content-Type will be folded (broken into 76 character lines) -when set and unfolded when read. The default is true. -
      mail.mime.setcontenttypefilenameboolean -If set to "true", the setFileName method -will also set the name parameter on the Content-Type -header to the specified filename. This supports interoperability with -some old mail clients. The default is true. -
      mail.mime.setdefaulttextcharsetboolean -When updating the headers of a message, a body -part with a text content type but no charset -parameter will have a charset parameter added to it -if this property is set to "true". -The default is true. -
      mail.mime.parameters.strictboolean -If set to false, when reading a message, parameter values in header fields -such as Content-Type and Content-Disposition -are allowed to contain whitespace and other special characters without -being quoted; the parameter value ends at the next semicolon. -If set to true (the default), parameter values are required to conform -to the MIME specification and must be quoted if they contain whitespace -or special characters. -
      mail.mime.applefilenamesboolean -Apple Mail incorrectly encodes filenames that contain spaces, -forgetting to quote the parameter value. If this property is -set to "true", JavaMail will try to detect this -situation when parsing parameters and work around it. -The default is false. -Note that this property handles a subset of the cases handled -by setting the mail.mime.parameters.strict property to false. -This property will likely be removed in a future release. -
      mail.mime.windowsfilenamesboolean -Internet Explorer 6 incorrectly includes a complete pathname -in the filename parameter of the Content-Disposition header -for uploaded files, and fails to properly escape the backslashes -in the pathname. If this property is -set to "true", JavaMail will preserve all backslashes -in the "filename" and "name" parameters of any MIME header. -The default is false. -Note that this is a violation of the MIME specification but may -be useful when using JavaMail to parse HTTP messages for uploaded -files sent by IE6. -
      mail.mime. ignoreunknownencodingboolean -If set to "true", an unknown value in the -Content-Transfer-Encoding header will be ignored -when reading a message and an encoding of "8bit" will be assumed. -If set to "false", an exception is thrown for an -unknown encoding value. The default is false. -
      mail.mime.uudecode. ignoreerrorsboolean -If set to "true", errors in the encoded format of a -uuencoded document will be ignored when reading a message part. -If set to "false", an exception is thrown for an -incorrectly encoded message part. The default is false. -
      mail.mime.uudecode. ignoremissingbeginendboolean -If set to "true", a missing "being" or "end" line in a -uuencoded document will be ignored when reading a message part. -If set to "false", an exception is thrown for a -uuencoded message part without the required "begin" and "end" lines. -The default is false. -
      mail.mime. ignorewhitespacelinesboolean -Normally the header of a MIME part is separated from the body by an empty -line. This System property may be set to "true" to cause -the parser to consider a line containing only whitespace to be an empty -line. The default value of this property is false. -
      mail.mime. ignoremultipartencodingboolean -The MIME spec does not allow body parts of type multipart/* to be encoded. -The Content-Transfer-Encoding header is ignored in this case. -Setting this System property to "false" will -cause the Content-Transfer-Encoding header to be honored for multipart -content. -The default value of this property is true. -
      mail.mime.allowencodedmessagesboolean -The MIME spec does not allow body parts of type message/* to be encoded. -The Content-Transfer-Encoding header is ignored in this case. -Some versions of Microsoft Outlook will incorrectly encode message -attachments. Setting this System property to "true" will -cause the Content-Transfer-Encoding header to be honored for message -attachments. -The default value of this property is false. -
      mail.mime.contenttypehandlerString -In some cases JavaMail is unable to process messages with an invalid -Content-Type header. The header may have incorrect syntax or other -problems. This property specifies the name of a class that will be -used to clean up the Content-Type header value before JavaMail uses it. -The class must have a method with this signature: -public static String cleanContentType(MimePart mp, String contentType) -Whenever JavaMail accesses the Content-Type header of a message, it -will pass the value to this method and use the returned value instead. -The value may be null if the Content-Type header isn't present. -Returning null will cause the default Content-Type to be used. -The MimePart may be used to access other headers of the message part -to determine how to correct the Content-Type. -Note that the Content-Type handler doesn't affect the -getHeader method, which still returns the raw header value. -Note also that the handler doesn't affect the IMAP provider; the IMAP -server is responsible for returning pre-parsed, syntactically correct -Content-Type information. -
      mail.alternatesString -A string containing other email addresses that the current user is known by. -The MimeMessage reply method will eliminate any -of these addresses from the recipient list in the message it constructs, -to avoid sending the reply back to the sender. -
      mail.replyallccboolean -If set to "true", the MimeMessage -reply method will put all recipients except the original -sender in the Cc list of the newly constructed message. -Normally, recipients in the To header of the original -message will also appear in the To list of the newly -constructed message. -
      -

      -The current -implementation of classes in this package log debugging information using -{@link java.util.logging.Logger} as described in the following table: -

      - - - - - - - - - - - - -
      Logger NameLogging LevelPurpose
      javax.mail.internetFINEGeneral debugging output
      - - - diff --git a/src/main/java/javax/mail/package.html b/src/main/java/javax/mail/package.html deleted file mode 100644 index a5e2c482..00000000 --- a/src/main/java/javax/mail/package.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - -The JavaMailTM API -provides classes that model a mail system. -The javax.mail package defines classes that are common to -all mail systems. -The javax.mail.internet package defines classes that are specific -to mail systems based on internet standards such as MIME, SMTP, POP3, and IMAP. -The JavaMail API includes the javax.mail package and subpackages. -

      -For an overview of the JavaMail API, read the JavaMail specification - -included in the download bundle or - -available on the JavaMail web site. -

      -The code to send a plain text message can be as simple as the following: -

      -    Properties props = new Properties();
      -    props.put("mail.smtp.host", "my-mail-server");
      -    Session session = Session.getInstance(props, null);
      -
      -    try {
      -	MimeMessage msg = new MimeMessage(session);
      -	msg.setFrom("me@example.com");
      -	msg.setRecipients(Message.RecipientType.TO,
      -			  "you@example.com");
      -	msg.setSubject("JavaMail hello world example");
      -	msg.setSentDate(new Date());
      -	msg.setText("Hello, world!\n");
      -	Transport.send(msg, "me@example.com", "my-password");
      -    } catch (MessagingException mex) {
      -	System.out.println("send failed, exception: " + mex);
      -    }
      -
      -The JavaMail download bundle contains many more complete examples -in the "demo" directory. -

      -Don't forget to see the - -JavaMail API FAQ -for answers to the most common questions. -The -JavaMail web site -contains many additional resources. -

      -The JavaMail API supports the following standard properties, -which may be set in the Session object, or in the -Properties object used to create the Session object. -The properties are always set as strings; the Type column describes -how the string is interpreted. For example, use -

      -	props.put("mail.debug", "true");
      -
      -to set the mail.debug property, which is of type boolean. -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameTypeDescription
      mail.debugboolean -The initial debug mode. -Default is false. -
      mail.fromString -The return email address of the current user, used by the -InternetAddress method getLocalAddress. -
      mail.mime.address.strictboolean -The MimeMessage class uses the InternetAddress method -parseHeader to parse headers in messages. This property -controls the strict flag passed to the parseHeader -method. The default is true. -
      mail.hostString -The default host name of the mail server for both Stores and Transports. -Used if the mail.protocol.host property isn't set. -
      mail.store.protocolString -Specifies the default message access protocol. The -Session method getStore() returns a Store -object that implements this protocol. By default the first Store -provider in the configuration files is returned. -
      mail.transport.protocolString -Specifies the default message transport protocol. The -Session method getTransport() returns a Transport -object that implements this protocol. By default the first Transport -provider in the configuration files is returned. -
      mail.userString -The default user name to use when connecting to the mail server. -Used if the mail.protocol.user property isn't set. -
      mail.protocol.classString -Specifies the fully qualified class name of the provider for the -specified protocol. Used in cases where more than one provider -for a given protocol exists; this property can be used to specify -which provider to use by default. The provider must still be listed -in a configuration file. -
      mail.protocol.hostString -The host name of the mail server for the specified protocol. -Overrides the mail.host property. -
      mail.protocol.portint -The port number of the mail server for the specified protocol. -If not specified the protocol's default port number is used. -
      mail.protocol.userString -The user name to use when connecting to mail servers -using the specified protocol. -Overrides the mail.user property. -
      - -

      -The following properties are supported by Sun's implementation of -JavaMail, but are not currently a required part of the specification. -The names, types, defaults, and semantics of these properties may -change in future releases. -

      - - - - - - - - - - - - - - - - - - - -
      NameTypeDescription
      mail.debug.authboolean -Include protocol authentication commands (including usernames and passwords) -in the debug output. -Default is false. -
      mail.transport.protocol.address-typeString -Specifies the default message transport protocol for the specified address type. -The Session method getTransport(Address) returns a -Transport object that implements this protocol when the address is of the -specified type (e.g., "rfc822" for standard internet addresses). -By default the first Transport configured for that address type is used. -This property can be used to override the behavior of the -{@link javax.mail.Transport#send send} method of the -{@link javax.mail.Transport Transport} class so that (for example) the "smtps" -protocol is used instead of the "smtp" protocol by setting the property -mail.transport.protocol.rfc822 to "smtps". -
      - -

      -The JavaMail API also supports several System properties; -see the {@link javax.mail.internet} package documentation -for details. - -

      -The JavaMail reference -implementation from Sun includes protocol providers in subpackages of -com.sun.mail. Note that the APIs to these protocol -providers are not part of the standard JavaMail API. Portable -programs will not use these APIs. -

      -Nonportable programs may use the APIs of the Sun protocol providers -by (for example) casting a returned Folder object to a -com.sun.mail.imap.IMAPFolder object. Similarly for -Store and Message objects returned from the -standard JavaMail APIs. -

      -The Sun protocol providers also support properties that are specific to -those providers. The package documentation for the -{@link com.sun.mail.imap IMAP}, {@link com.sun.mail.pop3 POP3}, -and {@link com.sun.mail.smtp SMTP} packages provide details. -

      -In addition to printing debugging output as controlled by the -{@link javax.mail.Session Session} configuration, the current -implementation of classes in this package log the same information using -{@link java.util.logging.Logger} as described in the following table: -

      - - - - - - - - - - - - - - - - - - -
      Logger NameLogging LevelPurpose
      javax.mailCONFIGConfiguration of the Session
      javax.mailFINEGeneral debugging output
      - - - diff --git a/src/main/java/javax/mail/search/AddressStringTerm.java b/src/main/java/javax/mail/search/AddressStringTerm.java deleted file mode 100644 index 9b6d00eb..00000000 --- a/src/main/java/javax/mail/search/AddressStringTerm.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; -import javax.mail.Address; -import javax.mail.internet.InternetAddress; - -/** - * This abstract class implements string comparisons for Message - * addresses.

      - * - * Note that this class differs from the AddressTerm class - * in that this class does comparisons on address strings rather than - * Address objects. - * - * @since JavaMail 1.1 - */ - -public abstract class AddressStringTerm extends StringTerm { - - private static final long serialVersionUID = 3086821234204980368L; - - /** - * Constructor. - * - * @param pattern the address pattern to be compared. - */ - protected AddressStringTerm(String pattern) { - super(pattern, true); // we need case-insensitive comparison. - } - - /** - * Check whether the address pattern specified in the constructor is - * a substring of the string representation of the given Address - * object.

      - * - * Note that if the string representation of the given Address object - * contains charset or transfer encodings, the encodings must be - * accounted for, during the match process.

      - * - * @param a The comparison is applied to this Address object. - * @return true if the match succeeds, otherwise false. - */ - protected boolean match(Address a) { - if (a instanceof InternetAddress) { - InternetAddress ia = (InternetAddress)a; - // We dont use toString() to get "a"'s String representation, - // because InternetAddress.toString() returns a RFC 2047 - // encoded string, which isn't what we need here. - - return super.match(ia.toUnicodeString()); - } else - return super.match(a.toString()); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof AddressStringTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/AddressTerm.java b/src/main/java/javax/mail/search/AddressTerm.java deleted file mode 100644 index 1a3aaa69..00000000 --- a/src/main/java/javax/mail/search/AddressTerm.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Address; - -/** - * This class implements Message Address comparisons. - * - * @author Bill Shannon - * @author John Mani - */ - -public abstract class AddressTerm extends SearchTerm { - /** - * The address. - * - * @serial - */ - protected Address address; - - private static final long serialVersionUID = 2005405551929769980L; - - protected AddressTerm(Address address) { - this.address = address; - } - - /** - * Return the address to match with. - */ - public Address getAddress() { - return address; - } - - /** - * Match against the argument Address. - */ - protected boolean match(Address a) { - return (a.equals(address)); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof AddressTerm)) - return false; - AddressTerm at = (AddressTerm)obj; - return at.address.equals(this.address); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return address.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/AndTerm.java b/src/main/java/javax/mail/search/AndTerm.java deleted file mode 100644 index 2459d09c..00000000 --- a/src/main/java/javax/mail/search/AndTerm.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements the logical AND operator on individual - * SearchTerms. - * - * @author Bill Shannon - * @author John Mani - */ -public final class AndTerm extends SearchTerm { - - /** - * The array of terms on which the AND operator should be - * applied. - * - * @serial - */ - private SearchTerm[] terms; - - private static final long serialVersionUID = -3583274505380989582L; - - /** - * Constructor that takes two terms. - * - * @param t1 first term - * @param t2 second term - */ - public AndTerm(SearchTerm t1, SearchTerm t2) { - terms = new SearchTerm[2]; - terms[0] = t1; - terms[1] = t2; - } - - /** - * Constructor that takes an array of SearchTerms. - * - * @param t array of terms - */ - public AndTerm(SearchTerm[] t) { - terms = new SearchTerm[t.length]; // clone the array - for (int i = 0; i < t.length; i++) - terms[i] = t[i]; - } - - /** - * Return the search terms. - */ - public SearchTerm[] getTerms() { - return (SearchTerm[])terms.clone(); - } - - /** - * The AND operation.

      - * - * The terms specified in the constructor are applied to - * the given object and the AND operator is applied to their results. - * - * @param msg The specified SearchTerms are applied to this Message - * and the AND operator is applied to their results. - * @return true if the AND succeds, otherwise false - */ - public boolean match(Message msg) { - for (int i=0; i < terms.length; i++) - if (!terms[i].match(msg)) - return false; - return true; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof AndTerm)) - return false; - AndTerm at = (AndTerm)obj; - if (at.terms.length != terms.length) - return false; - for (int i=0; i < terms.length; i++) - if (!terms[i].equals(at.terms[i])) - return false; - return true; - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - int hash = 0; - for (int i=0; i < terms.length; i++) - hash += terms[i].hashCode(); - return hash; - } -} diff --git a/src/main/java/javax/mail/search/BodyTerm.java b/src/main/java/javax/mail/search/BodyTerm.java deleted file mode 100644 index be065b80..00000000 --- a/src/main/java/javax/mail/search/BodyTerm.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.*; - -/** - * This class implements searches on a message body. - * All parts of the message that are of MIME type "text/*" are searched. - * The pattern is a simple string that must appear as a substring in - * the message body. - * - * @author Bill Shannon - * @author John Mani - */ -public final class BodyTerm extends StringTerm { - - private static final long serialVersionUID = -4888862527916911385L; - - /** - * Constructor - * @param pattern The String to search for - */ - public BodyTerm(String pattern) { - // Note: comparison is case-insensitive - super(pattern); - } - - /** - * The match method. - * - * @param msg The pattern search is applied on this Message's body - * @return true if the pattern is found; otherwise false - */ - public boolean match(Message msg) { - return matchPart(msg); - } - - /** - * Search all the parts of the message for any text part - * that matches the pattern. - */ - private boolean matchPart(Part p) { - try { - /* - * Using isMimeType to determine the content type avoids - * fetching the actual content data until we need it. - */ - if (p.isMimeType("text/*")) { - String s = (String)p.getContent(); - if (s == null) - return false; - /* - * We invoke our superclass' (i.e., StringTerm) match method. - * Note however that StringTerm.match() is not optimized - * for substring searches in large string buffers. We really - * need to have a StringTerm subclass, say BigStringTerm, - * with its own match() method that uses a better algorithm .. - * and then subclass BodyTerm from BigStringTerm. - */ - return super.match(s); - } else if (p.isMimeType("multipart/*")) { - Multipart mp = (Multipart)p.getContent(); - int count = mp.getCount(); - for (int i = 0; i < count; i++) - if (matchPart(mp.getBodyPart(i))) - return true; - } else if (p.isMimeType("message/rfc822")) { - return matchPart((Part)p.getContent()); - } - } catch (Exception ex) { - } - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof BodyTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/ComparisonTerm.java b/src/main/java/javax/mail/search/ComparisonTerm.java deleted file mode 100644 index f7148e14..00000000 --- a/src/main/java/javax/mail/search/ComparisonTerm.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -/** - * This class models the comparison operator. This is an abstract - * class; subclasses implement comparisons for different datatypes. - * - * @author Bill Shannon - * @author John Mani - */ -public abstract class ComparisonTerm extends SearchTerm { - public static final int LE = 1; - public static final int LT = 2; - public static final int EQ = 3; - public static final int NE = 4; - public static final int GT = 5; - public static final int GE = 6; - - /** - * The comparison. - * - * @serial - */ - protected int comparison; - - private static final long serialVersionUID = 1456646953666474308L; - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof ComparisonTerm)) - return false; - ComparisonTerm ct = (ComparisonTerm)obj; - return ct.comparison == this.comparison; - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return comparison; - } -} diff --git a/src/main/java/javax/mail/search/DateTerm.java b/src/main/java/javax/mail/search/DateTerm.java deleted file mode 100644 index 12672241..00000000 --- a/src/main/java/javax/mail/search/DateTerm.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import java.util.Date; - -/** - * This class implements comparisons for Dates - * - * @author Bill Shannon - * @author John Mani - */ -public abstract class DateTerm extends ComparisonTerm { - /** - * The date. - * - * @serial - */ - protected Date date; - - private static final long serialVersionUID = 4818873430063720043L; - - /** - * Constructor. - * @param comparison the comparison type - * @param date The Date to be compared against - */ - protected DateTerm(int comparison, Date date) { - this.comparison = comparison; - this.date = date; - } - - /** - * Return the Date to compare with. - */ - public Date getDate() { - return new Date(date.getTime()); - } - - /** - * Return the type of comparison. - */ - public int getComparison() { - return comparison; - } - - /** - * The date comparison method. - * - * @param d the date in the constructor is compared with this date - * @return true if the dates match, otherwise false - */ - protected boolean match(Date d) { - switch (comparison) { - case LE: - return d.before(date) || d.equals(date); - case LT: - return d.before(date); - case EQ: - return d.equals(date); - case NE: - return !d.equals(date); - case GT: - return d.after(date); - case GE: - return d.after(date) || d.equals(date); - default: - return false; - } - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof DateTerm)) - return false; - DateTerm dt = (DateTerm)obj; - return dt.date.equals(this.date) && super.equals(obj); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return date.hashCode() + super.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/FlagTerm.java b/src/main/java/javax/mail/search/FlagTerm.java deleted file mode 100644 index 090cc2ee..00000000 --- a/src/main/java/javax/mail/search/FlagTerm.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.*; - -/** - * This class implements comparisons for Message Flags. - * - * @author Bill Shannon - * @author John Mani - */ -public final class FlagTerm extends SearchTerm { - - /** - * Indicates whether to test for the presence or - * absence of the specified Flag. If true, - * then test whether all the specified flags are present, else - * test whether all the specified flags are absent. - * - * @serial - */ - private boolean set; - - /** - * Flags object containing the flags to test. - * - * @serial - */ - private Flags flags; - - private static final long serialVersionUID = -142991500302030647L; - - /** - * Constructor. - * - * @param flags Flags object containing the flags to check for - * @param set the flag setting to check for - */ - public FlagTerm(Flags flags, boolean set) { - this.flags = flags; - this.set = set; - } - - /** - * Return the Flags to test. - */ - public Flags getFlags() { - return (Flags)flags.clone(); - } - - /** - * Return true if testing whether the flags are set. - */ - public boolean getTestSet() { - return set; - } - - /** - * The comparison method. - * - * @param msg The flag comparison is applied to this Message - * @return true if the comparson succeeds, otherwise false. - */ - public boolean match(Message msg) { - - try { - Flags f = msg.getFlags(); - if (set) { // This is easy - if (f.contains(flags)) - return true; - else - return false; - } - - // Return true if ALL flags in the passed in Flags - // object are NOT set in this Message. - - // Got to do this the hard way ... - Flags.Flag[] sf = flags.getSystemFlags(); - - // Check each flag in the passed in Flags object - for (int i = 0; i < sf.length; i++) { - if (f.contains(sf[i])) - // this flag IS set in this Message, get out. - return false; - } - - String[] s = flags.getUserFlags(); - - // Check each flag in the passed in Flags object - for (int i = 0; i < s.length; i++) { - if (f.contains(s[i])) - // this flag IS set in this Message, get out. - return false; - } - - return true; - - } catch (Exception e) { - return false; - } - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof FlagTerm)) - return false; - FlagTerm ft = (FlagTerm)obj; - return ft.set == this.set && ft.flags.equals(this.flags); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return set ? flags.hashCode() : ~flags.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/FromStringTerm.java b/src/main/java/javax/mail/search/FromStringTerm.java deleted file mode 100644 index 488687c7..00000000 --- a/src/main/java/javax/mail/search/FromStringTerm.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; -import javax.mail.Address; - -/** - * This class implements string comparisons for the From Address - * header.

      - * - * Note that this class differs from the FromTerm class - * in that this class does comparisons on address strings rather than Address - * objects. The string comparisons are case-insensitive. - * - * @since JavaMail 1.1 - */ - -public final class FromStringTerm extends AddressStringTerm { - - private static final long serialVersionUID = 5801127523826772788L; - - /** - * Constructor. - * - * @param pattern the address pattern to be compared. - */ - public FromStringTerm(String pattern) { - super(pattern); - } - - /** - * Check whether the address string specified in the constructor is - * a substring of the From address of this Message. - * - * @param msg The comparison is applied to this Message's From - * address. - * @return true if the match succeeds, otherwise false. - */ - public boolean match(Message msg) { - Address[] from; - - try { - from = msg.getFrom(); - } catch (Exception e) { - return false; - } - - if (from == null) - return false; - - for (int i=0; i < from.length; i++) - if (super.match(from[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof FromStringTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/FromTerm.java b/src/main/java/javax/mail/search/FromTerm.java deleted file mode 100644 index 51ea2233..00000000 --- a/src/main/java/javax/mail/search/FromTerm.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; -import javax.mail.Address; - -/** - * This class implements comparisons for the From Address header. - * - * @author Bill Shannon - * @author John Mani - */ -public final class FromTerm extends AddressTerm { - - private static final long serialVersionUID = 5214730291502658665L; - - /** - * Constructor - * @param address The Address to be compared - */ - public FromTerm(Address address) { - super(address); - } - - /** - * The address comparator. - * - * @param msg The address comparison is applied to this Message - * @return true if the comparison succeeds, otherwise false - */ - public boolean match(Message msg) { - Address[] from; - - try { - from = msg.getFrom(); - } catch (Exception e) { - return false; - } - - if (from == null) - return false; - - for (int i=0; i < from.length; i++) - if (super.match(from[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof FromTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/HeaderTerm.java b/src/main/java/javax/mail/search/HeaderTerm.java deleted file mode 100644 index 15565042..00000000 --- a/src/main/java/javax/mail/search/HeaderTerm.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import java.util.Locale; -import javax.mail.Message; - -/** - * This class implements comparisons for Message headers. - * The comparison is case-insensitive. - * - * @author Bill Shannon - * @author John Mani - */ -public final class HeaderTerm extends StringTerm { - /** - * The name of the header. - * - * @serial - */ - private String headerName; - - private static final long serialVersionUID = 8342514650333389122L; - - /** - * Constructor. - * - * @param headerName The name of the header - * @param pattern The pattern to search for - */ - public HeaderTerm(String headerName, String pattern) { - super(pattern); - this.headerName = headerName; - } - - /** - * Return the name of the header to compare with. - */ - public String getHeaderName() { - return headerName; - } - - /** - * The header match method. - * - * @param msg The match is applied to this Message's header - * @return true if the match succeeds, otherwise false - */ - public boolean match(Message msg) { - String[] headers; - - try { - headers = msg.getHeader(headerName); - } catch (Exception e) { - return false; - } - - if (headers == null) - return false; - - for (int i=0; i < headers.length; i++) - if (super.match(headers[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof HeaderTerm)) - return false; - HeaderTerm ht = (HeaderTerm)obj; - // XXX - depends on header comparisons being case independent - return ht.headerName.equalsIgnoreCase(headerName) && super.equals(ht); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - // XXX - depends on header comparisons being case independent - return headerName.toLowerCase(Locale.ENGLISH).hashCode() + - super.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/IntegerComparisonTerm.java b/src/main/java/javax/mail/search/IntegerComparisonTerm.java deleted file mode 100644 index b937e6bb..00000000 --- a/src/main/java/javax/mail/search/IntegerComparisonTerm.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -/** - * This class implements comparisons for integers. - * - * @author Bill Shannon - * @author John Mani - */ -public abstract class IntegerComparisonTerm extends ComparisonTerm { - /** - * The number. - * - * @serial - */ - protected int number; - - private static final long serialVersionUID = -6963571240154302484L; - - protected IntegerComparisonTerm(int comparison, int number) { - this.comparison = comparison; - this.number = number; - } - - /** - * Return the number to compare with. - */ - public int getNumber() { - return number; - } - - /** - * Return the type of comparison. - */ - public int getComparison() { - return comparison; - } - - protected boolean match(int i) { - switch (comparison) { - case LE: - return i <= number; - case LT: - return i < number; - case EQ: - return i == number; - case NE: - return i != number; - case GT: - return i > number; - case GE: - return i >= number; - default: - return false; - } - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof IntegerComparisonTerm)) - return false; - IntegerComparisonTerm ict = (IntegerComparisonTerm)obj; - return ict.number == this.number && super.equals(obj); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return number + super.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/MessageIDTerm.java b/src/main/java/javax/mail/search/MessageIDTerm.java deleted file mode 100644 index 587d3da2..00000000 --- a/src/main/java/javax/mail/search/MessageIDTerm.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This term models the RFC822 "MessageId" - a message-id for - * Internet messages that is supposed to be unique per message. - * Clients can use this term to search a folder for a message given - * its MessageId.

      - * - * The MessageId is represented as a String. - * - * @author Bill Shannon - * @author John Mani - */ -public final class MessageIDTerm extends StringTerm { - - private static final long serialVersionUID = -2121096296454691963L; - - /** - * Constructor. - * - * @param msgid the msgid to search for - */ - public MessageIDTerm(String msgid) { - // Note: comparison is case-insensitive - super(msgid); - } - - /** - * The match method. - * - * @param msg the match is applied to this Message's - * Message-ID header - * @return true if the match succeeds, otherwise false - */ - public boolean match(Message msg) { - String[] s; - - try { - s = msg.getHeader("Message-ID"); - } catch (Exception e) { - return false; - } - - if (s == null) - return false; - - for (int i=0; i < s.length; i++) - if (super.match(s[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof MessageIDTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/MessageNumberTerm.java b/src/main/java/javax/mail/search/MessageNumberTerm.java deleted file mode 100644 index 1c1fe99d..00000000 --- a/src/main/java/javax/mail/search/MessageNumberTerm.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements comparisons for Message numbers. - * - * @author Bill Shannon - * @author John Mani - */ -public final class MessageNumberTerm extends IntegerComparisonTerm { - - private static final long serialVersionUID = -5379625829658623812L; - - /** - * Constructor. - * - * @param number the Message number - */ - public MessageNumberTerm(int number) { - super(EQ, number); - } - - /** - * The match method. - * - * @param msg the Message number is matched with this Message - * @return true if the match succeeds, otherwise false - */ - public boolean match(Message msg) { - int msgno; - - try { - msgno = msg.getMessageNumber(); - } catch (Exception e) { - return false; - } - - return super.match(msgno); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof MessageNumberTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/NotTerm.java b/src/main/java/javax/mail/search/NotTerm.java deleted file mode 100644 index 55e63676..00000000 --- a/src/main/java/javax/mail/search/NotTerm.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements the logical NEGATION operator. - * - * @author Bill Shannon - * @author John Mani - */ -public final class NotTerm extends SearchTerm { - /** - * The search term to negate. - * - * @serial - */ - private SearchTerm term; - - private static final long serialVersionUID = 7152293214217310216L; - - public NotTerm(SearchTerm t) { - term = t; - } - - /** - * Return the term to negate. - */ - public SearchTerm getTerm() { - return term; - } - - /* The NOT operation */ - public boolean match(Message msg) { - return !term.match(msg); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof NotTerm)) - return false; - NotTerm nt = (NotTerm)obj; - return nt.term.equals(this.term); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return term.hashCode() << 1; - } -} diff --git a/src/main/java/javax/mail/search/OrTerm.java b/src/main/java/javax/mail/search/OrTerm.java deleted file mode 100644 index 9a5e7cbb..00000000 --- a/src/main/java/javax/mail/search/OrTerm.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements the logical OR operator on individual SearchTerms. - * - * @author Bill Shannon - * @author John Mani - */ -public final class OrTerm extends SearchTerm { - - /** - * The array of terms on which the OR operator should - * be applied. - * - * @serial - */ - private SearchTerm[] terms; - - private static final long serialVersionUID = 5380534067523646936L; - - /** - * Constructor that takes two operands. - * - * @param t1 first term - * @param t2 second term - */ - public OrTerm(SearchTerm t1, SearchTerm t2) { - terms = new SearchTerm[2]; - terms[0] = t1; - terms[1] = t2; - } - - /** - * Constructor that takes an array of SearchTerms. - * - * @param t array of search terms - */ - public OrTerm(SearchTerm[] t) { - terms = new SearchTerm[t.length]; - for (int i = 0; i < t.length; i++) - terms[i] = t[i]; - } - - /** - * Return the search terms. - */ - public SearchTerm[] getTerms() { - return (SearchTerm[])terms.clone(); - } - - /** - * The OR operation.

      - * - * The terms specified in the constructor are applied to - * the given object and the OR operator is applied to their results. - * - * @param msg The specified SearchTerms are applied to this Message - * and the OR operator is applied to their results. - * @return true if the OR succeds, otherwise false - */ - - public boolean match(Message msg) { - for (int i=0; i < terms.length; i++) - if (terms[i].match(msg)) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof OrTerm)) - return false; - OrTerm ot = (OrTerm)obj; - if (ot.terms.length != terms.length) - return false; - for (int i=0; i < terms.length; i++) - if (!terms[i].equals(ot.terms[i])) - return false; - return true; - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - int hash = 0; - for (int i=0; i < terms.length; i++) - hash += terms[i].hashCode(); - return hash; - } -} diff --git a/src/main/java/javax/mail/search/ReceivedDateTerm.java b/src/main/java/javax/mail/search/ReceivedDateTerm.java deleted file mode 100644 index e9cb9833..00000000 --- a/src/main/java/javax/mail/search/ReceivedDateTerm.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import java.util.Date; -import javax.mail.Message; - -/** - * This class implements comparisons for the Message Received date - * - * @author Bill Shannon - * @author John Mani - */ -public final class ReceivedDateTerm extends DateTerm { - - private static final long serialVersionUID = -2756695246195503170L; - - /** - * Constructor. - * - * @param comparison the Comparison type - * @param date the date to be compared - */ - public ReceivedDateTerm(int comparison, Date date) { - super(comparison, date); - } - - /** - * The match method. - * - * @param msg the date comparator is applied to this Message's - * sent date - * @return true if the comparison succeeds, otherwise false - */ - public boolean match(Message msg) { - Date d; - - try { - d = msg.getReceivedDate(); - } catch (Exception e) { - return false; - } - - if (d == null) - return false; - - return super.match(d); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof ReceivedDateTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/RecipientStringTerm.java b/src/main/java/javax/mail/search/RecipientStringTerm.java deleted file mode 100644 index 08957872..00000000 --- a/src/main/java/javax/mail/search/RecipientStringTerm.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; -import javax.mail.Address; - -/** - * This class implements string comparisons for the Recipient Address - * headers.

      - * - * Note that this class differs from the RecipientTerm class - * in that this class does comparisons on address strings rather than Address - * objects. The string comparisons are case-insensitive. - * - * @since JavaMail 1.1 - */ - -public final class RecipientStringTerm extends AddressStringTerm { - - /** - * The recipient type. - * - * @serial - */ - private Message.RecipientType type; - - private static final long serialVersionUID = -8293562089611618849L; - - /** - * Constructor. - * - * @param type the recipient type - * @param pattern the address pattern to be compared. - */ - public RecipientStringTerm(Message.RecipientType type, String pattern) { - super(pattern); - this.type = type; - } - - /** - * Return the type of recipient to match with. - */ - public Message.RecipientType getRecipientType() { - return type; - } - - /** - * Check whether the address specified in the constructor is - * a substring of the recipient address of this Message. - * - * @param msg The comparison is applied to this Message's recipient - * address. - * @return true if the match succeeds, otherwise false. - */ - public boolean match(Message msg) { - Address[] recipients; - - try { - recipients = msg.getRecipients(type); - } catch (Exception e) { - return false; - } - - if (recipients == null) - return false; - - for (int i=0; i < recipients.length; i++) - if (super.match(recipients[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof RecipientStringTerm)) - return false; - RecipientStringTerm rst = (RecipientStringTerm)obj; - return rst.type.equals(this.type) && super.equals(obj); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return type.hashCode() + super.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/RecipientTerm.java b/src/main/java/javax/mail/search/RecipientTerm.java deleted file mode 100644 index f311ccb9..00000000 --- a/src/main/java/javax/mail/search/RecipientTerm.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; -import javax.mail.Address; - -/** - * This class implements comparisons for the Recipient Address headers. - * - * @author Bill Shannon - * @author John Mani - */ -public final class RecipientTerm extends AddressTerm { - - /** - * The recipient type. - * - * @serial - */ - private Message.RecipientType type; - - private static final long serialVersionUID = 6548700653122680468L; - - /** - * Constructor. - * - * @param type the recipient type - * @param address the address to match for - */ - public RecipientTerm(Message.RecipientType type, Address address) { - super(address); - this.type = type; - } - - /** - * Return the type of recipient to match with. - */ - public Message.RecipientType getRecipientType() { - return type; - } - - /** - * The match method. - * - * @param msg The address match is applied to this Message's recepient - * address - * @return true if the match succeeds, otherwise false - */ - public boolean match(Message msg) { - Address[] recipients; - - try { - recipients = msg.getRecipients(type); - } catch (Exception e) { - return false; - } - - if (recipients == null) - return false; - - for (int i=0; i < recipients.length; i++) - if (super.match(recipients[i])) - return true; - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof RecipientTerm)) - return false; - RecipientTerm rt = (RecipientTerm)obj; - return rt.type.equals(this.type) && super.equals(obj); - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return type.hashCode() + super.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/SearchException.java b/src/main/java/javax/mail/search/SearchException.java deleted file mode 100644 index 79e97078..00000000 --- a/src/main/java/javax/mail/search/SearchException.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.MessagingException; - - -/** - * The exception thrown when a Search expression could not be handled. - * - * @author John Mani - */ - -public class SearchException extends MessagingException { - - private static final long serialVersionUID = -7092886778226268686L; - - /** - * Constructs a SearchException with no detail message. - */ - public SearchException() { - super(); - } - - /** - * Constructs a SearchException with the specified detail message. - * @param s the detail message - */ - public SearchException(String s) { - super(s); - } -} diff --git a/src/main/java/javax/mail/search/SearchTerm.java b/src/main/java/javax/mail/search/SearchTerm.java deleted file mode 100644 index d0e8d9b6..00000000 --- a/src/main/java/javax/mail/search/SearchTerm.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import java.io.Serializable; - -import javax.mail.Message; - -/** - * Search criteria are expressed as a tree of search-terms, forming - * a parse-tree for the search expression.

      - * - * Search-terms are represented by this class. This is an abstract - * class; subclasses implement specific match methods.

      - * - * Search terms are serializable, which allows storing a search term - * between sessions. - * - * Warning: - * Serialized objects of this class may not be compatible with future - * JavaMail API releases. The current serialization support is - * appropriate for short term storage.

      - * - * Warning: - * Search terms that include references to objects of type - * Message.RecipientType will not be deserialized - * correctly on JDK 1.1 systems. While these objects will be deserialized - * without throwing any exceptions, the resulting objects violate the - * type-safe enum contract of the Message.RecipientType - * class. Proper deserialization of these objects depends on support - * for the readReplace method, added in JDK 1.2. - * - * @author Bill Shannon - * @author John Mani - */ -public abstract class SearchTerm implements Serializable { - - private static final long serialVersionUID = -6652358452205992789L; - - /** - * This method applies a specific match criterion to the given - * message and returns the result. - * - * @param msg The match criterion is applied on this message - * @return true, it the match succeeds, false if the match fails - */ - - public abstract boolean match(Message msg); -} diff --git a/src/main/java/javax/mail/search/SentDateTerm.java b/src/main/java/javax/mail/search/SentDateTerm.java deleted file mode 100644 index e298d098..00000000 --- a/src/main/java/javax/mail/search/SentDateTerm.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import java.util.Date; -import javax.mail.Message; - -/** - * This class implements comparisons for the Message SentDate. - * - * @author Bill Shannon - * @author John Mani - */ -public final class SentDateTerm extends DateTerm { - - private static final long serialVersionUID = 5647755030530907263L; - - /** - * Constructor. - * - * @param comparison the Comparison type - * @param date the date to be compared - */ - public SentDateTerm(int comparison, Date date) { - super(comparison, date); - } - - /** - * The match method. - * - * @param msg the date comparator is applied to this Message's - * sent date - * @return true if the comparison succeeds, otherwise false - */ - public boolean match(Message msg) { - Date d; - - try { - d = msg.getSentDate(); - } catch (Exception e) { - return false; - } - - if (d == null) - return false; - - return super.match(d); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof SentDateTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/SizeTerm.java b/src/main/java/javax/mail/search/SizeTerm.java deleted file mode 100644 index 9aab46d4..00000000 --- a/src/main/java/javax/mail/search/SizeTerm.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements comparisons for Message sizes. - * - * @author Bill Shannon - * @author John Mani - */ -public final class SizeTerm extends IntegerComparisonTerm { - - private static final long serialVersionUID = -2556219451005103709L; - - /** - * Constructor. - * - * @param comparison the Comparison type - * @param size the size - */ - public SizeTerm(int comparison, int size) { - super(comparison, size); - } - - /** - * The match method. - * - * @param msg the size comparator is applied to this Message's size - * @return true if the size is equal, otherwise false - */ - public boolean match(Message msg) { - int size; - - try { - size = msg.getSize(); - } catch (Exception e) { - return false; - } - - if (size == -1) - return false; - - return super.match(size); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof SizeTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/StringTerm.java b/src/main/java/javax/mail/search/StringTerm.java deleted file mode 100644 index 59c9db20..00000000 --- a/src/main/java/javax/mail/search/StringTerm.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -/** - * This class implements the match method for Strings. The current - * implementation provides only for substring matching. We - * could add comparisons (like strcmp ...). - * - * @author Bill Shannon - * @author John Mani - */ -public abstract class StringTerm extends SearchTerm { - /** - * The pattern. - * - * @serial - */ - protected String pattern; - - /** - * Ignore case when comparing? - * - * @serial - */ - protected boolean ignoreCase; - - private static final long serialVersionUID = 1274042129007696269L; - - protected StringTerm(String pattern) { - this.pattern = pattern; - ignoreCase = true; - } - - protected StringTerm(String pattern, boolean ignoreCase) { - this.pattern = pattern; - this.ignoreCase = ignoreCase; - } - - /** - * Return the string to match with. - */ - public String getPattern() { - return pattern; - } - - /** - * Return true if we should ignore case when matching. - */ - public boolean getIgnoreCase() { - return ignoreCase; - } - - protected boolean match(String s) { - int len = s.length() - pattern.length(); - for (int i=0; i <= len; i++) { - if (s.regionMatches(ignoreCase, i, - pattern, 0, pattern.length())) - return true; - } - return false; - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof StringTerm)) - return false; - StringTerm st = (StringTerm)obj; - if (ignoreCase) - return st.pattern.equalsIgnoreCase(this.pattern) && - st.ignoreCase == this.ignoreCase; - else - return st.pattern.equals(this.pattern) && - st.ignoreCase == this.ignoreCase; - } - - /** - * Compute a hashCode for this object. - */ - public int hashCode() { - return ignoreCase ? pattern.hashCode() : ~pattern.hashCode(); - } -} diff --git a/src/main/java/javax/mail/search/SubjectTerm.java b/src/main/java/javax/mail/search/SubjectTerm.java deleted file mode 100644 index 238f64c6..00000000 --- a/src/main/java/javax/mail/search/SubjectTerm.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.search; - -import javax.mail.Message; - -/** - * This class implements comparisons for the message Subject header. - * The comparison is case-insensitive. The pattern is a simple string - * that must appear as a substring in the Subject. - * - * @author Bill Shannon - * @author John Mani - */ -public final class SubjectTerm extends StringTerm { - - private static final long serialVersionUID = 7481568618055573432L; - - /** - * Constructor. - * - * @param pattern the pattern to search for - */ - public SubjectTerm(String pattern) { - // Note: comparison is case-insensitive - super(pattern); - } - - /** - * The match method. - * - * @param msg the pattern match is applied to this Message's - * subject header - * @return true if the pattern match succeeds, otherwise false - */ - public boolean match(Message msg) { - String subj; - - try { - subj = msg.getSubject(); - } catch (Exception e) { - return false; - } - - if (subj == null) - return false; - - return super.match(subj); - } - - /** - * Equality comparison. - */ - public boolean equals(Object obj) { - if (!(obj instanceof SubjectTerm)) - return false; - return super.equals(obj); - } -} diff --git a/src/main/java/javax/mail/search/package.html b/src/main/java/javax/mail/search/package.html deleted file mode 100644 index 960ea287..00000000 --- a/src/main/java/javax/mail/search/package.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - -Message search terms for the JavaMail API. -This package defines classes that can be used to construct a search -expression to search a folder for messages matching the expression; -see the {@link javax.mail.Folder#search search} method on -{@link javax.mail.Folder javax.mail.Folder}. -See {@link javax.mail.search.SearchTerm SearchTerm}. -

      -Note that the exact search capabilities depend on the protocol, -provider, and server in use. For the POP3 protocol, all searching is -done on the client side using the JavaMail classes. For IMAP, all -searching is done on the server side and is limited by the search -capabilities of the IMAP protocol and the IMAP server being used. -For example, IMAP date based searches have only day granularity. -

      -In general, all of the string patterns supported by search terms are -just simple strings; no regular expressions are supported. - - - diff --git a/src/main/java/javax/mail/util/ByteArrayDataSource.java b/src/main/java/javax/mail/util/ByteArrayDataSource.java deleted file mode 100644 index 0b25fe8a..00000000 --- a/src/main/java/javax/mail/util/ByteArrayDataSource.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.util; - -import java.io.*; -import javax.activation.*; -import javax.mail.internet.*; - -/** - * A DataSource backed by a byte array. The byte array may be - * passed in directly, or may be initialized from an InputStream - * or a String. - * - * @since JavaMail 1.4 - * @author John Mani - * @author Bill Shannon - * @author Max Spivak - */ -public class ByteArrayDataSource implements DataSource { - private byte[] data; // data - private int len = -1; - private String type; // content-type - private String name = ""; - - static class DSByteArrayOutputStream extends ByteArrayOutputStream { - public byte[] getBuf() { - return buf; - } - - public int getCount() { - return count; - } - } - - /** - * Create a ByteArrayDataSource with data from the - * specified InputStream and with the specified MIME type. - * The InputStream is read completely and the data is - * stored in a byte array. - * - * @param is the InputStream - * @param type the MIME type - * @exception IOException errors reading the stream - */ - public ByteArrayDataSource(InputStream is, String type) throws IOException { - DSByteArrayOutputStream os = new DSByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int len; - while ((len = is.read(buf)) > 0) - os.write(buf, 0, len); - this.data = os.getBuf(); - this.len = os.getCount(); - - /* - * ByteArrayOutputStream doubles the size of the buffer every time - * it needs to expand, which can waste a lot of memory in the worst - * case with large buffers. Check how much is wasted here and if - * it's too much, copy the data into a new buffer and allow the - * old buffer to be garbage collected. - */ - if (this.data.length - this.len > 256*1024) { - this.data = os.toByteArray(); - this.len = this.data.length; // should be the same - } - this.type = type; - } - - /** - * Create a ByteArrayDataSource with data from the - * specified byte array and with the specified MIME type. - * - * @param data the data - * @param type the MIME type - */ - public ByteArrayDataSource(byte[] data, String type) { - this.data = data; - this.type = type; - } - - /** - * Create a ByteArrayDataSource with data from the - * specified String and with the specified MIME type. - * The MIME type should include a charset - * parameter specifying the charset to be used for the - * string. If the parameter is not included, the - * default charset is used. - * - * @param data the String - * @param type the MIME type - * @exception IOException errors reading the String - */ - public ByteArrayDataSource(String data, String type) throws IOException { - String charset = null; - try { - ContentType ct = new ContentType(type); - charset = ct.getParameter("charset"); - } catch (ParseException pex) { - // ignore parse error - } - charset = MimeUtility.javaCharset(charset); - if (charset == null) - charset = MimeUtility.getDefaultJavaCharset(); - // XXX - could convert to bytes on demand rather than copying here - this.data = data.getBytes(charset); - this.type = type; - } - - /** - * Return an InputStream for the data. - * Note that a new stream is returned each time - * this method is called. - * - * @return the InputStream - * @exception IOException if no data has been set - */ - public InputStream getInputStream() throws IOException { - if (data == null) - throw new IOException("no data"); - if (len < 0) - len = data.length; - return new SharedByteArrayInputStream(data, 0, len); - } - - /** - * Return an OutputStream for the data. - * Writing the data is not supported; an IOException - * is always thrown. - * - * @exception IOException always - */ - public OutputStream getOutputStream() throws IOException { - throw new IOException("cannot do this"); - } - - /** - * Get the MIME content type of the data. - * - * @return the MIME type - */ - public String getContentType() { - return type; - } - - /** - * Get the name of the data. - * By default, an empty string ("") is returned. - * - * @return the name of this data - */ - public String getName() { - return name; - } - - /** - * Set the name of the data. - * - * @param name the name of this data - */ - public void setName(String name) { - this.name = name; - } -} diff --git a/src/main/java/javax/mail/util/SharedByteArrayInputStream.java b/src/main/java/javax/mail/util/SharedByteArrayInputStream.java deleted file mode 100644 index ca0f0a6c..00000000 --- a/src/main/java/javax/mail/util/SharedByteArrayInputStream.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.util; - -import java.io.*; -import javax.mail.internet.SharedInputStream; - -/** - * A ByteArrayInputStream that implements the SharedInputStream interface, - * allowing the underlying byte array to be shared between multiple readers. - * - * @author Bill Shannon - * @since JavaMail 1.4 - */ - -public class SharedByteArrayInputStream extends ByteArrayInputStream - implements SharedInputStream { - /** - * Position within shared buffer that this stream starts at. - */ - protected int start = 0; - - /** - * Create a SharedByteArrayInputStream representing the entire - * byte array. - * - * @param buf the byte array - */ - public SharedByteArrayInputStream(byte[] buf) { - super(buf); - } - - /** - * Create a SharedByteArrayInputStream representing the part - * of the byte array from offset for length - * bytes. - * - * @param buf the byte array - * @param offset offset in byte array to first byte to include - * @param length number of bytes to include - */ - public SharedByteArrayInputStream(byte[] buf, int offset, int length) { - super(buf, offset, length); - start = offset; - } - - /** - * Return the current position in the InputStream, as an - * offset from the beginning of the InputStream. - * - * @return the current position - */ - public long getPosition() { - return pos - start; - } - - /** - * Return a new InputStream representing a subset of the data - * from this InputStream, starting at start (inclusive) - * up to end (exclusive). start must be - * non-negative. If end is -1, the new stream ends - * at the same place as this stream. The returned InputStream - * will also implement the SharedInputStream interface. - * - * @param start the starting position - * @param end the ending position + 1 - * @return the new stream - */ - public InputStream newStream(long start, long end) { - if (start < 0) - throw new IllegalArgumentException("start < 0"); - if (end == -1) - end = count - this.start; - return new SharedByteArrayInputStream(buf, - this.start + (int)start, (int)(end - start)); - } -} diff --git a/src/main/java/javax/mail/util/SharedFileInputStream.java b/src/main/java/javax/mail/util/SharedFileInputStream.java deleted file mode 100644 index b052dca4..00000000 --- a/src/main/java/javax/mail/util/SharedFileInputStream.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package javax.mail.util; - -import java.io.*; -import javax.mail.internet.SharedInputStream; - -/** - * A SharedFileInputStream is a - * BufferedInputStream that buffers - * data from the file and supports the mark - * and reset methods. It also supports the - * newStream method that allows you to create - * other streams that represent subsets of the file. - * A RandomAccessFile object is used to - * access the file data.

      - * - * Note that when the SharedFileInputStream is closed, - * all streams created with the newStream - * method are also closed. This allows the creator of the - * SharedFileInputStream object to control access to the - * underlying file and ensure that it is closed when - * needed, to avoid leaking file descriptors. Note also - * that this behavior contradicts the requirements of - * SharedInputStream and may change in a future release. - * - * @author Bill Shannon - * @since JavaMail 1.4 - */ -public class SharedFileInputStream extends BufferedInputStream - implements SharedInputStream { - - private static int defaultBufferSize = 2048; - - /** - * The file containing the data. - * Shared by all related SharedFileInputStreams. - */ - protected RandomAccessFile in; - - /** - * The normal size of the read buffer. - */ - protected int bufsize; - - /** - * The file offset that corresponds to the first byte in - * the read buffer. - */ - protected long bufpos; - - /** - * The file offset of the start of data in this subset of the file. - */ - protected long start = 0; - - /** - * The amount of data in this subset of the file. - */ - protected long datalen; - - /** - * True if this is a top level stream created directly by "new". - * False if this is a derived stream created by newStream. - */ - private boolean master = true; - - /** - * A shared class that keeps track of the references - * to a particular file so it can be closed when the - * last reference is gone. - */ - static class SharedFile { - private int cnt; - private RandomAccessFile in; - - SharedFile(String file) throws IOException { - this.in = new RandomAccessFile(file, "r"); - } - - SharedFile(File file) throws IOException { - this.in = new RandomAccessFile(file, "r"); - } - - public synchronized RandomAccessFile open() { - cnt++; - return in; - } - - public synchronized void close() throws IOException { - if (cnt > 0 && --cnt <= 0) - in.close(); - } - - public synchronized void forceClose() throws IOException { - if (cnt > 0) { - // normal case, close exceptions propagated - cnt = 0; - in.close(); - } else { - // should already be closed, ignore exception - try { - in.close(); - } catch (IOException ioex) { } - } - } - - protected void finalize() throws Throwable { - super.finalize(); - in.close(); - } - } - - private SharedFile sf; - - /** - * Check to make sure that this stream has not been closed - */ - private void ensureOpen() throws IOException { - if (in == null) - throw new IOException("Stream closed"); - } - - /** - * Creates a SharedFileInputStream - * for the file. - * - * @param file the file - */ - public SharedFileInputStream(File file) throws IOException { - this(file, defaultBufferSize); - } - - /** - * Creates a SharedFileInputStream - * for the named file - * - * @param file the file - */ - public SharedFileInputStream(String file) throws IOException { - this(file, defaultBufferSize); - } - - /** - * Creates a SharedFileInputStream - * with the specified buffer size. - * - * @param file the file - * @param size the buffer size. - * @exception IllegalArgumentException if size <= 0. - */ - public SharedFileInputStream(File file, int size) throws IOException { - super(null); // XXX - will it NPE? - if (size <= 0) - throw new IllegalArgumentException("Buffer size <= 0"); - init(new SharedFile(file), size); - } - - /** - * Creates a SharedFileInputStream - * with the specified buffer size. - * - * @param file the file - * @param size the buffer size. - * @exception IllegalArgumentException if size <= 0. - */ - public SharedFileInputStream(String file, int size) throws IOException { - super(null); // XXX - will it NPE? - if (size <= 0) - throw new IllegalArgumentException("Buffer size <= 0"); - init(new SharedFile(file), size); - } - - private void init(SharedFile sf, int size) throws IOException { - this.sf = sf; - this.in = sf.open(); - this.start = 0; - this.datalen = in.length(); // XXX - file can't grow - this.bufsize = size; - buf = new byte[size]; - } - - /** - * Used internally by the newStream method. - */ - private SharedFileInputStream(SharedFile sf, long start, long len, - int bufsize) { - super(null); - this.master = false; - this.sf = sf; - this.in = sf.open(); - this.start = start; - this.bufpos = start; - this.datalen = len; - this.bufsize = bufsize; - buf = new byte[bufsize]; - } - - /** - * Fills the buffer with more data, taking into account - * shuffling and other tricks for dealing with marks. - * Assumes that it is being called by a synchronized method. - * This method also assumes that all data has already been read in, - * hence pos > count. - */ - private void fill() throws IOException { - if (markpos < 0) { - pos = 0; /* no mark: throw away the buffer */ - bufpos += count; - } else if (pos >= buf.length) /* no room left in buffer */ - if (markpos > 0) { /* can throw away early part of the buffer */ - int sz = pos - markpos; - System.arraycopy(buf, markpos, buf, 0, sz); - pos = sz; - bufpos += markpos; - markpos = 0; - } else if (buf.length >= marklimit) { - markpos = -1; /* buffer got too big, invalidate mark */ - pos = 0; /* drop buffer contents */ - bufpos += count; - } else { /* grow buffer */ - int nsz = pos * 2; - if (nsz > marklimit) - nsz = marklimit; - byte nbuf[] = new byte[nsz]; - System.arraycopy(buf, 0, nbuf, 0, pos); - buf = nbuf; - } - count = pos; - in.seek(bufpos + pos); - // limit to datalen - int len = buf.length - pos; - if (bufpos - start + pos + len > datalen) - len = (int)(datalen - (bufpos - start + pos)); - int n = in.read(buf, pos, len); - if (n > 0) - count = n + pos; - } - - /** - * See the general contract of the read - * method of InputStream. - * - * @return the next byte of data, or -1 if the end of the - * stream is reached. - * @exception IOException if an I/O error occurs. - */ - public synchronized int read() throws IOException { - ensureOpen(); - if (pos >= count) { - fill(); - if (pos >= count) - return -1; - } - return buf[pos++] & 0xff; - } - - /** - * Read characters into a portion of an array, reading from the underlying - * stream at most once if necessary. - */ - private int read1(byte[] b, int off, int len) throws IOException { - int avail = count - pos; - if (avail <= 0) { - - /* - if (false) { - /* If the requested length is at least as large as the buffer, and - if there is no mark/reset activity, do not bother to copy the - bytes into the local buffer. In this way buffered streams will - cascade harmlessly. */ /* - if (len >= buf.length && markpos < 0) { - // XXX - seek, update bufpos - how? - return in.read(b, off, len); - } - }*/ - - fill(); - avail = count - pos; - if (avail <= 0) return -1; - } - int cnt = (avail < len) ? avail : len; - System.arraycopy(buf, pos, b, off, cnt); - pos += cnt; - return cnt; - } - - /** - * Reads bytes from this stream into the specified byte array, - * starting at the given offset. - * - *

      This method implements the general contract of the corresponding - * {@link java.io.InputStream#read(byte[], int, int) read} - * method of the {@link java.io.InputStream} class. - * - * @param b destination buffer. - * @param off offset at which to start storing bytes. - * @param len maximum number of bytes to read. - * @return the number of bytes read, or -1 if the end of - * the stream has been reached. - * @exception IOException if an I/O error occurs. - */ - public synchronized int read(byte b[], int off, int len) - throws IOException - { - ensureOpen(); - if ((off | len | (off + len) | (b.length - (off + len))) < 0) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - int n = read1(b, off, len); - if (n <= 0) return n; - while ((n < len) /* && (in.available() > 0) */) { - int n1 = read1(b, off + n, len - n); - if (n1 <= 0) break; - n += n1; - } - return n; - } - - /** - * See the general contract of the skip - * method of InputStream. - * - * @param n the number of bytes to be skipped. - * @return the actual number of bytes skipped. - * @exception IOException if an I/O error occurs. - */ - public synchronized long skip(long n) throws IOException { - ensureOpen(); - if (n <= 0) { - return 0; - } - long avail = count - pos; - - if (avail <= 0) { - // If no mark position set then don't keep in buffer - /* - if (markpos <0) - return in.skip(n); - */ - - // Fill in buffer to save bytes for reset - fill(); - avail = count - pos; - if (avail <= 0) - return 0; - } - - long skipped = (avail < n) ? avail : n; - pos += skipped; - return skipped; - } - - /** - * Returns the number of bytes that can be read from this input - * stream without blocking. - * - * @return the number of bytes that can be read from this input - * stream without blocking. - * @exception IOException if an I/O error occurs. - */ - public synchronized int available() throws IOException { - ensureOpen(); - return (count - pos) + in_available(); - } - - private int in_available() throws IOException { - // XXX - overflow - return (int)((start + datalen) - (bufpos + count)); - } - - /** - * See the general contract of the mark - * method of InputStream. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - * @see #reset() - */ - public synchronized void mark(int readlimit) { - marklimit = readlimit; - markpos = pos; - } - - /** - * See the general contract of the reset - * method of InputStream. - *

      - * If markpos is -1 - * (no mark has been set or the mark has been - * invalidated), an IOException - * is thrown. Otherwise, pos is - * set equal to markpos. - * - * @exception IOException if this stream has not been marked or - * if the mark has been invalidated. - * @see #mark(int) - */ - public synchronized void reset() throws IOException { - ensureOpen(); - if (markpos < 0) - throw new IOException("Resetting to invalid mark"); - pos = markpos; - } - - /** - * Tests if this input stream supports the mark - * and reset methods. The markSupported - * method of SharedFileInputStream returns - * true. - * - * @return a boolean indicating if this stream type supports - * the mark and reset methods. - * @see java.io.InputStream#mark(int) - * @see java.io.InputStream#reset() - */ - public boolean markSupported() { - return true; - } - - /** - * Closes this input stream and releases any system resources - * associated with the stream. - * - * @exception IOException if an I/O error occurs. - */ - public void close() throws IOException { - if (in == null) - return; - try { - if (master) - sf.forceClose(); - else - sf.close(); - } finally { - sf = null; - in = null; - buf = null; - } - } - - /** - * Return the current position in the InputStream, as an - * offset from the beginning of the InputStream. - * - * @return the current position - */ - public long getPosition() { -//System.out.println("getPosition: start " + start + " pos " + pos + " bufpos " + bufpos + " = " + (bufpos + pos - start)); - if (in == null) - throw new RuntimeException("Stream closed"); - return bufpos + pos - start; - } - - /** - * Return a new InputStream representing a subset of the data - * from this InputStream, starting at start (inclusive) - * up to end (exclusive). start must be - * non-negative. If end is -1, the new stream ends - * at the same place as this stream. The returned InputStream - * will also implement the SharedInputStream interface. - * - * @param start the starting position - * @param end the ending position + 1 - * @return the new stream - */ - public synchronized InputStream newStream(long start, long end) { - if (in == null) - throw new RuntimeException("Stream closed"); - if (start < 0) - throw new IllegalArgumentException("start < 0"); - if (end == -1) - end = datalen; - return new SharedFileInputStream(sf, - this.start + (int)start, (int)(end - start), bufsize); - } - - // for testing... - /* - public static void main(String[] argv) throws Exception { - SharedFileInputStream is = new SharedFileInputStream(argv[0]); - java.util.Random r = new java.util.Random(); - int b; - while ((b = is.read()) >= 0) { - System.out.write(b); - if (r.nextDouble() < 0.3) { - InputStream is2 = is.newStream(is.getPosition(), -1); - int b2; - while ((b2 = is2.read()) >= 0) - ; - } - } - } - */ - - /** - * Force this stream to close. - */ - protected void finalize() throws Throwable { - super.finalize(); - close(); - } -} diff --git a/src/main/java/javax/mail/util/package.html b/src/main/java/javax/mail/util/package.html deleted file mode 100644 index 6b51aa17..00000000 --- a/src/main/java/javax/mail/util/package.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - -JavaMail API utility classes. -This package specifies utility classes that are useful with -other JavaMail APIs. - - -