diff --git a/app/build.gradle b/app/build.gradle
index 37fe359d4b..fb3058aa02 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -296,7 +296,7 @@ dependencies {
// https://javaee.github.io/javamail/
// https://projects.eclipse.org/projects/ee4j.javamail
// https://mvnrepository.com/artifact/com.sun.mail
- //implementation "com.sun.mail:android-mail:$javamail_version"
+ implementation "com.sun.mail:android-mail:$javamail_version"
implementation "com.sun.mail:android-activation:$javamail_version"
// https://jsoup.org/news/
diff --git a/app/src/main/java/com/sun/mail/auth/MD4.java b/app/src/main/java/com/sun/mail/auth/MD4.java
deleted file mode 100644
index 566de43f1e..0000000000
--- a/app/src/main/java/com/sun/mail/auth/MD4.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 2005, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-/*
- * 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 Jakarta Mail)
- */
-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.
- *
- * @param in the input byte array
- * @return the message digest 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/app/src/main/java/com/sun/mail/auth/Ntlm.java b/app/src/main/java/com/sun/mail/auth/Ntlm.java
deleted file mode 100644
index 882776fcca..0000000000
--- a/app/src/main/java/com/sun/mail/auth/Ntlm.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (c) 2005, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-/*
- * 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.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Locale;
-import java.util.Random;
-import java.util.logging.Level;
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.DESKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-
-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 Jakarta Mail)
- */
-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 Mac hmac;
-
- private MailLogger logger;
-
- // NTLM flags, as defined in Microsoft NTLM spec
- // https://msdn.microsoft.com/en-us/library/cc236621.aspx
- private static final int NTLMSSP_NEGOTIATE_UNICODE = 0x00000001;
- private static final int NTLMSSP_NEGOTIATE_OEM = 0x00000002;
- private static final int NTLMSSP_REQUEST_TARGET = 0x00000004;
- private static final int NTLMSSP_NEGOTIATE_SIGN = 0x00000010;
- private static final int NTLMSSP_NEGOTIATE_SEAL = 0x00000020;
- private static final int NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040;
- private static final int NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080;
- private static final int NTLMSSP_NEGOTIATE_NTLM = 0x00000200;
- private static final int NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000;
- private static final int NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000;
- private static final int NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
- private static final int NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000;
- private static final int NTLMSSP_TARGET_TYPE_SERVER = 0x00020000;
- private static final int NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000;
- private static final int NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000;
- private static final int NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000;
- private static final int NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000;
- private static final int NTLMSSP_NEGOTIATE_VERSION = 0x02000000;
- private static final int NTLMSSP_NEGOTIATE_128 = 0x20000000;
- private static final int NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000;
- private static final int NTLMSSP_NEGOTIATE_56 = 0x80000000;
-
- private static final byte RESPONSERVERSION = 1;
- private static final byte HIRESPONSERVERSION = 1;
- private static final byte[] Z6 = new byte[] { 0, 0, 0, 0, 0, 0 };
- private static final byte[] Z4 = new byte[] { 0, 0, 0, 0 };
-
- private void init0() {
- type1 = new byte[256]; // hopefully large enough
- type3 = new byte[512]; // ditto
- System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,1}, 0,
- type1, 0, 9);
- System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,3}, 0,
- type3, 0, 9);
-
- 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.
- *
- * @param ntdomain the NT domain
- * @param hostname the host name
- * @param username the user name
- * @param password the password
- * @param logger the MailLogger
- */
- 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(Locale.ENGLISH);
- 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;
- }
- }
-
- // for compatibility, just in case
- public String generateType1Msg(int flags) {
- return generateType1Msg(flags, false);
- }
-
- public String generateType1Msg(int flags, boolean v2) {
- int dlen = ntdomain.length();
- int type1flags =
- NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_OEM |
- NTLMSSP_NEGOTIATE_NTLM |
- NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED |
- NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
- flags;
- if (dlen != 0)
- type1flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;
- if (v2)
- type1flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
- writeInt(type1, 12, type1flags);
- type1[28] = (byte) 0x20; // host name offset
- writeShort(type1, 16, dlen);
- writeShort(type1, 18, dlen);
-
- int hlen = hostname.length();
- writeShort(type1, 24, hlen);
- writeShort(type1, 26, hlen);
-
- copybytes(type1, 32, hostname, "iso-8859-1");
- copybytes(type1, hlen+32, ntdomain, "iso-8859-1");
- writeInt(type1, 20, hlen+32);
-
- 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;
- }
-
- /**
- * Compute hash-based message authentication code for NTLMv2.
- */
- private byte[] hmacMD5(byte[] key, byte[] text) {
- try {
- if (hmac == null)
- hmac = Mac.getInstance("HmacMD5");
- } catch (NoSuchAlgorithmException ex) {
- throw new AssertionError();
- }
- try {
- byte[] nk = new byte[16];
- System.arraycopy(key, 0, nk, 0, key.length > 16 ? 16 : key.length);
- SecretKeySpec skey = new SecretKeySpec(nk, "HmacMD5");
- hmac.init(skey);
- return hmac.doFinal(text);
- } catch (InvalidKeyException ex) {
- assert false;
- } catch (RuntimeException e) {
- assert false;
- }
- return null;
- }
-
- 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;
- }
-
- /*
- * Calculate the NTLMv2 response based on the nthash, additional data,
- * and the original challenge.
- */
- private byte[] calcV2Response(byte[] nthash, byte[] blob, byte[] challenge)
- throws GeneralSecurityException {
- byte[] txt = null;
- try {
- txt = (username.toUpperCase(Locale.ENGLISH) + ntdomain).
- getBytes("UnicodeLittleUnmarked");
- } catch (UnsupportedEncodingException ex) {
- // should never happen
- assert false;
- }
- byte[] ntlmv2hash = hmacMD5(nthash, txt);
- byte[] cb = new byte[blob.length + 8];
- System.arraycopy(challenge, 0, cb, 0, 8);
- System.arraycopy(blob, 0, cb, 8, blob.length);
- byte[] result = new byte[blob.length + 16];
- System.arraycopy(hmacMD5(ntlmv2hash, cb), 0, result, 0, 16);
- System.arraycopy(blob, 0, result, 16, blob.length);
- return result;
- }
-
- public String generateType3Msg(String type2msg) {
- try {
-
- /* First decode the type2 message to get the server challenge */
- /* challenge is located at type2[24] for 8 bytes */
- byte[] type2 = null;
- try {
- type2 = BASE64DecoderStream.decode(type2msg.getBytes("us-ascii"));
- } catch (UnsupportedEncodingException ex) {
- // should never happen
- assert false;
- }
- if (logger.isLoggable(Level.FINE))
- logger.fine("type 2 message: " + toHex(type2));
-
- byte[] challenge = new byte[8];
- System.arraycopy(type2, 24, challenge, 0, 8);
-
- int type3flags =
- NTLMSSP_NEGOTIATE_UNICODE |
- NTLMSSP_NEGOTIATE_NTLM |
- NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
-
- int ulen = username.length()*2;
- writeShort(type3, 36, ulen);
- writeShort(type3, 38, ulen);
- int dlen = ntdomain.length()*2;
- writeShort(type3, 28, dlen);
- writeShort(type3, 30, dlen);
- int hlen = hostname.length()*2;
- writeShort(type3, 44, hlen);
- writeShort(type3, 46, hlen);
-
- int l = 64;
- copybytes(type3, l, ntdomain, "UnicodeLittleUnmarked");
- writeInt(type3, 32, l);
- l += dlen;
- copybytes(type3, l, username, "UnicodeLittleUnmarked");
- writeInt(type3, 40, l);
- l += ulen;
- copybytes(type3, l, hostname, "UnicodeLittleUnmarked");
- writeInt(type3, 48, l);
- l += hlen;
-
- byte[] msg = null;
- byte[] lmresponse = null;
- byte[] ntresponse = null;
- int flags = readInt(type2, 20);
-
- // did the server agree to NTLMv2?
- if ((flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0) {
- // yes, create an NTLMv2 response
- logger.fine("Using NTLMv2");
- type3flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
- byte[] nonce = new byte[8];
- // XXX - allow user to specify Random instance via properties?
- (new Random()).nextBytes(nonce);
- byte[] nthash = calcNTHash();
- lmresponse = calcV2Response(nthash, nonce, challenge);
- byte[] targetInfo = new byte[0];
- if ((flags & NTLMSSP_NEGOTIATE_TARGET_INFO) != 0) {
- int tlen = readShort(type2, 40);
- int toff = readInt(type2, 44);
- targetInfo = new byte[tlen];
- System.arraycopy(type2, toff, targetInfo, 0, tlen);
- }
- byte[] blob = new byte[32 + targetInfo.length];
- blob[0] = RESPONSERVERSION;
- blob[1] = HIRESPONSERVERSION;
- System.arraycopy(Z6, 0, blob, 2, 6);
- // convert time to NT format
- long now = (System.currentTimeMillis() + 11644473600000L) * 10000L;
- for (int i = 0; i < 8; i++) {
- blob[8 + i] = (byte)(now & 0xff);
- now >>= 8;
- }
- System.arraycopy(nonce, 0, blob, 16, 8);
- System.arraycopy(Z4, 0, blob, 24, 4);
- System.arraycopy(targetInfo, 0, blob, 28, targetInfo.length);
- System.arraycopy(Z4, 0, blob, 28 + targetInfo.length, 4);
- ntresponse = calcV2Response(nthash, blob, challenge);
- } else {
- byte[] lmhash = calcLMHash();
- lmresponse = calcResponse(lmhash, challenge);
- byte[] nthash = calcNTHash();
- ntresponse = calcResponse(nthash, challenge);
- }
- System.arraycopy(lmresponse, 0, type3, l, lmresponse.length);
- writeShort(type3, 12, lmresponse.length);
- writeShort(type3, 14, lmresponse.length);
- writeInt(type3, 16, l);
- l += 24;
- System.arraycopy(ntresponse, 0, type3, l, ntresponse.length);
- writeShort(type3, 20, ntresponse.length);
- writeShort(type3, 22, ntresponse.length);
- writeInt(type3, 24, l);
- l += ntresponse.length;
- writeShort(type3, 56, l);
-
- msg = new byte[l];
- System.arraycopy(type3, 0, msg, 0, l);
-
- writeInt(type3, 60, type3flags);
-
- 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 int readShort(byte[] b, int off) {
- return (((int)b[off]) & 0xff) |
- ((((int)b[off+1]) & 0xff) << 8);
- }
-
- private void writeShort(byte[] b, int off, int data) {
- b[off] = (byte) (data & 0xff);
- b[off+1] = (byte) ((data >> 8) & 0xff);
- }
-
- private static int readInt(byte[] b, int off) {
- return (((int)b[off]) & 0xff) |
- ((((int)b[off+1]) & 0xff) << 8) |
- ((((int)b[off+2]) & 0xff) << 16) |
- ((((int)b[off+3]) & 0xff) << 24);
- }
-
- private void writeInt(byte[] b, int off, int data) {
- b[off] = (byte) (data & 0xff);
- b[off+1] = (byte) ((data >> 8) & 0xff);
- b[off+2] = (byte) ((data >> 16) & 0xff);
- b[off+3] = (byte) ((data >> 24) & 0xff);
- }
-
- 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) {
- StringBuilder sb = new StringBuilder(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/app/src/main/java/com/sun/mail/auth/package.html b/app/src/main/java/com/sun/mail/auth/package.html
deleted file mode 100644
index 476f2ee06b..0000000000
--- a/app/src/main/java/com/sun/mail/auth/package.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- items;
-
- /**
- * Constructor
- */
- public Argument() {
- items = new ArrayList<>(1);
- }
-
- /**
- * Append the given Argument to this Argument. All items
- * from the source argument are copied into this destination
- * argument.
- *
- * @param arg the Argument to append
- * @return this
- */
- public Argument append(Argument arg) {
- items.addAll(arg.items);
- return this;
- }
-
- /**
- * 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
- * @return this
- */
- public Argument writeString(String s) {
- items.add(new AString(ASCIIUtility.getBytes(s)));
- return this;
- }
-
- /**
- * Convert the given string into bytes in the specified
- * charset, and write the bytes out as an ASTRING
- *
- * @param s String to write out
- * @param charset the charset
- * @return this
- * @exception UnsupportedEncodingException for bad charset
- */
- public Argument writeString(String s, String charset)
- throws UnsupportedEncodingException {
- if (charset == null) // convenience
- writeString(s);
- else
- items.add(new AString(s.getBytes(charset)));
- return this;
- }
-
- /**
- * Convert the given string into bytes in the specified
- * charset, and write the bytes out as an ASTRING
- *
- * @param s String to write out
- * @param charset the charset
- * @return this
- * @since JavaMail 1.6.0
- */
- public Argument writeString(String s, Charset charset) {
- if (charset == null) // convenience
- writeString(s);
- else
- items.add(new AString(s.getBytes(charset)));
- return this;
- }
-
- /**
- * Write out given string as an NSTRING, depending on the type
- * of the characters inside the string. The string should
- * contain only ASCII characters.
- *
- * @param s String to write out
- * @return this
- * @since JavaMail 1.5.1
- */
- public Argument writeNString(String s) {
- if (s == null)
- items.add(new NString(null));
- else
- items.add(new NString(ASCIIUtility.getBytes(s)));
- return this;
- }
-
- /**
- * Convert the given string into bytes in the specified
- * charset, and write the bytes out as an NSTRING
- *
- * @param s String to write out
- * @param charset the charset
- * @return this
- * @exception UnsupportedEncodingException for bad charset
- * @since JavaMail 1.5.1
- */
- public Argument writeNString(String s, String charset)
- throws UnsupportedEncodingException {
- if (s == null)
- items.add(new NString(null));
- else if (charset == null) // convenience
- writeString(s);
- else
- items.add(new NString(s.getBytes(charset)));
- return this;
- }
-
- /**
- * Convert the given string into bytes in the specified
- * charset, and write the bytes out as an NSTRING
- *
- * @param s String to write out
- * @param charset the charset
- * @return this
- * @since JavaMail 1.6.0
- */
- public Argument writeNString(String s, Charset charset) {
- if (s == null)
- items.add(new NString(null));
- else if (charset == null) // convenience
- writeString(s);
- else
- items.add(new NString(s.getBytes(charset)));
- return this;
- }
-
- /**
- * Write out given byte[] as a Literal.
- * @param b byte[] to write out
- * @return this
- */
- public Argument writeBytes(byte[] b) {
- items.add(b);
- return this;
- }
-
- /**
- * Write out given ByteArrayOutputStream as a Literal.
- * @param b ByteArrayOutputStream to be written out.
- * @return this
- */
- public Argument writeBytes(ByteArrayOutputStream b) {
- items.add(b);
- return this;
- }
-
- /**
- * Write out given data as a literal.
- * @param b Literal representing data to be written out.
- * @return this
- */
- public Argument writeBytes(Literal b) {
- items.add(b);
- return this;
- }
-
- /**
- * 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
- * @return this
- */
- public Argument writeAtom(String s) {
- items.add(new Atom(s));
- return this;
- }
-
- /**
- * Write out number.
- * @param i number
- * @return this
- */
- public Argument writeNumber(int i) {
- items.add(Integer.valueOf(i));
- return this;
- }
-
- /**
- * Write out number.
- * @param i number
- * @return this
- */
- public Argument writeNumber(long i) {
- items.add(Long.valueOf(i));
- return this;
- }
-
- /**
- * Write out as parenthesised list.
- *
- * @param c the Argument
- * @return this
- */
- public Argument writeArgument(Argument c) {
- items.add(c);
- return this;
- }
-
- /*
- * 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.get(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 NString) {
- nstring(((NString)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 {
- nastring(bytes, protocol, false);
- }
-
- /**
- * Write out given String as either NIL, QuotedString, or Literal.
- */
- private void nstring(byte[] bytes, Protocol protocol)
- throws IOException, ProtocolException {
- if (bytes == null) {
- DataOutputStream os = (DataOutputStream)protocol.getOutputStream();
- os.writeBytes("NIL");
- } else
- nastring(bytes, protocol, true);
- }
-
- private void nastring(byte[] bytes, Protocol protocol, boolean doQuote)
- 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 : doQuote;
- boolean escape = false;
- boolean utf8 = protocol.supportsUtf8();
-
- byte b;
- for (int i = 0; i < len; i++) {
- b = bytes[i];
- if (b == '\0' || b == '\r' || b == '\n' ||
- (!utf8 && ((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) <= ' ') || ((b & 0xff) > 0177)) {
- quote = true;
- if (b == '"' || b == '\\') // need to escape these characters
- escape = true;
- }
- }
-
- /*
- * Make sure the (case-independent) string "NIL" is always quoted,
- * so as not to be confused with a real NIL (handled above in nstring).
- * This is more than is necessary, but it's rare to begin with and
- * this makes it safer than doing the test in nstring above in case
- * some code calls writeString when it should call writeNString.
- */
- if (!quote && bytes.length == 3 &&
- (bytes[0] == 'N' || bytes[0] == 'n') &&
- (bytes[1] == 'I' || bytes[1] == 'i') &&
- (bytes[2] == 'L' || bytes[2] == 'l'))
- quote = 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;
- }
-}
-
-class NString {
- byte[] bytes;
-
- NString(byte[] b) {
- bytes = b;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/BadCommandException.java b/app/src/main/java/com/sun/mail/iap/BadCommandException.java
deleted file mode 100644
index 4a1b33bf6e..0000000000
--- a/app/src/main/java/com/sun/mail/iap/BadCommandException.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/iap/ByteArray.java b/app/src/main/java/com/sun/mail/iap/ByteArray.java
deleted file mode 100644
index e05110bcaf..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ByteArray.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- *
- * @param b the byte array to wrap
- * @param start start position in byte array
- * @param count number of bytes in byte array
- */
- 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.
- *
- * @param size the size of the ByteArray
- * @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.
- *
- * @return the wrapped byte array
- */
- public byte[] getBytes() {
- return bytes;
- }
-
- /**
- * Returns a new byte array that is a copy of the data.
- *
- * @return a new byte array with the bytes from start for count
- */
- public byte[] getNewBytes() {
- byte[] b = new byte[count];
- System.arraycopy(bytes, start, b, 0, count);
- return b;
- }
-
- /**
- * Returns the start position
- *
- * @return the start position
- */
- public int getStart() {
- return start;
- }
-
- /**
- * Returns the count of bytes
- *
- * @return the number of bytes
- */
- public int getCount() {
- return count;
- }
-
- /**
- * Set the count of bytes.
- *
- * @param count the number of bytes
- * @since JavaMail 1.4.1
- */
- public void setCount(int count) {
- this.count = count;
- }
-
- /**
- * Returns a ByteArrayInputStream.
- *
- * @return the ByteArrayInputStream
- */
- public ByteArrayInputStream toByteArrayInputStream() {
- return new ByteArrayInputStream(bytes, start, count);
- }
-
- /**
- * Grow the byte array by incr bytes.
- *
- * @param incr how much to grow
- * @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/app/src/main/java/com/sun/mail/iap/CommandFailedException.java b/app/src/main/java/com/sun/mail/iap/CommandFailedException.java
deleted file mode 100644
index 8c141e7ba5..0000000000
--- a/app/src/main/java/com/sun/mail/iap/CommandFailedException.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/iap/ConnectionException.java b/app/src/main/java/com/sun/mail/iap/ConnectionException.java
deleted file mode 100644
index 12dd496106..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ConnectionException.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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 p the Protocol object
- * @param r the Response
- */
- public ConnectionException(Protocol p, Response r) {
- super(r);
- this.p = p;
- }
-
- public Protocol getProtocol() {
- return p;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/Literal.java b/app/src/main/java/com/sun/mail/iap/Literal.java
deleted file mode 100644
index 14c592b274..0000000000
--- a/app/src/main/java/com/sun/mail/iap/Literal.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the size of the data
- */
- public int size();
-
- /**
- * Write the data to the OutputStream.
- *
- * @param os the output stream
- * @exception IOException for I/O errors
- */
- public void writeTo(OutputStream os) throws IOException;
-}
diff --git a/app/src/main/java/com/sun/mail/iap/LiteralException.java b/app/src/main/java/com/sun/mail/iap/LiteralException.java
deleted file mode 100644
index 660747f442..0000000000
--- a/app/src/main/java/com/sun/mail/iap/LiteralException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the response object
- */
- public LiteralException(Response r) {
- super(r.toString());
- response = r;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/ParsingException.java b/app/src/main/java/com/sun/mail/iap/ParsingException.java
deleted file mode 100644
index a1e7168a8f..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ParsingException.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/iap/Protocol.java b/app/src/main/java/com/sun/mail/iap/Protocol.java
deleted file mode 100644
index b7bd688248..0000000000
--- a/app/src/main/java/com/sun/mail/iap/Protocol.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.iap;
-
-import java.util.Properties;
-import java.io.*;
-import java.nio.channels.SocketChannel;
-import java.net.*;
-import javax.net.ssl.SSLSocket;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-
-import com.sun.mail.util.PropUtil;
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.SocketFetcher;
-import com.sun.mail.util.TraceInputStream;
-import com.sun.mail.util.TraceOutputStream;
-
-/**
- * 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 TraceInputStream traceInput; // the Tracer
- private volatile ResponseInputStream input;
-
- private TraceOutputStream traceOutput; // the Tracer
- private volatile DataOutputStream output;
-
- private int tagCounter = 0;
- private final String tagPrefix;
-
- private String localHostName;
-
- private final List handlers
- = new CopyOnWriteArrayList<>();
-
- private volatile long timestamp;
-
- // package private, to allow testing
- static final AtomicInteger tagNum = new AtomicInteger();
-
- 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
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- public Protocol(String host, int port,
- Properties props, String prefix,
- boolean isSSL, MailLogger logger)
- throws IOException, ProtocolException {
- boolean connected = false; // did constructor succeed?
- tagPrefix = computePrefix(props, prefix);
- 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));
- }
-
- /**
- * Compute the tag prefix to be used for this connection.
- * Start with "A" - "Z", then "AA" - "ZZ", and finally "AAA" - "ZZZ".
- * Wrap around after that.
- */
- private String computePrefix(Properties props, String prefix) {
- // XXX - in case someone depends on the tag prefix
- if (PropUtil.getBooleanProperty(props,
- prefix + ".reusetagprefix", false))
- return "A";
- // tag prefix, wrap around after three letters
- int n = tagNum.getAndIncrement() % (26*26*26 + 26*26 + 26);
- String tagPrefix;
- if (n < 26)
- tagPrefix = new String(new char[] { (char)('A' + n) });
- else if (n < (26*26 + 26)) {
- n -= 26;
- tagPrefix = new String(new char[] {
- (char)('A' + n/26), (char)('A' + n%26) });
- } else {
- n -= (26*26 + 26);
- tagPrefix = new String(new char[] {
- (char)('A' + n/(26*26)),
- (char)('A' + (n%(26*26))/26),
- (char)('A' + n%26) });
- }
- return tagPrefix;
- }
-
- /**
- * Constructor for debugging.
- *
- * @param in the InputStream to read from
- * @param out the PrintStream to write to
- * @param props Properties object used by this protocol
- * @param debug true to enable debugging output
- * @exception IOException for I/O errors
- */
- public Protocol(InputStream in, PrintStream out, Properties props,
- boolean debug) throws IOException {
- this.host = "localhost";
- this.props = props;
- this.quote = false;
- tagPrefix = computePrefix(props, "mail.imap");
- logger = new MailLogger(this.getClass(), "DEBUG", debug, System.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.
- *
- * @return the timestamp
- */
- public long getTimestamp() {
- return timestamp;
- }
-
- /**
- * Adds a response handler.
- *
- * @param h the response handler
- */
- public void addResponseHandler(ResponseHandler h) {
- handlers.add(h);
- }
-
- /**
- * Removed the specified response handler.
- *
- * @param h the response handler
- */
- public void removeResponseHandler(ResponseHandler h) {
- handlers.remove(h);
- }
-
- /**
- * Notify response handlers
- *
- * @param responses the responses
- */
- public void notifyResponseHandlers(Response[] responses) {
- if (handlers.isEmpty()) {
- return;
- }
-
- for (Response r : responses) {
- if (r != null) {
- for (ResponseHandler rh : handlers) {
- if (rh != null) {
- rh.handleResponse(r);
- }
- }
- }
- }
- }
-
- protected void processGreeting(Response r) throws ProtocolException {
- if (r.isBYE())
- throw new ConnectionException(this, r);
- }
-
- /**
- * Return the Protocol's InputStream.
- *
- * @return the input stream
- */
- protected ResponseInputStream getInputStream() {
- return input;
- }
-
- /**
- * Return the Protocol's OutputStream
- *
- * @return the output stream
- */
- protected OutputStream getOutputStream() {
- return output;
- }
-
- /**
- * Returns whether this Protocol supports non-synchronizing literals
- * Default is false. Subclasses should override this if required
- *
- * @return true if the server supports non-synchronizing literals
- */
- protected synchronized boolean supportsNonSyncLiterals() {
- return false;
- }
-
- public Response readResponse()
- throws IOException, ProtocolException {
- return new Response(this);
- }
-
- /**
- * Is another response available in our buffer?
- *
- * @return true if another response is in the buffer
- * @since JavaMail 1.5.4
- */
- public boolean hasResponse() {
- /*
- * XXX - Really should peek ahead in the buffer to see
- * if there's a *complete* response available, but if there
- * isn't who's going to read more data into the buffer
- * until there is?
- */
- try {
- return input.available() > 0;
- } catch (IOException ex) {
- }
- return false;
- }
-
- /**
- * 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.
- *
- * @return the buffer to use
- * @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 = tagPrefix + Integer.toString(tagCounter++); // 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);
- List v = new ArrayList<>();
- boolean done = false;
- String tag = null;
-
- // write the command
- try {
- tag = writeCommand(command, args);
- } catch (LiteralException lex) {
- v.add(lex.getResponse());
- done = true;
- } catch (Exception ex) {
- // Convert this into a BYE response
- v.add(Response.byeResponse(ex));
- done = true;
- }
-
- Response byeResp = null;
- while (!done) {
- Response r = null;
- try {
- r = readResponse();
- } catch (IOException ioex) {
- if (byeResp == null) // convert this into a BYE response
- byeResp = Response.byeResponse(ioex);
- // else, connection closed after BYE was sent
- break;
- } catch (ProtocolException pex) {
- logger.log(Level.FINE, "ignoring bad response", pex);
- continue; // skip this response
- }
-
- if (r.isBYE()) {
- byeResp = r;
- continue;
- }
-
- v.add(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.add(byeResp); // must be last
- Response[] responses = new Response[v.size()];
- v.toArray(responses);
- timestamp = System.currentTimeMillis();
- commandEnd();
- return responses;
- }
-
- /**
- * Convenience routine to handle OK, NO, BAD and BYE responses.
- *
- * @param response the response
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param cmd the command
- * @param args the arguments
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param cmd the command to issue
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- 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();
- }
-
- /**
- * Start compression on the current connection.
- * cmd is the command to issue to start compression.
- * If the command succeeds, we begin compression.
- *
- * @param cmd the command to issue
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- public synchronized void startCompression(String cmd)
- throws IOException, ProtocolException {
- // XXX - check whether compression is already enabled?
- simpleCommand(cmd, null);
-
- // need to create our own Inflater and Deflater in order to set nowrap
- Inflater inf = new Inflater(true);
- traceInput = new TraceInputStream(new InflaterInputStream(
- socket.getInputStream(), inf), traceLogger);
- traceInput.setQuote(quote);
- input = new ResponseInputStream(traceInput);
-
- // configure the Deflater
- int level = PropUtil.getIntProperty(props, prefix + ".compress.level",
- Deflater.DEFAULT_COMPRESSION);
- int strategy = PropUtil.getIntProperty(props,
- prefix + ".compress.strategy",
- Deflater.DEFAULT_STRATEGY);
- if (logger.isLoggable(Level.FINE))
- logger.log(Level.FINE,
- "Creating Deflater with compression level {0} and strategy {1}",
- new Object[] { level, strategy });
- Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
- try {
- def.setLevel(level);
- } catch (IllegalArgumentException ex) {
- logger.log(Level.FINE, "Ignoring bad compression level", ex);
- }
- try {
- def.setStrategy(strategy);
- } catch (IllegalArgumentException ex) {
- logger.log(Level.FINE, "Ignoring bad compression strategy", ex);
- }
- traceOutput = new TraceOutputStream(new DeflaterOutputStream(
- socket.getOutputStream(), def, true), traceLogger);
- traceOutput.setQuote(quote);
- output = new DataOutputStream(new BufferedOutputStream(traceOutput));
- }
-
- /**
- * Is this connection using an SSL socket?
- *
- * @return true if using SSL
- * @since JavaMail 1.4.6
- */
- public boolean isSSL() {
- return socket instanceof SSLSocket;
- }
-
- /**
- * Return the address the socket connected to.
- *
- * @return the InetAddress the socket is connected to
- * @since JavaMail 1.5.2
- */
- public InetAddress getInetAddress() {
- return socket.getInetAddress();
- }
-
- /**
- * Return the SocketChannel associated with this connection, if any.
- *
- * @return the SocketChannel
- * @since JavaMail 1.5.2
- */
- public SocketChannel getChannel() {
- SocketChannel ret = socket.getChannel();
- if (ret != null)
- return ret;
-
- // XXX - Android is broken and SSL wrapped sockets don't delegate
- // the getChannel method to the wrapped Socket
- if (socket instanceof SSLSocket) {
- try {
- Field f = socket.getClass().getDeclaredField("socket");
- f.setAccessible(true);
- Socket s = (Socket)f.get(socket);
- ret = s.getChannel();
- } catch (Exception ex) {
- // ignore anything that might go wrong
- }
- }
- return ret;
- }
-
- /**
- * Return the local SocketAddress (host and port) for this
- * end of the connection.
- *
- * @return the SocketAddress
- * @since Jakarta Mail 1.6.4
- */
- public SocketAddress getLocalSocketAddress() {
- return socket.getLocalSocketAddress();
- }
-
- /**
- * Does the server support UTF-8?
- * This implementation returns false.
- * Subclasses should override as appropriate.
- *
- * @return true if the server supports UTF-8
- * @since JavaMail 1.6.0
- */
- public boolean supportsUtf8() {
- return false;
- }
-
- /**
- * 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 <prefix>.localhost overrides
- * <prefix>.localaddress,
- * which overrides what InetAddress would tell us.
- *
- * @return the name of the local host
- */
- 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?
- *
- * @return true if protocol tracing is 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.
- */
- @Override
- protected void finalize() throws Throwable {
- try {
- disconnect();
- } finally {
- super.finalize();
- }
- }
-
- /*
- * Probe points for GlassFish monitoring.
- */
- private void commandStart(String command) { }
- private void commandEnd() { }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/ProtocolException.java b/app/src/main/java/com/sun/mail/iap/ProtocolException.java
deleted file mode 100644
index f4d9e3a5d0..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ProtocolException.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the Response
- */
- public ProtocolException(Response r) {
- super(r.toString());
- response = r;
- }
-
- /**
- * Return the offending Response object.
- *
- * @return the Response object
- */
- public Response getResponse() {
- return response;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/Response.java b/app/src/main/java/com/sun/mail/iap/Response.java
deleted file mode 100644
index fcc4369957..0000000000
--- a/app/src/main/java/com/sun/mail/iap/Response.java
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.iap;
-
-import java.io.*;
-import java.util.*;
-import java.nio.charset.StandardCharsets;
-
-import com.sun.mail.util.ASCIIUtility;
-
-/**
- * This class represents a response obtained from the input stream
- * of an IMAP server.
- *
- * @author John Mani
- * @author Bill Shannon
- */
-
-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;
- /** @since JavaMail 1.5.4 */
- protected Exception ex;
- protected boolean utf8;
-
- 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;
-
- /**
- * An ATOM is any CHAR delimited by:
- * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\' | ']'
- * (CTL is handled in readDelimString.)
- */
- private static String ATOM_CHAR_DELIM = " (){%*\"\\]";
-
- /**
- * An ASTRING_CHAR is any CHAR delimited by:
- * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\'
- * (CTL is handled in readDelimString.)
- */
- private static String ASTRING_CHAR_DELIM = " (){%*\"\\";
-
- public Response(String s) {
- this(s, true);
- }
-
- /**
- * Constructor for testing.
- *
- * @param s the response string
- * @param supportsUtf8 allow UTF-8 in response?
- * @since JavaMail 1.6.0
- */
- public Response(String s, boolean supportsUtf8) {
- if (supportsUtf8)
- buffer = s.getBytes(StandardCharsets.UTF_8);
- else
- buffer = s.getBytes(StandardCharsets.US_ASCII);
- size = buffer.length;
- utf8 = supportsUtf8;
- parse();
- }
-
- /**
- * Read a new Response from the given Protocol
- *
- * @param p the Protocol object
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- 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
- utf8 = p.supportsUtf8();
-
- parse();
- }
-
- /**
- * Copy constructor.
- *
- * @param r the Response to copy
- */
- public Response(Response r) {
- index = r.index;
- pindex = r.pindex;
- size = r.size;
- buffer = r.buffer;
- type = r.type;
- tag = r.tag;
- ex = r.ex;
- utf8 = r.utf8;
- }
-
- /**
- * Return a Response object that looks like a BYE protocol response.
- * Include the details of the exception in the response string.
- *
- * @param ex the exception
- * @return the synthetic Response object
- */
- public static Response byeResponse(Exception ex) {
- String err = "* BYE Jakarta Mail Exception: " + ex.toString();
- err = err.replace('\r', ' ').replace('\n', ' ');
- Response r = new Response(err);
- r.type |= SYNTHETIC;
- r.ex = ex;
- return r;
- }
-
- /**
- * Does the server support UTF-8?
- *
- * @return true if the server supports UTF-8
- * @since JavaMail 1.6.0
- */
- public boolean supportsUtf8() {
- return utf8;
- }
-
- 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 past any spaces. If the next non-space character is c,
- * consume it and return true. Otherwise stop at that point
- * and return false.
- *
- * @param c the character to look for
- * @return true if the character is found
- */
- public boolean isNextNonSpace(char c) {
- skipSpaces();
- if (index < size && buffer[index] == (byte)c) {
- index++;
- return true;
- }
- return false;
- }
-
- /**
- * 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 readDelimString(ATOM_CHAR_DELIM);
- }
-
- /**
- * Extract a string stopping at control characters or any
- * character in delim.
- */
- private String readDelimString(String delim) {
- skipSpaces();
-
- if (index >= size) // already at end of response
- return null;
-
- int b;
- int start = index;
- while (index < size && ((b = (((int)buffer[index])&0xff)) >= ' ') &&
- delim.indexOf((char)b) < 0 && b != 0x7f)
- index++;
-
- return 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 [].
- *
- * @param delim the delimiter character
- * @return the string
- */
- 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 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 '('
-
- // to handle buggy IMAP servers, we tolerate multiple spaces as
- // well as spaces after the left paren or before the right paren
- List result = new ArrayList<>();
- while (!isNextNonSpace(')')) {
- String s = atom ? readAtomString() : readString();
- if (s == null) // not the expected string or atom
- break;
- result.add(s);
- }
-
- return result.toArray(new String[result.size()]);
- }
-
- /**
- * 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 (plus ']').
- *
- * Any errors in parsing returns null
- *
- * ASTRING := QuotedString | Literal | 1*ASTRING_CHAR
- *
- * @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 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 toString(buffer, start, start + count);
- else
- return new ByteArray(buffer, start, count);
- } else if (parseAtoms) { // parse as ASTRING-CHARs
- int start = index; // track this, so that we can use to
- // creating ByteArrayInputStream below.
- String s = readDelimString(ASTRING_CHAR_DELIM);
- 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
- }
-
- private String toString(byte[] buffer, int start, int end) {
- return utf8 ?
- new String(buffer, start, end - start, StandardCharsets.UTF_8) :
- ASCIIUtility.toString(buffer, start, end);
- }
-
- 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.
- *
- * @return the rest of the response
- */
- public String getRest() {
- skipSpaces();
- return toString(buffer, index, size);
- }
-
- /**
- * Return the exception for a synthetic BYE response.
- *
- * @return the exception
- * @since JavaMail 1.5.4
- */
- public Exception getException() {
- return ex;
- }
-
- /**
- * Reset pointer to beginning of response.
- */
- public void reset() {
- index = pindex;
- }
-
- @Override
- public String toString() {
- return toString(buffer, 0, size);
- }
-
-}
diff --git a/app/src/main/java/com/sun/mail/iap/ResponseHandler.java b/app/src/main/java/com/sun/mail/iap/ResponseHandler.java
deleted file mode 100644
index 7d5a61c822..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ResponseHandler.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.iap;
-
-/**
- * This class
- *
- * @author John Mani
- */
-
-public interface ResponseHandler {
- public void handleResponse(Response r);
-}
diff --git a/app/src/main/java/com/sun/mail/iap/ResponseInputStream.java b/app/src/main/java/com/sun/mail/iap/ResponseInputStream.java
deleted file mode 100644
index 9e05b18f1f..0000000000
--- a/app/src/main/java/com/sun/mail/iap/ResponseInputStream.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param in the InputStream to wrap
- */
- public ResponseInputStream(InputStream in) {
- bin = new BufferedInputStream(in, 2 * 1024);
- }
-
- /**
- * Read a Response from the InputStream.
- *
- * @return ByteArray that contains the Response
- * @exception IOException for I/O errors
- */
- public ByteArray readResponse() throws IOException {
- return readResponse(null);
- }
-
- /**
- * Read a Response from the InputStream.
- *
- * @param ba the ByteArray in which to store the response, or null
- * @return ByteArray that contains the Response
- * @exception IOException for I/O errors
- */
- 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)) {
- if (b == '\n') {
- if ((idx > 0) && buffer[idx-1] == '\r')
- gotCRLF = true;
- }
- 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);
- if (actual == -1)
- throw new IOException("Connection dropped by server?");
- count -= actual;
- idx += actual;
- }
- }
- // back to top of loop to read until CRLF
- }
- ba.setCount(idx);
- return ba;
- }
-
- /**
- * How much buffered data do we have?
- *
- * @return number of bytes available
- * @exception IOException if the stream has been closed
- * @since JavaMail 1.5.4
- */
- public int available() throws IOException {
- return bin.available();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/iap/package.html b/app/src/main/java/com/sun/mail/iap/package.html
deleted file mode 100644
index 2607e33a2e..0000000000
--- a/app/src/main/java/com/sun/mail/iap/package.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.iap package
-
-
-
-
-This package includes internal IMAP support classes and
-SHOULD NOT BE USED DIRECTLY BY APPLICATIONS .
-
-
-
-
diff --git a/app/src/main/java/com/sun/mail/imap/ACL.java b/app/src/main/java/com/sun/mail/imap/ACL.java
deleted file mode 100644
index ee99c95e2c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/ACL.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- public Object clone() throws CloneNotSupportedException {
- ACL acl = (ACL)super.clone();
- acl.rights = (Rights)this.rights.clone();
- return acl;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/AppendUID.java b/app/src/main/java/com/sun/mail/imap/AppendUID.java
deleted file mode 100644
index 56c77fed82..0000000000
--- a/app/src/main/java/com/sun/mail/imap/AppendUID.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import com.sun.mail.iap.*;
-
-/**
- * Information from the APPENDUID response code
- * defined by the UIDPLUS extension -
- * RFC 4315 .
- *
- * @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/app/src/main/java/com/sun/mail/imap/CopyUID.java b/app/src/main/java/com/sun/mail/imap/CopyUID.java
deleted file mode 100644
index 3648fc5cb2..0000000000
--- a/app/src/main/java/com/sun/mail/imap/CopyUID.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import com.sun.mail.imap.protocol.UIDSet;
-
-/**
- * Information from the COPYUID response code
- * defined by the UIDPLUS extension -
- * RFC 4315 .
- *
- * @author Bill Shannon
- */
-
-public class CopyUID {
- public long uidvalidity = -1;
- public UIDSet[] src;
- public UIDSet[] dst;
-
- public CopyUID(long uidvalidity, UIDSet[] src, UIDSet[] dst) {
- this.uidvalidity = uidvalidity;
- this.src = src;
- this.dst = dst;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/DefaultFolder.java b/app/src/main/java/com/sun/mail/imap/DefaultFolder.java
deleted file mode 100644
index 9dab04fb5a..0000000000
--- a/app/src/main/java/com/sun/mail/imap/DefaultFolder.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.Folder;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.MethodNotSupportedException;
-import com.sun.mail.iap.ProtocolException;
-import com.sun.mail.imap.protocol.IMAPProtocol;
-import com.sun.mail.imap.protocol.ListInfo;
-
-/**
- * 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
- }
-
- @Override
- public synchronized String getName() {
- return fullName;
- }
-
- @Override
- public Folder getParent() {
- return null;
- }
-
- @Override
- public synchronized Folder[] list(final String pattern)
- throws MessagingException {
- ListInfo[] li = null;
-
- li = (ListInfo[])doCommand(new ProtocolCommand() {
- @Override
- 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;
- }
-
- @Override
- public synchronized Folder[] listSubscribed(final String pattern)
- throws MessagingException {
- ListInfo[] li = null;
-
- li = (ListInfo[])doCommand(new ProtocolCommand() {
- @Override
- 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;
- }
-
- @Override
- public boolean hasNewMessages() throws MessagingException {
- // Not applicable on DefaultFolder
- return false;
- }
-
- @Override
- public Folder getFolder(String name) throws MessagingException {
- return ((IMAPStore)store).newIMAPFolder(name, UNKNOWN_SEPARATOR);
- }
-
- @Override
- public boolean delete(boolean recurse) throws MessagingException {
- // Not applicable on DefaultFolder
- throw new MethodNotSupportedException("Cannot delete Default Folder");
- }
-
- @Override
- public boolean renameTo(Folder f) throws MessagingException {
- // Not applicable on DefaultFolder
- throw new MethodNotSupportedException("Cannot rename Default Folder");
- }
-
- @Override
- public void appendMessages(Message[] msgs) throws MessagingException {
- // Not applicable on DefaultFolder
- throw new MethodNotSupportedException("Cannot append to Default Folder");
- }
-
- @Override
- public Message[] expunge() throws MessagingException {
- // Not applicable on DefaultFolder
- throw new MethodNotSupportedException("Cannot expunge Default Folder");
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/IMAPBodyPart.java b/app/src/main/java/com/sun/mail/imap/IMAPBodyPart.java
deleted file mode 100644
index 181bb94512..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPBodyPart.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.PropUtil;
-import com.sun.mail.util.ReadableMime;
-import com.sun.mail.util.LineOutputStream;
-import com.sun.mail.util.SharedByteArrayOutputStream;
-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.
- */
- @Override
- protected void updateHeaders() {
- return;
- }
-
- @Override
- public int getSize() throws MessagingException {
- return bs.size;
- }
-
- @Override
- public int getLineCount() throws MessagingException {
- return bs.lines;
- }
-
- @Override
- public String getContentType() throws MessagingException {
- return type;
- }
-
- @Override
- public String getDisposition() throws MessagingException {
- return bs.disposition;
- }
-
- @Override
- public void setDisposition(String disposition) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public String getEncoding() throws MessagingException {
- return bs.encoding;
- }
-
- @Override
- public String getContentID() throws MessagingException {
- return bs.id;
- }
-
- @Override
- public String getContentMD5() throws MessagingException {
- return bs.md5;
- }
-
- @Override
- public void setContentMD5(String md5) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- 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;
- }
-
- @Override
- public void setDescription(String description, String charset)
- throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public String getFileName() throws MessagingException {
- String filename = null;
- if (bs.dParams != null)
- filename = bs.dParams.get("filename");
- if ((filename == null || filename.isEmpty()) && 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;
- }
-
- @Override
- public void setFileName(String filename) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- 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) {
- message.forceCheckExpunged(); // may throw MessageRemovedException
- // nope, the server doesn't think it's expunged.
- // can't tell the difference between the server returning NIL
- // and some other error that caused null to be returned above,
- // so we'll just assume it was empty content.
- is = new ByteArrayInputStream(new byte[0]);
- }
- 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(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
- */
- @Override
- 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());
- }
-
- @Override
- 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();
- }
-
- @Override
- public void setDataHandler(DataHandler content) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public void setContent(Object o, String type) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public void setContent(Multipart mp) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public String[] getHeader(String name) throws MessagingException {
- loadHeaders();
- return super.getHeader(name);
- }
-
- @Override
- public void setHeader(String name, String value)
- throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public void addHeader(String name, String value)
- throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public void removeHeader(String name) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public Enumeration getAllHeaders() throws MessagingException {
- loadHeaders();
- return super.getAllHeaders();
- }
-
- @Override
- public Enumeration getMatchingHeaders(String[] names)
- throws MessagingException {
- loadHeaders();
- return super.getMatchingHeaders(names);
- }
-
- @Override
- public Enumeration getNonMatchingHeaders(String[] names)
- throws MessagingException {
- loadHeaders();
- return super.getNonMatchingHeaders(names);
- }
-
- @Override
- public void addHeaderLine(String line) throws MessagingException {
- throw new IllegalWriteException("IMAPBodyPart is read-only");
- }
-
- @Override
- public Enumeration getAllHeaderLines() throws MessagingException {
- loadHeaders();
- return super.getAllHeaderLines();
- }
-
- @Override
- public Enumeration getMatchingHeaderLines(String[] names)
- throws MessagingException {
- loadHeaders();
- return super.getMatchingHeaderLines(names);
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/imap/IMAPFolder.java b/app/src/main/java/com/sun/mail/imap/IMAPFolder.java
deleted file mode 100644
index 74f2f56b96..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPFolder.java
+++ /dev/null
@@ -1,4153 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.util.Date;
-import java.util.Vector;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Locale;
-import java.util.logging.Level;
-import java.io.*;
-import java.net.SocketTimeoutException;
-import java.nio.channels.SocketChannel;
-
-import javax.mail.*;
-import javax.mail.event.*;
-import javax.mail.internet.*;
-import javax.mail.search.*;
-
-import com.sun.mail.util.PropUtil;
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.CRLFOutputStream;
-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 #open(int,com.sun.mail.imap.ResyncData) open(int,ResyncData)}
- * method and {@link com.sun.mail.imap.ResyncData ResyncData} class supports
- * the IMAP CONDSTORE and QRESYNC extensions.
- * Refer to RFC 4551
- * and RFC 5162
- * 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 RFC 3501, 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 volatile 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 IdleManager idleManager;
-
- 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 uidNotSticky = false; // RFC 4315
- private volatile long highestmodseq = -1; // RFC 4551 - CONDSTORE
- 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
- */
- @Deprecated
- public static final FetchProfileItem SIZE =
- new FetchProfileItem("SIZE");
-
- /**
- * MESSAGE is a fetch profile item that can be included in a
- * FetchProfile during a fetch request to a Folder.
- * This item indicates that the entire messages (headers and body,
- * including all "attachments") in the specified
- * range are desired to be prefetched. Note that the entire message
- * content is cached in memory while the Folder is open. The cached
- * message will be parsed locally to return header information and
- * message content.
- *
- * An example of how a client uses this is below:
- *
- *
- * FetchProfile fp = new FetchProfile();
- * fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
- * folder.fetch(msgs, fp);
- *
- *
- *
- * @since JavaMail 1.5.2
- */
- public static final FetchProfileItem MESSAGE =
- new FetchProfileItem("MESSAGE");
-
- /**
- * INTERNALDATE is a fetch profile item that can be included in a
- * FetchProfile during a fetch request to a Folder.
- * This item indicates that the IMAP INTERNALDATE values
- * (received date) of the 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.INTERNALDATE);
- * folder.fetch(msgs, fp);
- *
- *
- *
- * @since JavaMail 1.5.5
- */
- public static final FetchProfileItem INTERNALDATE =
- new FetchProfileItem("INTERNALDATE");
- }
-
- /**
- * 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
- * @param isNamespace if this folder represents a namespace
- */
- 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().getDebug(), store.getSession().getDebugOut());
- connectionPoolLogger = 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.
- *
- * @param li the ListInfo for this folder
- * @param store the store containing this 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.
- */
- @Override
- 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.
- */
- @Override
- public String getFullName() {
- return fullName;
- }
-
- /**
- * Get this folder's parent.
- */
- @Override
- 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.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- public Folder[] list(String pattern) throws MessagingException {
- return doList(pattern, false);
- }
-
- /**
- * List all subscribed subfolders matching the specified pattern.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- public synchronized char getSeparator() throws MessagingException {
- if (separator == UNKNOWN_SEPARATOR) {
- ListInfo[] li = null;
-
- li = (ListInfo[])doCommand(new ProtocolCommand() {
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- public synchronized void setSubscribed(final boolean subscribe)
- throws MessagingException {
- doCommandIgnoreFailure(new ProtocolCommand() {
- @Override
- 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.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- public synchronized boolean hasNewMessages() throws MessagingException {
- synchronized (messageCacheLock) {
- if (opened) { // If we are open, we already have this information
- // Folder is open, make sure information is up to date
- // 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() {
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- 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() {
- @Override
- 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.
- */
- @Override
- public synchronized void open(int mode) throws MessagingException {
- open(mode, null);
- }
-
- /**
- * Open this folder in the given mode, with the given
- * resynchronization data.
- *
- * @param mode the open mode (Folder.READ_WRITE or Folder.READ_ONLY)
- * @param rd the ResyncData instance
- * @return a List of MailEvent instances, or null if none
- * @exception MessagingException if the open fails
- * @since JavaMail 1.5.1
- */
- public synchronized List open(int mode, ResyncData rd)
- 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);
-
- List openEvents = null;
- 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 {
- /*
- * Enable QRESYNC or CONDSTORE if needed and not enabled.
- * QRESYNC implies CONDSTORE, but servers that support
- * QRESYNC are not required to support just CONDSTORE
- * per RFC 5162.
- */
- if (rd != null) {
- if (rd == ResyncData.CONDSTORE) {
- if (!protocol.isEnabled("CONDSTORE") &&
- !protocol.isEnabled("QRESYNC")) {
- if (protocol.hasCapability("CONDSTORE"))
- protocol.enable("CONDSTORE");
- else
- protocol.enable("QRESYNC");
- }
- } else {
- if (!protocol.isEnabled("QRESYNC"))
- protocol.enable("QRESYNC");
- }
- }
-
- if (mode == READ_ONLY)
- mi = protocol.examine(fullName, rd);
- else
- mi = protocol.select(fullName, rd);
- } 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 {
- throw logoutAndThrow(pex.getMessage(), pex);
- } finally {
- releaseProtocol(false);
- }
- }
-
- 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
- ReadOnlyFolderException ife = new ReadOnlyFolderException(
- this, "Cannot open in desired mode");
- throw cleanupAndThrow(ife);
- }
- }
-
- // 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;
- uidNotSticky = mi.uidNotSticky;
- highestmodseq = mi.highestmodseq;
-
- // Create the message cache of appropriate size
- messageCache = new MessageCache(this, (IMAPStore)store, total);
-
- // process saved responses and return corresponding events
- if (mi.responses != null) {
- openEvents = new ArrayList<>();
- for (IMAPResponse ir : mi.responses) {
- if (ir.keyEquals("VANISHED")) {
- // "VANISHED" SP ["(EARLIER)"] SP known-uids
- String[] s = ir.readAtomStringList();
- // check that it really is "EARLIER"
- if (s == null || s.length != 1 ||
- !s[0].equalsIgnoreCase("EARLIER"))
- continue; // it's not, what to do with it here?
- String uids = ir.readAtom();
- UIDSet[] uidset = UIDSet.parseUIDSets(uids);
- long[] luid = UIDSet.toArray(uidset, uidnext);
- if (luid != null && luid.length > 0)
- openEvents.add(
- new MessageVanishedEvent(this, luid));
- } else if (ir.keyEquals("FETCH")) {
- assert ir instanceof FetchResponse :
- "!ir instanceof FetchResponse";
- Message msg = processFetchResponse((FetchResponse)ir);
- if (msg != null)
- openEvents.add(new MessageChangedEvent(this,
- MessageChangedEvent.FLAGS_CHANGED, msg));
- }
- }
- }
- } // 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);
-
- return openEvents;
- }
-
- private MessagingException cleanupAndThrow(MessagingException ife) {
- try {
- try {
- // close mailbox and return connection
- protocol.close();
- releaseProtocol(true);
- } catch (ProtocolException pex) {
- // something went wrong, close connection
- try {
- addSuppressed(ife, logoutAndThrow(pex.getMessage(), pex));
- } finally {
- releaseProtocol(false);
- }
- }
- } catch (Throwable thr) {
- addSuppressed(ife, thr);
- }
- return ife;
- }
-
- private MessagingException logoutAndThrow(String why, ProtocolException t) {
- MessagingException ife = new MessagingException(why, t);
- try {
- protocol.logout();
- } catch (Throwable thr) {
- addSuppressed(ife, thr);
- }
- return ife;
- }
-
- private void addSuppressed(Throwable ife, Throwable thr) {
- if (isRecoverable(thr)) {
- ife.addSuppressed(thr);
- } else {
- thr.addSuppressed(ife);
- if (thr instanceof Error) {
- throw (Error) thr;
- }
- if (thr instanceof RuntimeException) {
- throw (RuntimeException) thr;
- }
- throw new RuntimeException("unexpected exception", thr);
- }
- }
-
- private boolean isRecoverable(Throwable t) {
- return (t instanceof Exception) || (t instanceof LinkageError);
- }
-
- /**
- * Prefetch attributes, based on the given FetchProfile.
- */
- @Override
- public synchronized void fetch(Message[] msgs, FetchProfile fp)
- throws MessagingException {
- // cache this information in case connection is closed and
- // protocol is set to null
- boolean isRev1;
- FetchItem[] fitems;
- synchronized (messageCacheLock) {
- checkOpened();
- isRev1 = protocol.isREV1();
- fitems = protocol.getFetchItems();
- }
-
- StringBuilder command = new StringBuilder();
- 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 (isRev1)
- command.append(first ?
- "BODY.PEEK[HEADER]" : " BODY.PEEK[HEADER]");
- else
- command.append(first ? "RFC822.HEADER" : " RFC822.HEADER");
- first = false;
- }
- if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE)) {
- allHeaders = true;
- if (isRev1)
- command.append(first ? "BODY.PEEK[]" : " BODY.PEEK[]");
- else
- command.append(first ? "RFC822" : " RFC822");
- first = false;
- }
- if (fp.contains(FetchProfile.Item.SIZE) ||
- fp.contains(IMAPFolder.FetchProfileItem.SIZE)) {
- command.append(first ? "RFC822.SIZE" : " RFC822.SIZE");
- first = false;
- }
- if (fp.contains(IMAPFolder.FetchProfileItem.INTERNALDATE)) {
- command.append(first ? "INTERNALDATE" : " INTERNALDATE");
- 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, isRev1));
- }
- }
-
- /*
- * Add any additional extension fetch items.
- */
- 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) {
-
- // check again to make sure folder is still open
- checkOpened();
-
- // Apply the test, and get the sequence-number set for
- // the messages that need to be prefetched.
- MessageSet[] msgsets = Utility.toMessageSetSorted(msgs, condition);
-
- if (msgsets == null)
- // We already have what we need.
- return;
-
- Response[] r = null;
- // to collect non-FETCH responses & unsolicited FETCH FLAG responses
- List v = new ArrayList<>();
- 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.add(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.add(f);
- }
-
- // Dispatch any unsolicited responses
- if (!v.isEmpty()) {
- Response[] responses = new Response[v.size()];
- v.toArray(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.
- *
- * @return the IMAP FETCH items to request
- * @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.
- *
- * @param msgnum the message sequence number
- * @return the new IMAPMessage object
- * @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, boolean isRev1) {
- StringBuilder sb;
-
- if (isRev1)
- sb = new StringBuilder("BODY.PEEK[HEADER.FIELDS (");
- else
- sb = new StringBuilder("RFC822.HEADER.LINES (");
-
- for (int i = 0; i < hdrs.length; i++) {
- if (i > 0)
- sb.append(" ");
- sb.append(hdrs[i]);
- }
-
- if (isRev1)
- sb.append(")]");
- else
- sb.append(")");
-
- return sb.toString();
- }
-
- /**
- * Set the specified flags for the given array of messages.
- */
- @Override
- 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.toMessageSetSorted(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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- public synchronized void close(boolean expunge) throws MessagingException {
- close(expunge, false);
- }
-
- /**
- * Close this folder without waiting for the server.
- *
- * @exception MessagingException for failures
- */
- 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;
-
- boolean reuseProtocol = true;
- 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 {
- // Unselect isn't supported so we need to
- // select a folder to cause this one to be
- // deselected without expunging messages.
- // We try to do that by reopening the current
- // folder read-only. If the current folder
- // was renamed out from under us, the EXAMINE
- // might fail, but that's ok because it still
- // leaves us with the folder deselected.
- if (protocol != null) {
- boolean selected = true;
- try {
- protocol.examine(fullName);
- // success, folder still selected
- } catch (CommandFailedException ex) {
- // EXAMINE failed, folder is no
- // longer selected
- selected = false;
- }
- if (selected && protocol != null)
- protocol.close();
- }
- }
- } catch (ProtocolException pex2) {
- reuseProtocol = false; // something went wrong
- }
- } 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(reuseProtocol);
- }
- }
- }
-
- // 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
- messageCacheLock.notifyAll(); // wake up anyone waiting
- notifyConnectionListeners(ConnectionEvent.CLOSED);
- }
-
- /**
- * Check whether this connection is really open.
- */
- @Override
- 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.
- */
- @Override
- public synchronized Flags getPermanentFlags() {
- if (permanentFlags == null)
- return null;
- return (Flags)(permanentFlags.clone());
- }
-
- /**
- * Get the total message count.
- */
- @Override
- public synchronized int getMessageCount() throws MessagingException {
- synchronized (messageCacheLock) {
- if (opened) {
- // Folder is open, we know what the total message count is ..
- // 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);
- }
- }
- }
-
- // If this folder is not yet open, we use STATUS to
- // get the total message count
- checkExists();
- 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);
- }
- }
-
- /**
- * Get the new message count.
- */
- @Override
- public synchronized int getNewMessageCount() throws MessagingException {
- synchronized (messageCacheLock) {
- if (opened) {
- // Folder is open, we know what the new message count is ..
- // 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);
- }
- }
- }
-
- // If this folder is not yet open, we use STATUS to
- // get the new message count
- checkExists();
- 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);
- }
- }
-
- /**
- * Get the unread message count.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- public synchronized Message getMessage(int msgnum)
- throws MessagingException {
- checkOpened();
- checkRange(msgnum);
-
- return messageCache.getMessage(msgnum);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized Message[] getMessages() throws MessagingException {
- /*
- * Need to override Folder method to throw FolderClosedException
- * instead of IllegalStateException if not really closed.
- */
- checkOpened();
- int total = getMessageCount();
- Message[] msgs = new Message[total];
- for (int i = 1; i <= total; i++)
- msgs[i - 1] = messageCache.getMessage(i);
- return msgs;
- }
-
- /**
- * Append the given messages into this folder.
- */
- @Override
- 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() {
- @Override
- 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 4315 .
- *
- * @param msgs the messages to append
- * @return array of AppendUID objects
- * @exception MessagingException for failures
- * @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() {
- @Override
- 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 4315 .
- *
- * @param msgs the messages to add
- * @return the messages in this folder
- * @exception MessagingException for failures
- * @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.
- */
- @Override
- public synchronized void copyMessages(Message[] msgs, Folder folder)
- throws MessagingException {
- copymoveMessages(msgs, folder, false);
- }
-
- /**
- * Copy the specified messages from this folder, to the
- * specified destination.
- * 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 copied message.
- *
- * Depends on the COPYUID response code defined by the
- * UIDPLUS extension -
- * RFC 4315 .
- *
- * @param msgs the messages to copy
- * @param folder the folder to copy the messages to
- * @return array of AppendUID objects
- * @exception MessagingException for failures
- * @since JavaMail 1.5.1
- */
- public synchronized AppendUID[] copyUIDMessages(Message[] msgs,
- Folder folder) throws MessagingException {
- return copymoveUIDMessages(msgs, folder, false);
- }
-
- /**
- * Move the specified messages from this folder, to the
- * specified destination.
- *
- * Depends on the MOVE extension
- * (RFC 6851 ).
- *
- * @param msgs the messages to move
- * @param folder the folder to move the messages to
- * @exception MessagingException for failures
- *
- * @since JavaMail 1.5.4
- */
- public synchronized void moveMessages(Message[] msgs, Folder folder)
- throws MessagingException {
- copymoveMessages(msgs, folder, true);
- }
-
- /**
- * Move the specified messages from this folder, to the
- * specified destination.
- * 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 moved message.
- *
- * Depends on the MOVE extension
- * (RFC 6851 )
- * and the COPYUID response code defined by the
- * UIDPLUS extension
- * (RFC 4315 ).
- *
- * @param msgs the messages to move
- * @param folder the folder to move the messages to
- * @return array of AppendUID objects
- * @exception MessagingException for failures
- * @since JavaMail 1.5.4
- */
- public synchronized AppendUID[] moveUIDMessages(Message[] msgs,
- Folder folder) throws MessagingException {
- return copymoveUIDMessages(msgs, folder, true);
- }
-
- /**
- * Copy or move the specified messages from this folder, to the
- * specified destination.
- *
- * @since JavaMail 1.5.4
- */
- private synchronized void copymoveMessages(Message[] msgs, Folder folder,
- boolean move) 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");
- if (move)
- p.move(ms, folder.getFullName());
- else
- 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.
- if (move)
- throw new MessagingException(
- "Move between stores not supported");
- else
- super.copyMessages(msgs, folder);
- }
-
- /**
- * Copy or move the specified messages from this folder, to the
- * specified destination.
- * 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 copied message.
- *
- * Depends on the COPYUID response code defined by the
- * UIDPLUS extension -
- * RFC 4315 .
- * Move depends on the MOVE extension -
- * RFC 6851 .
- *
- * @param msgs the messages to copy
- * @param folder the folder to copy the messages to
- * @param move move instead of copy?
- * @return array of AppendUID objects
- * @exception MessagingException for failures
- * @since JavaMail 1.5.4
- */
- private synchronized AppendUID[] copymoveUIDMessages(Message[] msgs,
- Folder folder, boolean move) throws MessagingException {
- checkOpened();
-
- if (msgs.length == 0) // boundary condition
- return null;
-
- // the destination must belong to our same store
- if (folder.getStore() != store) // destination is a different store.
- throw new MessagingException(
- move ?
- "can't moveUIDMessages to a different store" :
- "can't copyUIDMessages to a different store");
-
- // call fetch to make sure we have all the UIDs
- // necessary to interpret the COPYUID response
- FetchProfile fp = new FetchProfile();
- fp.add(UIDFolder.FetchProfileItem.UID);
- fetch(msgs, fp);
- // XXX - could pipeline the FETCH with the COPY/MOVE below
-
- synchronized (messageCacheLock) {
- try {
- IMAPProtocol p = getProtocol();
- // XXX - messages have to be from this Folder, who checks?
- MessageSet[] ms = Utility.toMessageSet(msgs, null);
- if (ms == null)
- throw new MessageRemovedException(
- "Messages have been removed");
- CopyUID cuid;
- if (move)
- cuid = p.moveuid(ms, folder.getFullName());
- else
- cuid = p.copyuid(ms, folder.getFullName());
-
- /*
- * Correlate source UIDs with destination UIDs.
- * This won't be time or space efficient if there's
- * a lot of messages.
- *
- * In order to make sense of the returned UIDs, we need
- * the UIDs for every one of the original messages.
- * We fetch them above, to make sure we have them.
- * This is critical for MOVE since after the MOVE the
- * messages are gone/expunged.
- *
- * Assume the common case is that the messages are
- * in order by UID. Map the returned source
- * UIDs to their corresponding Message objects.
- * Step through the msgs array looking for the
- * Message object in the returned source message
- * list. Most commonly the source message (UID)
- * for the Nth original message will be in the Nth
- * position in the returned source message (UID)
- * list. Thus, the destination UID is in the Nth
- * position in the returned destination UID list.
- * But if the source message isn't where expected,
- * we have to search the entire source message
- * list, starting from where we expect it and
- * wrapping around until we've searched it all.
- * (Gmail will often return the lists in an unexpected order.)
- *
- * A possible optimization:
- * If the number of UIDs returned is the same as the
- * number of messages being copied/moved, we could
- * sort the source messages by message number, sort
- * the source and destination parallel arrays by source
- * UID, and the resulting message and destination UID
- * arrays will correspond.
- *
- * If the returned UID array size is different, some
- * message was expunged while we were trying to copy/move it.
- * This should be rare but would mean falling back to the
- * general algorithm.
- */
- long[] srcuids = UIDSet.toArray(cuid.src);
- long[] dstuids = UIDSet.toArray(cuid.dst);
- // map source UIDs to Message objects
- // XXX - could inline/optimize this
- Message[] srcmsgs = getMessagesByUID(srcuids);
- AppendUID[] result = new AppendUID[msgs.length];
- for (int i = 0; i < msgs.length; i++) {
- int j = i;
- do {
- if (msgs[i] == srcmsgs[j]) {
- result[i] = new AppendUID(
- cuid.uidvalidity, dstuids[j]);
- break;
- }
- j++;
- if (j >= srcmsgs.length)
- j = 0;
- } while (j != i);
- }
- return result;
- } 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);
- }
- }
- }
-
- /**
- * Expunge all messages marked as DELETED.
- */
- @Override
- 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 4315 .
- *
- * @param msgs the messages to expunge
- * @return the expunged messages
- * @exception MessagingException for failures
- */
- 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(Long.valueOf(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.
- * If the property mail.imap.throwsearchexception is true,
- * and the search term is too complex for the IMAP protocol,
- * SearchException is thrown. Otherwise, if the search term is too
- * complex, super.search is called to do the search on
- * the client.
- *
- * @param term the search term
- * @return the messages that match
- * @exception SearchException if mail.imap.throwsearchexception is
- * true and the search is too complex for the IMAP protocol
- * @exception MessagingException for other failures
- */
- @Override
- public synchronized Message[] search(SearchTerm term)
- throws MessagingException {
- checkOpened();
-
- try {
- Message[] matchMsgs = null;
-
- synchronized(messageCacheLock) {
- int[] matches = getProtocol().search(term);
- if (matches != null)
- matchMsgs = getMessagesBySeqNumbers(matches);
- }
- return matchMsgs;
-
- } catch (CommandFailedException cfx) {
- // unsupported charset or search criterion
- return super.search(term);
- } catch (SearchException sex) {
- // too complex for IMAP
- if (((IMAPStore)store).throwSearchException())
- throw sex;
- 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.
- */
- @Override
- 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.toMessageSetSorted(msgs, null);
- if (ms == null)
- throw new MessageRemovedException(
- "Messages have been removed");
- int[] matches = p.search(ms, term);
- if (matches != null)
- matchMsgs = getMessagesBySeqNumbers(matches);
- }
- 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 .
- *
- * @param term the SortTerms
- * @return the messages in sorted order
- * @exception MessagingException for failures
- * @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 .
- *
- * @param term the SortTerms
- * @param sterm the SearchTerm
- * @return the messages in sorted order
- * @exception MessagingException for failures
- * @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 = getMessagesBySeqNumbers(matches);
- }
- 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.
- */
- @Override
- public synchronized void addMessageCountListener(MessageCountListener l) {
- super.addMessageCountListener(l);
- hasMessageCountListener = true;
- }
-
- /***********************************************************
- * UIDFolder interface methods
- **********************************************************/
-
- /**
- * Returns the UIDValidity for this folder.
- */
- @Override
- 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);
- }
-
- if (status == null)
- throw new MessagingException("Cannot obtain UIDValidity");
- 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
- * @exception MessagingException for failures
- * @since JavaMail 1.3.3
- */
- @Override
- 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);
- }
-
- if (status == null)
- throw new MessagingException("Cannot obtain UIDNext");
- return status.uidnext;
- }
-
- /**
- * Get the Message corresponding to the given UID.
- * If no such message exists, null is returned.
- */
- @Override
- public synchronized Message getMessageByUID(long uid)
- throws MessagingException {
- checkOpened(); // insure folder is open
-
- IMAPMessage m = null;
-
- try {
- synchronized(messageCacheLock) {
- Long l = Long.valueOf(uid);
-
- if (uidTable != null) {
- // Check in uidTable
- m = uidTable.get(l);
- if (m != null) // found it
- return m;
- } else
- uidTable = new Hashtable<>();
-
- // Check with the server
- // Issue UID FETCH command
- getProtocol().fetchSequenceNumber(uid);
-
- if (uidTable != null) {
- // Check in uidTable
- m = uidTable.get(l);
- if (m != null) // found it
- return 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.
- */
- @Override
- 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
- long[] ua = getProtocol().fetchSequenceNumbers(start, end);
-
- List ma = new ArrayList<>();
- // NOTE: Below must be within messageCacheLock region
- for (int i = 0; i < ua.length; i++) {
- Message m = uidTable.get(Long.valueOf(ua[i]));
- if (m != null) // found it
- ma.add(m);
- }
- msgs = ma.toArray(new Message[ma.size()]);
- }
- } 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.
- */
- @Override
- public synchronized Message[] getMessagesByUID(long[] uids)
- throws MessagingException {
- checkOpened(); // insure that folder is open
-
- try {
- synchronized(messageCacheLock) {
- long[] unavailUids = uids;
- if (uidTable != null) {
- // to collect unavailable UIDs
- List v = new ArrayList<>();
- for (long uid : uids) {
- if (!uidTable.containsKey(uid)) {
- // This UID has not been loaded yet.
- v.add(uid);
- }
- }
-
- int vsize = v.size();
- unavailUids = new long[vsize];
- for (int i = 0; i < vsize; i++) {
- unavailUids[i] = v.get(i);
- }
- } else
- uidTable = new Hashtable<>();
-
- if (unavailUids.length > 0) {
- // Issue UID FETCH request for given uids
- getProtocol().fetchSequenceNumbers(unavailUids);
- }
-
- // 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(Long.valueOf(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.
- */
- @Override
- 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
-
- if (!(message instanceof IMAPMessage))
- throw new MessagingException("message is not an IMAPMessage");
- 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(Long.valueOf(uid), m);
- }
- } catch (ConnectionException cex) {
- throw new FolderClosedException(this, cex.getMessage());
- } catch (ProtocolException pex) {
- throw new MessagingException(pex.getMessage(), pex);
- }
- }
-
- return uid;
- }
-
- /**
- * Servers that support the UIDPLUS extension
- * (RFC 4315 )
- * may indicate that this folder does not support persistent UIDs;
- * that is, UIDVALIDITY will be different each time the folder is
- * opened. Only valid when the folder is open.
- *
- * @return true if UIDs are not sticky
- * @exception MessagingException for failures
- * @exception IllegalStateException if the folder isn't open
- * @see "RFC 4315"
- * @since JavaMail 1.6.0
- */
- public synchronized boolean getUIDNotSticky() throws MessagingException {
- checkOpened();
- return uidNotSticky;
- }
-
- /**
- * Get or create Message objects for the UIDs.
- */
- private Message[] createMessagesForUIDs(long[] uids) {
- IMAPMessage[] msgs = new IMAPMessage[uids.length];
- for (int i = 0; i < uids.length; i++) {
- IMAPMessage m = null;
- if (uidTable != null)
- m = uidTable.get(Long.valueOf(uids[i]));
- if (m == null) {
- // fake it, we don't know what message this really is
- m = newIMAPMessage(-1); // no sequence number
- m.setUID(uids[i]);
- m.setExpunged(true);
- }
- msgs[i++] = m;
- }
- return msgs;
- }
-
- /**
- * Returns the HIGHESTMODSEQ for this folder.
- *
- * @return the HIGHESTMODSEQ value
- * @exception MessagingException for failures
- * @see "RFC 4551"
- * @since JavaMail 1.5.1
- */
- public synchronized long getHighestModSeq() throws MessagingException {
- if (opened) // we already have this information
- return highestmodseq;
-
- IMAPProtocol p = null;
- Status status = null;
-
- try {
- p = getStoreProtocol(); // XXX
- if (!p.hasCapability("CONDSTORE"))
- throw new BadCommandException("CONDSTORE not supported");
- String[] item = { "HIGHESTMODSEQ" };
- status = p.status(fullName, item);
- } catch (BadCommandException bex) {
- // Probably a RFC1730 server
- throw new MessagingException("Cannot obtain HIGHESTMODSEQ", 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);
- }
-
- if (status == null)
- throw new MessagingException("Cannot obtain HIGHESTMODSEQ");
- return status.highestmodseq;
- }
-
- /**
- * Get the messages that have been changed since the given MODSEQ value.
- * Also, prefetch the flags for the messages.
- *
- * The server must support the CONDSTORE extension.
- *
- * @param start the first message number
- * @param end the last message number
- * @param modseq the MODSEQ value
- * @return the changed messages
- * @exception MessagingException for failures
- * @see "RFC 4551"
- * @since JavaMail 1.5.1
- */
- public synchronized Message[] getMessagesByUIDChangedSince(
- long start, long end, long modseq)
- throws MessagingException {
- checkOpened(); // insure that folder is open
-
- try {
- synchronized (messageCacheLock) {
- IMAPProtocol p = getProtocol();
- if (!p.hasCapability("CONDSTORE"))
- throw new BadCommandException("CONDSTORE not supported");
-
- // Issue FETCH for given range
- int[] nums = p.uidfetchChangedSince(start, end, modseq);
- return getMessagesBySeqNumbers(nums);
- }
- } catch(ConnectionException cex) {
- throw new FolderClosedException(this, cex.getMessage());
- } catch (ProtocolException pex) {
- throw new MessagingException(pex.getMessage(), pex);
- }
- }
-
- /**
- * 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() {
- @Override
- 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() {
- @Override
- 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() {
- @Override
- 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() {
- @Override
- 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() {
- @Override
- 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() {
- @Override
- 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() {
- @Override
- 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.
- *
- * @return array of attributes for this folder
- * @exception MessagingException for failures
- * @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] : 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.
- *
- * @param once only do one notification?
- * @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 {
- synchronized (this) {
- /*
- * We can't support the idle method if we're using SocketChannels
- * because SocketChannels don't allow simultaneous read and write.
- * If we're blocked in a read waiting for IDLE responses, we can't
- * send the DONE message to abort the IDLE. Sigh.
- * XXX - We could do select here too, like IdleManager, instead
- * of blocking in read, but that's more complicated.
- */
- if (protocol != null && protocol.getChannel() != null)
- throw new MessagingException(
- "idle method not supported with SocketChannels");
- }
- if (!startIdle(null))
- 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.
- *
- * The messageCacheLock is held in handleIdle while processing
- * the responses so that we can update the number of messages
- * in the folder (for example).
- */
- for (;;) {
- if (!handleIdle(once))
- break;
- }
-
- /*
- * 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) {
- // restore the interrupted state, which callers might depend on
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Start the IDLE command and put this folder into the IDLE state.
- * IDLE processing is done later in handleIdle(), e.g., called from
- * the IdleManager.
- *
- * @return true if IDLE started, false otherwise
- * @exception MessagingException if the server doesn't support the
- * IDLE extension
- * @exception IllegalStateException if the folder isn't open
- * @since JavaMail 1.5.2
- */
- boolean startIdle(final IdleManager im) throws MessagingException {
- // ASSERT: Must NOT be called with this folder's
- // synchronization lock held.
- assert !Thread.holdsLock(this);
- synchronized(this) {
- checkOpened();
- if (im != null && idleManager != null && im != idleManager)
- throw new MessagingException(
- "Folder already being watched by another IdleManager");
- Boolean started = (Boolean)doOptionalCommand("IDLE not supported",
- new ProtocolCommand() {
- @Override
- public Object doCommand(IMAPProtocol p)
- throws ProtocolException {
- // if the IdleManager is already watching this folder,
- // there's nothing to do here
- if (idleState == IDLE &&
- im != null && im == idleManager)
- return Boolean.TRUE; // already watching it
- if (idleState == RUNNING) {
- p.idleStart();
- logger.finest("startIdle: set to IDLE");
- idleState = IDLE;
- idleManager = im;
- 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) {
- // restore the interrupted state, which callers
- // might depend on
- Thread.currentThread().interrupt();
- }
- return Boolean.FALSE;
- }
- }
- });
- logger.log(Level.FINEST, "startIdle: return {0}", started);
- return started.booleanValue();
- }
- }
-
- /**
- * Read a response from the server while we're in the IDLE state.
- * We hold the messageCacheLock while processing the
- * responses so that we can update the number of messages
- * in the folder (for example).
- *
- * @param once only do one notification?
- * @return true if we should look for more IDLE responses,
- * false if IDLE is done
- * @exception MessagingException for errors
- * @since JavaMail 1.5.2
- */
- boolean handleIdle(boolean once) throws MessagingException {
- Response r = null;
- do {
- r = protocol.readIdleResponse();
- try {
- synchronized (messageCacheLock) {
- if (r.isBYE() && r.isSynthetic() && idleState == IDLE) {
- /*
- * If it was a timeout and no bytes were transferred
- * we ignore it and go back and read again.
- * If the I/O was otherwise interrupted, and no
- * bytes were transferred, we take it as a request
- * to abort the IDLE.
- */
- Exception ex = r.getException();
- if (ex instanceof InterruptedIOException &&
- ((InterruptedIOException)ex).
- bytesTransferred == 0) {
- if (ex instanceof SocketTimeoutException) {
- logger.finest(
- "handleIdle: ignoring socket timeout");
- r = null; // repeat do/while loop
- } else {
- logger.finest("handleIdle: interrupting IDLE");
- IdleManager im = idleManager;
- if (im != null) {
- logger.finest(
- "handleIdle: request IdleManager to abort");
- im.requestAbort(this);
- } else {
- logger.finest("handleIdle: abort IDLE");
- protocol.idleAbort();
- idleState = ABORTING;
- }
- // normally will exit the do/while loop
- }
- continue;
- }
- }
- boolean done = true;
- try {
- if (protocol == null ||
- !protocol.processIdleResponse(r))
- return false; // done
- done = false;
- } finally {
- if (done) {
- logger.finest("handleIdle: set to RUNNING");
- idleState = RUNNING;
- idleManager = null;
- messageCacheLock.notifyAll();
- }
- }
- if (once) {
- if (idleState == IDLE) {
- try {
- protocol.idleAbort();
- } catch (Exception ex) {
- // ignore any failures, still have to abort.
- // connection failures will be detected above
- // in the call to readIdleResponse.
- }
- idleState = ABORTING;
- }
- }
- }
- } catch (ConnectionException cex) {
- // Oops, the folder died on us.
- throw new FolderClosedException(this, cex.getMessage());
- } catch (ProtocolException pex) {
- throw new MessagingException(pex.getMessage(), pex);
- }
- // keep processing responses already in our buffer
- } while (r == null || protocol.hasResponse());
- return true;
- }
-
- /*
- * 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) {
- IdleManager im = idleManager;
- if (im != null) {
- logger.finest("waitIfIdle: request IdleManager to abort");
- im.requestAbort(this);
- } else {
- logger.finest("waitIfIdle: abort IDLE");
- protocol.idleAbort();
- idleState = ABORTING;
- }
- } else
- logger.log(Level.FINEST, "waitIfIdle: idleState {0}", idleState);
- try {
- // give up lock and wait to be not idle
- if (logger.isLoggable(Level.FINEST))
- logger.finest("waitIfIdle: wait to be not idle: " +
- Thread.currentThread());
- messageCacheLock.wait();
- if (logger.isLoggable(Level.FINEST))
- logger.finest("waitIfIdle: wait done, idleState " +
- idleState + ": " + Thread.currentThread());
- } catch (InterruptedException ex) {
- // restore the interrupted state, which callers might depend on
- Thread.currentThread().interrupt();
- // If someone is trying to interrupt us we can't keep going
- // around the loop waiting for IDLE to complete, but we can't
- // just return because callers expect the idleState to be
- // RUNNING when we return. Throwing this exception seems
- // like the best choice.
- throw new ProtocolException("Interrupted waitIfIdle", ex);
- }
- }
- }
-
- /*
- * Send the DONE command that aborts the IDLE; used by IdleManager.
- */
- void idleAbort() {
- synchronized (messageCacheLock) {
- if (idleState == IDLE && protocol != null) {
- protocol.idleAbort();
- idleState = ABORTING;
- }
- }
- }
-
- /*
- * Send the DONE command that aborts the IDLE and wait for the response;
- * used by IdleManager.
- */
- void idleAbortWait() {
- synchronized (messageCacheLock) {
- if (idleState == IDLE && protocol != null) {
- protocol.idleAbort();
- idleState = ABORTING;
-
- // read responses until OK or connection failure
- try {
- for (;;) {
- if (!handleIdle(false))
- break;
- }
- } catch (Exception ex) {
- // assume it's a connection failure; nothing more to do
- logger.log(Level.FINEST, "Exception in idleAbortWait", ex);
- }
- logger.finest("IDLE aborted");
- }
- }
- }
-
- /**
- * Return the SocketChannel for this connection, if any, for use
- * in IdleManager.
- */
- SocketChannel getChannel() {
- return protocol != null ? protocol.getChannel() : null;
- }
-
- /**
- * Send the IMAP ID command (if supported by the server) and return
- * the result from the server. The ID command identfies the client
- * to the server and returns information about the server to the client.
- * See RFC 2971 .
- * The returned Map is unmodifiable.
- *
- * @param clientParams a Map of keys and values identifying the client
- * @return a Map of keys and values identifying the server
- * @exception MessagingException if the server doesn't support the
- * ID extension
- * @since JavaMail 1.5.1
- */
- @SuppressWarnings("unchecked")
- public Map id(final Map clientParams)
- throws MessagingException {
- checkOpened();
- return (Map)doOptionalCommand("ID not supported",
- new ProtocolCommand() {
- @Override
- public Object doCommand(IMAPProtocol p)
- throws ProtocolException {
- return p.id(clientParams);
- }
- });
- }
-
- /**
- * Use the IMAP STATUS command to get the indicated item.
- * The STATUS item may be a standard item such as "RECENT" or "UNSEEN",
- * or may be a server-specific item.
- * The folder must be closed. If the item is not found, or the
- * folder is open, -1 is returned.
- *
- * @param item the STATUS item to fetch
- * @return the value of the STATUS item, or -1
- * @exception MessagingException for errors
- * @since JavaMail 1.5.2
- */
- public synchronized long getStatusItem(String item)
- throws MessagingException {
- if (!opened) {
- checkExists();
-
- IMAPProtocol p = null;
- Status status = null;
- try {
- p = getStoreProtocol(); // XXX
- String[] items = { item };
- status = p.status(fullName, items);
- return status != null ? status.getItem(item) : -1;
- } 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);
- } finally {
- releaseStoreProtocol(p);
- }
- }
- return -1;
- }
-
- /**
- * 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.
- */
- @Override
- 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()) {
- // HIGHESTMODSEQ can be updated on any OK response
- r.skipSpaces();
- if (r.readByte() == '[') {
- String s = r.readAtom();
- if (s.equalsIgnoreCase("HIGHESTMODSEQ"))
- highestmodseq = r.readLong();
- }
- r.reset();
- 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();
- if (seqnum > realTotal) {
- // A message was expunged that we never knew about.
- // Exchange will do this. Just ignore the notification.
- // (Alternatively, we could simulate an EXISTS for the
- // expunged message before expunging it.)
- return;
- }
- 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) };
- if (msgs[0] == null) // XXX - should never happen
- msgs = null;
- }
-
- messageCache.expungeMessage(seqnum);
-
- // decrement 'realTotal'; but leave 'total' unchanged
- realTotal--;
-
- if (msgs != null) // Do the notification here.
- notifyMessageRemovedListeners(false, msgs);
-
- } else if (ir.keyEquals("VANISHED")) {
- // after the folder is opened with QRESYNC, a VANISHED response
- // without the (EARLIER) tag is used instead of the EXPUNGE
- // response
-
- // "VANISHED" SP ["(EARLIER)"] SP known-uids
- String[] s = ir.readAtomStringList();
- if (s == null) { // no (EARLIER)
- String uids = ir.readAtom();
- UIDSet[] uidset = UIDSet.parseUIDSets(uids);
- // assume no duplicates and no UIDs out of range
- realTotal -= UIDSet.size(uidset);
- long[] luid = UIDSet.toArray(uidset);
- Message[] msgs = createMessagesForUIDs(luid);
- for (Message m : msgs) {
- if (m.getMessageNumber() > 0)
- messageCache.expungeMessage(m.getMessageNumber());
- }
- if (doExpungeNotification && hasMessageCountListener) {
- notifyMessageRemovedListeners(true, msgs);
- }
- } // else if (EARLIER), ignore
-
- } else if (ir.keyEquals("FETCH")) {
- assert ir instanceof FetchResponse : "!ir instanceof FetchResponse";
- Message msg = processFetchResponse((FetchResponse)ir);
- if (msg != null)
- notifyMessageChangedListeners(
- MessageChangedEvent.FLAGS_CHANGED, msg);
-
- } else if (ir.keyEquals("RECENT")) {
- // update 'recent'
- recent = ir.getNumber();
- }
- }
-
- /**
- * Process a FETCH response.
- * The only unsolicited FETCH response that makes sense
- * to me (for now) is FLAGS updates, which might include
- * UID and MODSEQ information. Ignore any other junk.
- */
- private Message processFetchResponse(FetchResponse fr) {
- IMAPMessage msg = getMessageBySeqNumber(fr.getNumber());
- if (msg != null) { // should always be true
- boolean notify = false;
-
- UID uid = fr.getItem(UID.class);
- if (uid != null && msg.getUID() != uid.uid) {
- msg.setUID(uid.uid);
- if (uidTable == null)
- uidTable = new Hashtable<>();
- uidTable.put(Long.valueOf(uid.uid), msg);
- notify = true;
- }
-
- MODSEQ modseq = fr.getItem(MODSEQ.class);
- if (modseq != null && msg._getModSeq() != modseq.modseq) {
- msg.setModSeq(modseq.modseq);
- /*
- * XXX - should we update the folder's HIGHESTMODSEQ or not?
- *
- if (modseq.modseq > highestmodseq)
- highestmodseq = modseq.modseq;
- */
- notify = true;
- }
-
- // Get FLAGS response, if present
- FLAGS flags = fr.getItem(FLAGS.class);
- if (flags != null) {
- msg._setFlags(flags); // assume flags changed
- notify = true;
- }
-
- // handle any extension items that might've changed
- // XXX - no notifications associated with extension items
- msg.handleExtensionFetchItems(fr.getExtensionItems());
-
- if (!notify)
- msg = null;
- }
- return msg;
- }
-
- /**
- * 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);
- * }
- *
- *
- * ASSERT: Must be called with this folder's synchronization lock held.
- *
- * @return the IMAPProtocol for the Store's connection
- * @exception ProtocolException for protocol errors
- */
- protected synchronized IMAPProtocol getStoreProtocol()
- throws ProtocolException {
- connectionPoolLogger.fine("getStoreProtocol() borrowing a connection");
- return ((IMAPStore)store).getFolderStoreProtocol();
- }
-
- /**
- * Throw the appropriate 'closed' exception.
- *
- * @param cex the ConnectionException
- * @exception FolderClosedException if the folder is closed
- * @exception StoreClosedException if the store is closed
- */
- 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
- * @exception ProtocolException for protocol errors
- */
- protected IMAPProtocol getProtocol() throws ProtocolException {
- assert Thread.holdsLock(messageCacheLock);
- waitIfIdle();
- // if we no longer have a protocol object after waiting, it probably
- // means the connection has been closed due to a communnication error,
- // or possibly because the folder has been closed
- if (protocol == null)
- throw new ConnectionException("Connection closed");
- 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.
- *
- * @param protocol the IMAPProtocol for the connection
- * @return the results of the command
- * @exception ProtocolException for protocol errors
- */
- 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;
- * }
- * });
- *
- *
- * @param cmd the protocol command
- * @return the result of the command
- * @exception MessagingException for failures
- */
- 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 synchronized Object doProtocolCommand(ProtocolCommand cmd)
- throws ProtocolException {
- /*
- * 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.
- *
- * ASSERT: Must be called with this folder's synchronization lock held.
- *
- * @param p the IMAPProtocol object
- */
- 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
- *
- * @param returnToPool return the protocol object to the pool?
- */
- 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
- *
- * @param keepStoreAlive keep the Store alive too?
- * @exception ProtocolException for protocol errors
- */
- protected void keepConnectionAlive(boolean keepStoreAlive)
- throws ProtocolException {
-
- assert Thread.holdsLock(messageCacheLock);
- if (protocol == null) // in case connection was closed
- return;
- 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
- *
- * @param seqnum the message sequence number
- * @return the IMAPMessage object
- */
- protected IMAPMessage getMessageBySeqNumber(int seqnum) {
- if (seqnum > messageCache.size()) {
- // Microsoft Exchange will sometimes return message
- // numbers that it has not yet notified the client
- // about via EXISTS; ignore those messages here.
- // GoDaddy IMAP does this too.
- if (logger.isLoggable(Level.FINE))
- logger.fine("ignoring message number " +
- seqnum + " outside range " + messageCache.size());
- return null;
- }
- return messageCache.getMessageBySeqnum(seqnum);
- }
-
- /**
- * Get the message objects for the given sequence numbers.
- *
- * ASSERT: This method must be called only when holding the
- * messageCacheLock
- *
- * @param seqnums the array of message sequence numbers
- * @return the IMAPMessage objects
- * @since JavaMail 1.5.3
- */
- protected IMAPMessage[] getMessagesBySeqNumbers(int[] seqnums) {
- IMAPMessage[] msgs = new IMAPMessage[seqnums.length];
- int nulls = 0;
- // Map seq-numbers into actual Messages.
- for (int i = 0; i < seqnums.length; i++) {
- msgs[i] = getMessageBySeqNumber(seqnums[i]);
- if (msgs[i] == null)
- nulls++;
- }
- if (nulls > 0) { // compress the array to remove the nulls
- IMAPMessage[] nmsgs = new IMAPMessage[seqnums.length - nulls];
- for (int i = 0, j = 0; i < msgs.length; i++) {
- if (msgs[i] != null)
- nmsgs[j++] = msgs[i];
- }
- msgs = nmsgs;
- }
- return msgs;
- }
-
- 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();
- }
-
- @Override
- public int size() {
- return msgSize;
- }
-
- @Override
- 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;
- }
-
- @Override
- 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;
- }
-
- @Override
- 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;
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/imap/IMAPInputStream.java b/app/src/main/java/com/sun/mail/imap/IMAPInputStream.java
deleted file mode 100644
index 52018e288b..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPInputStream.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.FolderClosedIOException;
-import com.sun.mail.util.MessageRemovedIOException;
-
-/**
- * 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.
- *
- * @param msg the IMAPMessage the data will come from
- * @param section the IMAP section/part identifier for the data
- * @param max the number of bytes in this section
- * @param peek peek instead of fetch?
- */
- 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();
- // nope, the server doesn't think it's expunged.
- // can't tell the difference between the server returning NIL
- // and some other error that caused null to be returned above,
- // so we'll just assume it was empty content.
- ba = new ByteArray(0);
- }
- }
-
- // 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.
-
- int origin = b != null ? b.getOrigin() : pos;
- if (origin < 0) {
- /*
- * Some versions of Exchange will return the entire message
- * body even though we only ask for a chunk, and the returned
- * data won't specify an "origin". If this happens, and we
- * get more data than we asked for, assume it's the entire
- * message body.
- */
- if (pos == 0) {
- /*
- * If we got more or less than we asked for,
- * this is the last buffer of data.
- */
- lastBuffer = n != cnt;
- } else {
- /*
- * We asked for data NOT starting at the beginning,
- * but we got back data starting at the beginning.
- * Possibly we could extract the needed data from
- * some part of the data we got back, but really this
- * should never happen so we just assume something is
- * broken and terminate the data here.
- */
- n = 0;
- lastBuffer = true;
- }
- } else if (origin == pos) {
- /*
- * If we got less than we asked for,
- * this is the last buffer of data.
- */
- lastBuffer = n < cnt;
- } else {
- /*
- * We got back data that doesn't match the request.
- * Just terminate the data here.
- */
- n = 0;
- lastBuffer = true;
- }
-
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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/app/src/main/java/com/sun/mail/imap/IMAPMessage.java b/app/src/main/java/com/sun/mail/imap/IMAPMessage.java
deleted file mode 100644
index 453479d989..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPMessage.java
+++ /dev/null
@@ -1,1700 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.ReadableMime;
-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 long size = -1; // RFC822.SIZE
-
- private Boolean peek; // use BODY.PEEK when fetching content?
-
- // this message's IMAP UID
- private volatile long uid = -1;
-
- // this message's IMAP MODSEQ - RFC 4551 CONDSTORE
- private volatile long modseq = -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;
-
- // Indicates that we've cached the body of this message
- private volatile boolean bodyLoaded = 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.
- *
- * @param folder the folder containing this message
- * @param msgnum the message sequence number
- */
- protected IMAPMessage(IMAPFolder folder, int msgnum) {
- super(folder, msgnum);
- flags = null;
- }
-
- /**
- * Constructor, for use by IMAPNestedMessage.
- *
- * @param session the Session
- */
- 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.
- *
- * @return the IMAPProtocol object for the containing folder
- * @exception ProtocolException for protocol errors
- * @exception FolderClosedException if the folder is closed
- */
- 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.
- *
- * @return the message cache lock object
- */
- 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.
- *
- * @return the message sequence number
- */
- protected int getSequenceNumber() {
- return ((IMAPFolder)folder).messageCache.seqnumOf(getMessageNumber());
- }
-
- /**
- * Wrapper around the protected method Message.setMessageNumber() to
- * make that method accessible to IMAPFolder.
- */
- @Override
- protected void setMessageNumber(int msgnum) {
- super.setMessageNumber(msgnum);
- }
-
- /**
- * Return the UID for this message.
- * Returns -1 if not known; use UIDFolder.getUID() in this case.
- *
- * @return the UID
- * @see javax.mail.UIDFolder#getUID
- */
- protected long getUID() {
- return uid;
- }
-
- protected void setUID(long uid) {
- this.uid = uid;
- }
-
- /**
- * Return the modification sequence number (MODSEQ) for this message.
- * Returns -1 if not known.
- *
- * @return the modification sequence number
- * @exception MessagingException for failures
- * @see "RFC 4551"
- * @since JavaMail 1.5.1
- */
- public synchronized long getModSeq() throws MessagingException {
- if (modseq != -1)
- return modseq;
-
- synchronized (getMessageCacheLock()) { // Acquire Lock
- try {
- IMAPProtocol p = getProtocol();
- checkExpunged(); // insure that message is not expunged
- MODSEQ ms = p.fetchMODSEQ(getSequenceNumber());
-
- if (ms != null)
- modseq = ms.modseq;
- } catch (ConnectionException cex) {
- throw new FolderClosedException(folder, cex.getMessage());
- } catch (ProtocolException pex) {
- throw new MessagingException(pex.getMessage(), pex);
- }
- }
- return modseq;
- }
-
- long _getModSeq() {
- return modseq;
- }
-
- void setModSeq(long modseq) {
- this.modseq = modseq;
- }
-
- // expose to MessageCache
- @Override
- 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.
- *
- * @exception MessageRemovedException if the message has been removed
- * @exception FolderClosedException if the folder has been closed
- */
- 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();
- }
-
- // Should we ignore the size in the BODYSTRUCTURE?
- // MUST be overridden by IMAPNestedMessage
- protected boolean ignoreBodyStructureSize() {
- return ((IMAPStore)folder.getStore()).ignoreBodyStructureSize();
- }
-
- /**
- * Get the "From" attribute.
- */
- @Override
- public Address[] getFrom() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getFrom();
- 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);
- }
-
- @Override
- public void setFrom(Address address) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- @Override
- public void addFrom(Address[] addresses) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the "Sender" attribute.
- */
- @Override
- public Address getSender() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getSender();
- loadEnvelope();
- if (envelope.sender != null && envelope.sender.length > 0)
- return (envelope.sender)[0]; // there can be only one sender
- else
- return null;
- }
-
-
- @Override
- public void setSender(Address address) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the desired Recipient type.
- */
- @Override
- public Address[] getRecipients(Message.RecipientType type)
- throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getRecipients(type);
- 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);
- }
-
- @Override
- public void setRecipients(Message.RecipientType type, Address[] addresses)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- @Override
- public void addRecipients(Message.RecipientType type, Address[] addresses)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the ReplyTo addresses.
- */
- @Override
- public Address[] getReplyTo() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getReplyTo();
- 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);
- }
-
- @Override
- public void setReplyTo(Address[] addresses) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the decoded subject.
- */
- @Override
- public String getSubject() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getSubject();
-
- 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;
- }
-
- @Override
- public void setSubject(String subject, String charset)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the SentDate.
- */
- @Override
- public Date getSentDate() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getSentDate();
- loadEnvelope();
- if (envelope.date == null)
- return null;
- else
- return new Date(envelope.date.getTime());
- }
-
- @Override
- public void setSentDate(Date d) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the received date (INTERNALDATE).
- */
- @Override
- public Date getReceivedDate() throws MessagingException {
- checkExpunged();
- if (receivedDate == null)
- loadEnvelope(); // have to go to the server for this
- 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.
- * Note also that if the size of the message is greater than
- * Integer.MAX_VALUE (2GB), this method returns Integer.MAX_VALUE.
- */
- @Override
- public int getSize() throws MessagingException {
- checkExpunged();
- // if bodyLoaded, size is already set
- if (size == -1)
- loadEnvelope(); // XXX - could just fetch the size
- if (size > Integer.MAX_VALUE)
- return Integer.MAX_VALUE; // the best we can do...
- else
- return (int)size;
- }
-
- /**
- * Get the message size as a long.
- *
- * Suitable for messages that might be larger than 2GB.
- * @return the message size as a long integer
- * @exception MessagingException for failures
- * @since JavaMail 1.6
- */
- public long getSizeLong() throws MessagingException {
- checkExpunged();
- // if bodyLoaded, size is already set
- 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
- */
- @Override
- public int getLineCount() throws MessagingException {
- checkExpunged();
- // XXX - superclass doesn't implement this
- loadBODYSTRUCTURE();
- return bs.lines;
- }
-
- /**
- * Get the content language.
- */
- @Override
- public String[] getContentLanguage() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getContentLanguage();
- loadBODYSTRUCTURE();
- if (bs.language != null)
- return bs.language.clone();
- else
- return null;
- }
-
- @Override
- public void setContentLanguage(String[] languages)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the In-Reply-To header.
- *
- * @return the In-Reply-To header
- * @exception MessagingException for failures
- * @since JavaMail 1.3.3
- */
- public String getInReplyTo() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getHeader("In-Reply-To", " ");
- loadEnvelope();
- return envelope.inReplyTo;
- }
-
- /**
- * Get the Content-Type.
- *
- * Generate this header from the BODYSTRUCTURE. Append parameters
- * as well.
- */
- @Override
- public synchronized String getContentType() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getContentType();
-
- // 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.
- */
- @Override
- public String getDisposition() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getDisposition();
- loadBODYSTRUCTURE();
- return bs.disposition;
- }
-
- @Override
- public void setDisposition(String disposition) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the Content-Transfer-Encoding.
- */
- @Override
- public String getEncoding() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getEncoding();
- loadBODYSTRUCTURE();
- return bs.encoding;
- }
-
- /**
- * Get the Content-ID.
- */
- @Override
- public String getContentID() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getContentID();
- loadBODYSTRUCTURE();
- return bs.id;
- }
-
- @Override
- public void setContentID(String cid) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the Content-MD5.
- */
- @Override
- public String getContentMD5() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getContentMD5();
- loadBODYSTRUCTURE();
- return bs.md5;
- }
-
- @Override
- public void setContentMD5(String md5) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the decoded Content-Description.
- */
- @Override
- public String getDescription() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getDescription();
-
- 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;
- }
-
- @Override
- public void setDescription(String description, String charset)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get the Message-ID.
- */
- @Override
- public String getMessageID() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getMessageID();
- loadEnvelope();
- return envelope.messageId;
- }
-
- /**
- * Get the "filename" Disposition parameter. (Only available in
- * IMAP4rev1). If thats not available, get the "name" ContentType
- * parameter.
- */
- @Override
- public String getFileName() throws MessagingException {
- checkExpunged();
- if (bodyLoaded)
- return super.getFileName();
-
- 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;
- }
-
- @Override
- 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
- */
- @Override
- protected InputStream getContentStream() throws MessagingException {
- if (bodyLoaded)
- return super.getContentStream();
- 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) {
- forceCheckExpunged(); // may throw MessageRemovedException
- // nope, the server doesn't think it's expunged.
- // can't tell the difference between the server returning NIL
- // and some other error that caused null to be returned above,
- // so we'll just assume it was empty content.
- is = new ByteArrayInputStream(new byte[0]);
- }
- return is;
- }
-
- /**
- * Get the DataHandler object for this message.
- */
- @Override
- public synchronized DataHandler getDataHandler()
- throws MessagingException {
- checkExpunged();
-
- if (dh == null && !bodyLoaded) {
- 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();
- }
-
- @Override
- 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
- */
- @Override
- public InputStream getMimeStream() throws MessagingException {
- // XXX - need an "if (bodyLoaded)" version
- 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.
- // can't tell the difference between the server returning NIL
- // and some other error that caused null to be returned above,
- // so we'll just assume it was empty content.
- is = new ByteArrayInputStream(new byte[0]);
- }
- return is;
- }
-
- /**
- * Write out the bytes into the given OutputStream.
- */
- @Override
- public void writeTo(OutputStream os)
- throws IOException, MessagingException {
- if (bodyLoaded) {
- super.writeTo(os);
- return;
- }
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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);
- }
-
- @Override
- public void setHeader(String name, String value)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- @Override
- public void addHeader(String name, String value)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- @Override
- public void removeHeader(String name)
- throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get all headers.
- */
- @Override
- public Enumeration getAllHeaders() throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getAllHeaders();
- }
-
- /**
- * Get matching headers.
- */
- @Override
- public Enumeration getMatchingHeaders(String[] names)
- throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getMatchingHeaders(names);
- }
-
- /**
- * Get non-matching headers.
- */
- @Override
- public Enumeration getNonMatchingHeaders(String[] names)
- throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getNonMatchingHeaders(names);
- }
-
- @Override
- public void addHeaderLine(String line) throws MessagingException {
- throw new IllegalWriteException("IMAPMessage is read-only");
- }
-
- /**
- * Get all header-lines.
- */
- @Override
- public Enumeration getAllHeaderLines() throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getAllHeaderLines();
- }
-
- /**
- * Get all matching header-lines.
- */
- @Override
- public Enumeration getMatchingHeaderLines(String[] names)
- throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getMatchingHeaderLines(names);
- }
-
- /**
- * Get all non-matching headerlines.
- */
- @Override
- public Enumeration getNonMatchingHeaderLines(String[] names)
- throws MessagingException {
- checkExpunged();
- loadHeaders();
- return super.getNonMatchingHeaderLines(names);
- }
-
- /**
- * Get the Flags for this message.
- */
- @Override
- public synchronized Flags getFlags() throws MessagingException {
- checkExpunged();
- loadFlags();
- return super.getFlags();
- }
-
- /**
- * Test if the given Flags are set in this message.
- */
- @Override
- public synchronized boolean isSet(Flags.Flag flag)
- throws MessagingException {
- checkExpunged();
- loadFlags();
- return super.isSet(flag);
- }
-
- /**
- * Set/Unset the given flags in this message.
- */
- @Override
- 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. This overrides the default
- * value from the "mail.imap.peek" property.
- *
- * @param peek the peek flag
- * @since JavaMail 1.3.3
- */
- public synchronized void setPeek(boolean peek) {
- this.peek = Boolean.valueOf(peek);
- }
-
- /**
- * Get whether or not to use the PEEK variant of FETCH when
- * fetching message content.
- *
- * @return the peek flag
- * @since JavaMail 1.3.3
- */
- public synchronized boolean getPeek() {
- if (peek == null)
- return ((IMAPStore)folder.getStore()).getPeek();
- else
- return peek.booleanValue();
- }
-
- /**
- * 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;
- content = null;
- contentStream = null;
- bodyLoaded = false;
- }
-
- /**
- * 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 boolean needMessage = false;
- private boolean needRDate = false;
- private String[] hdrs = null;
- private Set need = new HashSet<>();
-
- /**
- * Create a FetchProfileCondition to determine if we need to fetch
- * any of the information specified in the FetchProfile.
- *
- * @param fp the FetchProfile
- * @param fitems the FETCH items
- */
- @SuppressWarnings("deprecation") // for FetchProfile.Item.SIZE
- 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;
- if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE))
- needMessage = true;
- if (fp.contains(IMAPFolder.FetchProfileItem.INTERNALDATE))
- needRDate = 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.
- */
- @Override
- public boolean test(IMAPMessage m) {
- if (needEnvelope && m._getEnvelope() == null && !m.bodyLoaded)
- return true; // no envelope
- if (needFlags && m._getFlags() == null)
- return true; // no flags
- if (needBodyStructure && m._getBodyStructure() == null &&
- !m.bodyLoaded)
- 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 && !m.bodyLoaded) // no size
- return true;
- if (needMessage && !m.bodyLoaded) // no message body
- return true;
- if (needRDate && m.receivedDate == null) // no received date
- 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 = 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.
- *
- * @param item the fetch item
- * @param hdrs the headers we're asking for
- * @param allHeaders load all headers?
- * @return did we handle this fetch item?
- * @exception MessagingException for failures
- * @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;
- else if (item instanceof MODSEQ)
- modseq = ((MODSEQ)item).modseq;
-
- // 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(Long.valueOf(u.uid), this);
- }
-
- // Check for header items
- else if (item instanceof RFC822DATA ||
- item instanceof BODY) {
- InputStream headerStream;
- boolean isHeader;
- if (item instanceof RFC822DATA) { // IMAP4
- headerStream =
- ((RFC822DATA)item).getByteArrayInputStream();
- isHeader = ((RFC822DATA)item).isHeader();
- } else { // IMAP4rev1
- headerStream =
- ((BODY)item).getByteArrayInputStream();
- isHeader = ((BODY)item).isHeader();
- }
-
- if (!isHeader) {
- // load the entire message by using the superclass
- // MimeMessage.parse method
- // first, save the size of the message
- try {
- size = headerStream.available();
- } catch (IOException ex) {
- // should never occur
- }
- parse(headerStream);
- bodyLoaded = true;
- setHeadersLoaded(true);
- } else {
- // 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 = 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.
- *
- * @param extensionItems the Map to add fetch items to
- * @since JavaMail 1.4.6
- */
- protected void handleExtensionFetchItems(
- Map extensionItems) {
- if (extensionItems == null || extensionItems.isEmpty())
- return;
- if (items == null)
- items = new HashMap<>();
- 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.
- *
- * @param fitem the FetchItem
- * @return the data associated with the FetchItem
- * @exception MessagingException for failures
- * @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];
- handleExtensionFetchItems(f.getExtensionItems());
- if (items != null) {
- Object o = items.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.
- *
- * @param fitem the FetchItem
- * @return the data associated with the FetchItem
- * @exception MessagingException for failures
- * @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 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/app/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java b/app/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java
deleted file mode 100644
index c55ef022c2..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPMultipartDataSource.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-
-import javax.mail.*;
-import javax.mail.internet.*;
-
-import com.sun.mail.imap.protocol.*;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class
- *
- * @author John Mani
- */
-
-public class IMAPMultipartDataSource extends MimePartDataSource
- implements MultipartDataSource {
- private List parts;
-
- protected IMAPMultipartDataSource(MimePart part, BODYSTRUCTURE[] bs,
- String sectionId, IMAPMessage msg) {
- super(part);
-
- parts = new ArrayList<>(bs.length);
- for (int i = 0; i < bs.length; i++)
- parts.add(
- new IMAPBodyPart(bs[i],
- sectionId == null ?
- Integer.toString(i+1) :
- sectionId + "." + Integer.toString(i+1),
- msg)
- );
- }
-
- @Override
- public int getCount() {
- return parts.size();
- }
-
- @Override
- public BodyPart getBodyPart(int index) throws MessagingException {
- return parts.get(index);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java b/app/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java
deleted file mode 100644
index 12cd4e453a..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPNestedMessage.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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().
- */
- @Override
- protected IMAPProtocol getProtocol()
- throws ProtocolException, FolderClosedException {
- return msg.getProtocol();
- }
-
- /*
- * Is this an IMAP4 REV1 server?
- */
- @Override
- protected boolean isREV1() throws FolderClosedException {
- return msg.isREV1();
- }
-
- /*
- * Get the enclosing message's messageCacheLock. Overrides
- * IMAPMessage.getMessageCacheLock().
- */
- @Override
- protected Object getMessageCacheLock() {
- return msg.getMessageCacheLock();
- }
-
- /*
- * Get the enclosing message's sequence number. Overrides
- * IMAPMessage.getSequenceNumber().
- */
- @Override
- protected int getSequenceNumber() {
- return msg.getSequenceNumber();
- }
-
- /*
- * Check whether the enclosing message is expunged. Overrides
- * IMAPMessage.checkExpunged().
- */
- @Override
- protected void checkExpunged() throws MessageRemovedException {
- msg.checkExpunged();
- }
-
- /*
- * Check whether the enclosing message is expunged. Overrides
- * Message.isExpunged().
- */
- @Override
- public boolean isExpunged() {
- return msg.isExpunged();
- }
-
- /*
- * Get the enclosing message's fetchBlockSize.
- */
- @Override
- protected int getFetchBlockSize() {
- return msg.getFetchBlockSize();
- }
-
- /*
- * Get the enclosing message's ignoreBodyStructureSize.
- */
- @Override
- protected boolean ignoreBodyStructureSize() {
- return msg.ignoreBodyStructureSize();
- }
-
- /*
- * IMAPMessage uses RFC822.SIZE. We use the "size" field from
- * our BODYSTRUCTURE.
- */
- @Override
- public int getSize() throws MessagingException {
- return bs.size;
- }
-
- /*
- * Disallow setting flags on nested messages
- */
- @Override
- 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/app/src/main/java/com/sun/mail/imap/IMAPProvider.java b/app/src/main/java/com/sun/mail/imap/IMAPProvider.java
deleted file mode 100644
index 0f35374380..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPProvider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The IMAP protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class IMAPProvider extends Provider {
- public IMAPProvider() {
- super(Provider.Type.STORE, "imap", IMAPStore.class.getName(),
- "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/IMAPSSLProvider.java b/app/src/main/java/com/sun/mail/imap/IMAPSSLProvider.java
deleted file mode 100644
index 354e63d490..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPSSLProvider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The IMAP SSL protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class IMAPSSLProvider extends Provider {
- public IMAPSSLProvider() {
- super(Provider.Type.STORE, "imaps", IMAPSSLStore.class.getName(),
- "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/IMAPSSLStore.java b/app/src/main/java/com/sun/mail/imap/IMAPSSLStore.java
deleted file mode 100644
index 488b4ec13c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPSSLStore.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param session the Session
- * @param url the URLName of this store
- */
- public IMAPSSLStore(Session session, URLName url) {
- super(session, url, "imaps", true); // call super constructor
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/IMAPStore.java b/app/src/main/java/com/sun/mail/imap/IMAPStore.java
deleted file mode 100644
index 866f177377..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IMAPStore.java
+++ /dev/null
@@ -1,2211 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.lang.reflect.*;
-import java.util.Vector;
-import java.util.StringTokenizer;
-import java.util.Locale;
-import java.util.Properties;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Map;
-import java.util.HashMap;
-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;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 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.
- *
- * The {@link #id id} method supports the IMAP ID extension;
- * see RFC 2971 .
- * The fields ID_NAME, ID_VERSION, etc. represent the suggested field names
- * in RFC 2971 section 3.3 and may be used as keys in the Map containing
- * client values or server values.
- *
- * 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 Jakarta Mail 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;
-
- public static final String ID_NAME = "name";
- public static final String ID_VERSION = "version";
- public static final String ID_OS = "os";
- public static final String ID_OS_VERSION = "os-version";
- public static final String ID_VENDOR = "vendor";
- public static final String ID_SUPPORT_URL = "support-url";
- public static final String ID_ADDRESS = "address";
- public static final String ID_DATE = "date";
- public static final String ID_COMMAND = "command";
- public static final String ID_ARGUMENTS = "arguments";
- public static final String ID_ENVIRONMENT = "environment";
-
- 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 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 enableResponseEvents = false;
- // enable notification of IMAP responses during IDLE
- private boolean enableImapEvents = false;
- private String guid; // for Yahoo! Mail IMAP
- private boolean throwSearchException = false;
- private boolean peek = false;
- private boolean closeFoldersOnStoreFailure = true;
- private boolean enableCompress = false; // enable COMPRESS=DEFLATE
- private boolean finalizeCleanClose = false;
-
- /*
- * 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();
- Properties props = session.getProperties();
-
- boolean debug = PropUtil.getBooleanProperty(props,
- "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.getIntProperty(props,
- "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.getIntProperty(props,
- "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.getIntProperty(props,
- "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.getIntProperty(props,
- "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.getBooleanProperty(props,
- "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() {
- @Override
- 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.
- *
- * @param session the Session
- * @param url the URLName of this store
- */
- public IMAPStore(Session session, URLName url) {
- this(session, url, "imap", false);
- }
-
- /**
- * Constructor used by this class and by IMAPSSLStore subclass.
- *
- * @param session the Session
- * @param url the URLName of this store
- * @param name the protocol name for this store
- * @param isSSL use SSL?
- */
- protected IMAPStore(Session session, URLName url,
- String name, boolean isSSL) {
- super(session, url); // call super constructor
- Properties props = session.getProperties();
-
- if (url != null)
- name = url.getProtocol();
- this.name = name;
- if (!isSSL)
- isSSL = PropUtil.getBooleanProperty(props,
- "mail." + name + ".ssl.enable", false);
- if (isSSL)
- this.defaultPort = 993;
- else
- this.defaultPort = 143;
- this.isSSL = isSSL;
-
- debug = session.getDebug();
- debugusername = PropUtil.getBooleanProperty(props,
- "mail.debug.auth.username", true);
- debugpassword = PropUtil.getBooleanProperty(props,
- "mail.debug.auth.password", false);
- logger = new MailLogger(this.getClass(),
- "DEBUG " + name.toUpperCase(Locale.ENGLISH),
- session.getDebug(), session.getDebugOut());
-
- boolean partialFetch = PropUtil.getBooleanProperty(props,
- "mail." + name + ".partialfetch", true);
- if (!partialFetch) {
- blksize = -1;
- logger.config("mail.imap.partialfetch: false");
- } else {
- blksize = PropUtil.getIntProperty(props,
- "mail." + name +".fetchsize", 1024 * 16);
- if (logger.isLoggable(Level.CONFIG))
- logger.config("mail.imap.fetchsize: " + blksize);
- }
-
- ignoreSize = PropUtil.getBooleanProperty(props,
- "mail." + name +".ignorebodystructuresize", false);
- if (logger.isLoggable(Level.CONFIG))
- logger.config("mail.imap.ignorebodystructuresize: " + ignoreSize);
-
- statusCacheTimeout = PropUtil.getIntProperty(props,
- "mail." + name + ".statuscachetimeout", 1000);
- if (logger.isLoggable(Level.CONFIG))
- logger.config("mail.imap.statuscachetimeout: " +
- statusCacheTimeout);
-
- appendBufferSize = PropUtil.getIntProperty(props,
- "mail." + name + ".appendbuffersize", -1);
- if (logger.isLoggable(Level.CONFIG))
- logger.config("mail.imap.appendbuffersize: " + appendBufferSize);
-
- minIdleTime = PropUtil.getIntProperty(props,
- "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 STARTTLS is enabled
- enableStartTLS = PropUtil.getBooleanProperty(props,
- "mail." + name + ".starttls.enable", false);
- if (enableStartTLS)
- logger.config("enable STARTTLS");
-
- // check if STARTTLS is required
- requireStartTLS = PropUtil.getBooleanProperty(props,
- "mail." + name + ".starttls.required", false);
- if (requireStartTLS)
- logger.config("require STARTTLS");
-
- // check if SASL is enabled
- enableSASL = PropUtil.getBooleanProperty(props,
- "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);
- List v = new ArrayList<>(5);
- 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);
- }
- }
-
- // 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.getBooleanProperty(props,
- "mail." + name + ".forcepasswordrefresh", false);
- if (forcePasswordRefresh)
- logger.config("enable forcePasswordRefresh");
-
- // check if enableimapevents is enabled
- enableResponseEvents = PropUtil.getBooleanProperty(props,
- "mail." + name + ".enableresponseevents", false);
- if (enableResponseEvents)
- logger.config("enable IMAP response events");
-
- // check if enableresponseevents is enabled
- enableImapEvents = PropUtil.getBooleanProperty(props,
- "mail." + name + ".enableimapevents", false);
- if (enableImapEvents)
- logger.config("enable IMAP IDLE events");
-
- // check if message cache debugging set
- messageCacheDebug = PropUtil.getBooleanProperty(props,
- "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);
-
- // check if throwsearchexception is enabled
- throwSearchException = PropUtil.getBooleanProperty(props,
- "mail." + name + ".throwsearchexception", false);
- if (throwSearchException)
- logger.config("throw SearchException");
-
- // check if peek is set
- peek = PropUtil.getBooleanProperty(props,
- "mail." + name + ".peek", false);
- if (peek)
- logger.config("peek");
-
- // check if closeFoldersOnStoreFailure is set
- closeFoldersOnStoreFailure = PropUtil.getBooleanProperty(props,
- "mail." + name + ".closefoldersonstorefailure", true);
- if (closeFoldersOnStoreFailure)
- logger.config("closeFoldersOnStoreFailure");
-
- // check if COMPRESS is enabled
- enableCompress = PropUtil.getBooleanProperty(props,
- "mail." + name + ".compress.enable", false);
- if (enableCompress)
- logger.config("enable COMPRESS");
-
- // check if finalizeCleanClose is enabled
- finalizeCleanClose = PropUtil.getBooleanProperty(props,
- "mail." + name + ".finalizecleanclose", false);
- if (finalizeCleanClose)
- logger.config("close connection cleanly in finalize");
-
- 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.
- */
- @Override
- 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.getIntProperty(session.getProperties(),
- "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));
- protocol.addResponseHandler(nonStoreResponseHandler);
- login(protocol, user, password);
- protocol.removeResponseHandler(nonStoreResponseHandler);
- 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 (IMAPReferralException ex) {
- // login failure due to IMAP REFERRAL, close connection to server
- if (protocol != null)
- protocol.disconnect();
- protocol = null;
- throw new ReferralException(ex.getUrl(), ex.getMessage());
- } catch (CommandFailedException cex) {
- // login failure, close connection to server
- if (protocol != null)
- protocol.disconnect();
- protocol = null;
- Response r = cex.getResponse();
- throw new AuthenticationFailedException(
- r != null ? r.getRest() : cex.getMessage());
- } 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.
- *
- * @param host the host name
- * @param port the port number
- * @return the new IMAPProtocol object
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol errors
- * @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
- // and we're not already using SSL
- if ((enableStartTLS || requireStartTLS) && !p.isSSL()) {
- 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
- // http://en.wikipedia.org/wiki/Yahoo%21_Mail#Free_IMAP_and_SMTPs_access
- if (guid != null) {
- Map gmap = new HashMap<>();
- gmap.put("GUID", guid);
- p.id(gmap);
- }
-
- /*
- * 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) {
- try {
- p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw);
- if (!p.isAuthenticated())
- throw new CommandFailedException(
- "SASL authentication failed");
- } catch (UnsupportedOperationException ex) {
- // continue to try other authentication methods below
- }
- }
-
- if (!p.isAuthenticated())
- authenticate(p, authzid, u, pw);
-
- 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"
- }
- }
-
- if (enableCompress) {
- if (p.hasCapability("COMPRESS=DEFLATE")) {
- p.compress();
- }
- }
-
- // if server supports UTF-8, enable it for client use
- // note that this is safe to enable even if mail.mime.allowutf8=false
- if (p.hasCapability("UTF8=ACCEPT") || p.hasCapability("UTF8=ONLY"))
- p.enable("UTF8=ACCEPT");
- }
-
- /**
- * Authenticate using one of the non-SASL mechanisms.
- *
- * @param p the IMAPProtocol object
- * @param authzid the authorization ID
- * @param user the user name
- * @param password the password
- * @exception ProtocolException on failures
- */
- private void authenticate(IMAPProtocol p, String authzid,
- String user, String password)
- throws ProtocolException {
- // this list must match the "if" statements below
- String defaultAuthenticationMechanisms = "PLAIN LOGIN NTLM XOAUTH2";
-
- // setting mail.imap.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;
-
- /*
- * 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();
- m = m.toUpperCase(Locale.ENGLISH);
-
- /*
- * If using the default mechanisms, check if this one is disabled.
- */
- if (mechs == defaultAuthenticationMechanisms) {
- String dprop = "mail." + name + ".auth." +
- m.toLowerCase(Locale.ENGLISH) + ".disable";
- boolean disabled = PropUtil.getBooleanProperty(
- session.getProperties(),
- dprop, m.equals("XOAUTH2"));
- if (disabled) {
- if (logger.isLoggable(Level.FINE))
- logger.fine("mechanism " + m +
- " disabled by property: " + dprop);
- continue;
- }
- }
-
- if (!(p.hasCapability("AUTH=" + m) ||
- (m.equals("LOGIN") && p.hasCapability("AUTH-LOGIN")))) {
- logger.log(Level.FINE, "mechanism {0} not supported by server",
- m);
- continue;
- }
-
- if (m.equals("PLAIN"))
- p.authplain(authzid, user, password);
- else if (m.equals("LOGIN"))
- p.authlogin(user, password);
- else if (m.equals("NTLM"))
- p.authntlm(authzid, user, password);
- else if (m.equals("XOAUTH2"))
- p.authoauth2(user, password);
- else {
- logger.log(Level.FINE, "no authenticator for mechanism {0}", m);
- continue;
- }
- return;
- }
-
- if (!p.hasCapability("LOGINDISABLED")) {
- p.login(user, password);
- return;
- }
-
- throw new ProtocolException("No login methods supported!");
- }
-
- /**
- * 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.
- *
- * @param p the IMAPProtocol connection
- * @exception ProtocolException for protocol errors
- * @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 synchronized 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.
- *
- * @param user the user name for the store
- * @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.
- *
- * @param password the password for the store
- * @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);
- p.addResponseHandler(nonStoreResponseHandler);
- // Use cached auth info
- login(p, user, password);
- p.removeResponseHandler(nonStoreResponseHandler);
- } 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 = 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();
- } catch (RuntimeException ignored) {
- // don't let any exception stop us
- }
- p = null;
- continue; // try again, from the top
- }
- }
-
- // if proxyAuthUser has changed, switch to new user
- if (proxyAuthUser != null &&
- !proxyAuthUser.equals(p.getProxyAuthUser()) &&
- p.hasCapability("X-UNAUTHENTICATE")) {
- 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.unauthenticate();
- login(p, user, password);
- p.removeResponseHandler(nonStoreResponseHandler);
- p.addResponseHandler(this);
- } catch (ProtocolException pex) {
- try {
- p.removeResponseHandler(nonStoreResponseHandler);
- p.disconnect();
- } catch (RuntimeException ignored) {
- // 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 = pool.authenticatedConnections.firstElement();
-
- // if proxyAuthUser has changed, switch to new user
- if (proxyAuthUser != null &&
- !proxyAuthUser.equals(p.getProxyAuthUser()) &&
- p.hasCapability("X-UNAUTHENTICATE")) {
- p.unauthenticate();
- login(p, user, password);
- }
- }
-
- 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) {
- // restore the interrupted state, which callers might
- // depend on
- Thread.currentThread().interrupt();
- // don't keep looking for a connection if we've been
- // interrupted
- throw new ProtocolException(
- "Interrupted getStoreProtocol", 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.getBooleanProperty(session.getProperties(),
- "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 =
- 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 = 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;
- }
-
- /**
- * Throw a SearchException if the search expression is too complex?
- */
- boolean throwSearchException() {
- return throwSearchException;
- }
-
- /**
- * Get the default "peek" value.
- */
- boolean getPeek() {
- return peek;
- }
-
- /**
- * Return true if the specified capability string is in the list
- * of capabilities the server announced.
- *
- * @param capability the capability string
- * @return true if the server supports this capability
- * @exception MessagingException for failures
- * @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);
- }
- }
-
- /**
- * Set the user name to be used with the PROXYAUTH command.
- * The PROXYAUTH user name can also be set using the
- * mail.imap.proxyauth.user property when this
- * Store is created.
- *
- * @param user the user name to set
- * @since JavaMail 1.5.1
- */
- public void setProxyAuthUser(String user) {
- proxyAuthUser = user;
- }
-
- /**
- * Get the user name to be used with the PROXYAUTH command.
- *
- * @return the user name
- * @since JavaMail 1.5.1
- */
- public String getProxyAuthUser() {
- return proxyAuthUser;
- }
-
- /**
- * Check whether this store is connected. Override superclass
- * method, to actually ping our server connection.
- */
- @Override
- 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.
- */
- @Override
- public synchronized void close() throws MessagingException {
- cleanup();
- // do these again in case cleanup returned early
- // because we were already closed due to a failure,
- // in which case we force close everything
- closeAllFolders(true);
- emptyConnectionPool(true);
- }
-
- @Override
- protected void finalize() throws Throwable {
- if (!finalizeCleanClose) {
- // when finalizing, close connections abruptly
- synchronized (connectionFailedLock) {
- connectionFailed = true;
- forceClose = true;
- }
- closeFoldersOnStoreFailure = true; // make sure folders get closed
- }
- try {
- close();
- } finally {
- super.finalize();
- }
- }
-
- /**
- * 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);
-
- if (!force || closeFoldersOnStoreFailure) {
- closeAllFolders(force);
- }
-
- emptyConnectionPool(force);
-
- // to set the state and send the closed connection event
- try {
- super.close();
- } catch (MessagingException mex) {
- // ignore it
- }
- logger.fine("IMAPStore cleanup done");
- }
-
- /**
- * Close all open Folders. If force is true, close them forcibly.
- */
- private void closeAllFolders(boolean force) {
- List 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 = foldersCopy.get(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
- }
- }
-
- }
- }
-
- /**
- * Get the default folder, representing the root of this user's
- * namespace. Returns a closed DefaultFolder object.
- */
- @Override
- public synchronized Folder getDefaultFolder() throws MessagingException {
- checkConnected();
- return new DefaultFolder(this);
- }
-
- /**
- * Get named folder. Returns a new, closed IMAPFolder.
- */
- @Override
- public synchronized Folder getFolder(String name)
- throws MessagingException {
- checkConnected();
- return newIMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR);
- }
-
- /**
- * Get named folder. Returns a new, closed IMAPFolder.
- */
- @Override
- 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.
- *
- * @param fullName the full name of the folder
- * @param separator the separator character for the folder hierarchy
- * @param isNamespace does this name represent a namespace?
- * @return the new IMAPFolder object
- */
- protected IMAPFolder newIMAPFolder(String fullName, char separator,
- Boolean isNamespace) {
- IMAPFolder f = null;
- if (folderConstructor != null) {
- try {
- Object[] o =
- { fullName, Character.valueOf(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.
- *
- * @param fullName the full name of the folder
- * @param separator the separator character for the folder hierarchy
- * @return the new IMAPFolder object
- */
- 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.
- *
- * @param li the ListInfo for the folder
- * @return the new IMAPFolder object
- */
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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
- */
- @Override
- 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
- */
- @Override
- 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.
- */
- @Override
- 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) {
- // restore the interrupted state, which callers might
- // depend on
- Thread.currentThread().interrupt();
- // stop waiting and return to caller
- throw new MessagingException("idle interrupted", 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) {
- // restore the interrupted state, which callers might
- // depend on
- Thread.currentThread().interrupt();
- }
- }
-
- } 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) {
- // If someone is trying to interrupt us we can't keep going
- // around the loop waiting for IDLE to complete, but we can't
- // just return because callers expect the idleState to be
- // RUNNING when we return. Throwing this exception seems
- // like the best choice.
- throw new ProtocolException("Interrupted waitIfIdle", ex);
- }
- }
- }
-
- /**
- * Send the IMAP ID command (if supported by the server) and return
- * the result from the server. The ID command identfies the client
- * to the server and returns information about the server to the client.
- * See RFC 2971 .
- * The returned Map is unmodifiable.
- *
- * @param clientParams a Map of keys and values identifying the client
- * @return a Map of keys and values identifying the server
- * @exception MessagingException if the server doesn't support the
- * ID extension
- * @since JavaMail 1.5.1
- */
- public synchronized Map id(Map clientParams)
- throws MessagingException {
- checkConnected();
- Map serverParams = null;
-
- IMAPProtocol p = null;
- try {
- p = getStoreProtocol();
- serverParams = p.id(clientParams);
- } catch (BadCommandException bex) {
- throw new MessagingException("ID 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 serverParams;
- }
-
- /**
- * Handle notifications and alerts.
- * Response must be an OK, NO, BAD, or BYE response.
- */
- void handleResponseCode(Response r) {
- if (enableResponseEvents)
- notifyStoreListeners(IMAPStore.RESPONSE, r.toString());
- 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/app/src/main/java/com/sun/mail/imap/IdleManager.java b/app/src/main/java/com/sun/mail/imap/IdleManager.java
deleted file mode 100644
index 781f58d5cd..0000000000
--- a/app/src/main/java/com/sun/mail/imap/IdleManager.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.Socket;
-import java.nio.*;
-import java.nio.channels.*;
-import java.util.*;
-import java.util.logging.*;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executor;
-
-import javax.mail.*;
-
-import com.sun.mail.imap.protocol.IMAPProtocol;
-import com.sun.mail.util.MailLogger;
-
-/**
- * IdleManager uses the optional IMAP IDLE command
- * (RFC 2177 )
- * to watch multiple folders for new messages.
- * IdleManager uses an Executor to execute tasks in separate threads.
- * An Executor is typically provided by an ExecutorService.
- * For example, for a Java SE application:
- *
- * ExecutorService es = Executors.newCachedThreadPool();
- * final IdleManager idleManager = new IdleManager(session, es);
- *
- * For a Java EE 7 application:
- *
- * {@literal @}Resource
- * ManagedExecutorService es;
- * final IdleManager idleManager = new IdleManager(session, es);
- *
- * To watch for new messages in a folder, open the folder, register a listener,
- * and ask the IdleManager to watch the folder:
- *
- * Folder folder = store.getFolder("INBOX");
- * folder.open(Folder.READ_WRITE);
- * folder.addMessageCountListener(new MessageCountAdapter() {
- * public void messagesAdded(MessageCountEvent ev) {
- * Folder folder = (Folder)ev.getSource();
- * Message[] msgs = ev.getMessages();
- * System.out.println("Folder: " + folder +
- * " got " + msgs.length + " new messages");
- * try {
- * // process new messages
- * idleManager.watch(folder); // keep watching for new messages
- * } catch (MessagingException mex) {
- * // handle exception related to the Folder
- * }
- * }
- * });
- * idleManager.watch(folder);
- *
- * This delivers the events for each folder in a separate thread, NOT
- * using the Executor. To deliver all events in a single thread
- * using the Executor, set the following properties for the Session
- * (once), and then add listeners and watch the folder as above.
- *
- * // the following should be done once...
- * Properties props = session.getProperties();
- * props.put("mail.event.scope", "session"); // or "application"
- * props.put("mail.event.executor", es);
- *
- * Note that, after processing new messages in your listener, or doing any
- * other operations on the folder in any other thread, you need to tell
- * the IdleManager to watch for more new messages. Unless, of course, you
- * close the folder.
- *
- * The IdleManager is created with a Session, which it uses only to control
- * debug output. A single IdleManager instance can watch multiple Folders
- * from multiple Stores and multiple Sessions.
- *
- * Due to limitations in the Java SE nio support, a
- * {@link java.nio.channels.SocketChannel SocketChannel} must be used instead
- * of a {@link java.net.Socket Socket} to connect to the server. However,
- * SocketChannels don't support all the features of Sockets, such as connecting
- * through a SOCKS proxy server. SocketChannels also don't support
- * simultaneous read and write, which means that the
- * {@link com.sun.mail.imap.IMAPFolder#idle idle} method can't be used if
- * SocketChannels are being used; use this IdleManager instead.
- * To enable support for SocketChannels instead of Sockets, set the
- * mail.imap.usesocketchannels property in the Session used to
- * access the IMAP Folder. (Or mail.imaps.usesocketchannels if
- * you're using the "imaps" protocol.) This will effect all connections in
- * that Session, but you can create another Session without this property set
- * if you need to use the features that are incompatible with SocketChannels.
- *
- * NOTE: The IdleManager, and all APIs and properties related to it, should
- * be considered EXPERIMENTAL . They may be changed in the
- * future in ways that are incompatible with applications using the
- * current APIs.
- *
- * @since JavaMail 1.5.2
- */
-public class IdleManager {
- private Executor es;
- private Selector selector;
- private MailLogger logger;
- private volatile boolean die = false;
- private volatile boolean running;
- private Queue toWatch = new ConcurrentLinkedQueue<>();
- private Queue toAbort = new ConcurrentLinkedQueue<>();
-
- /**
- * Create an IdleManager. The Session is used only to configure
- * debugging output. The Executor is used to create the
- * "select" thread.
- *
- * @param session the Session containing configuration information
- * @param es the Executor used to create threads
- * @exception IOException for Selector failures
- */
- public IdleManager(Session session, Executor es) throws IOException {
- this.es = es;
- logger = new MailLogger(this.getClass(), "DEBUG IMAP",
- session.getDebug(), session.getDebugOut());
- selector = Selector.open();
- es.execute(new Runnable() {
- @Override
- public void run() {
- logger.fine("IdleManager select starting");
- try {
- running = true;
- select();
- } finally {
- running = false;
- logger.fine("IdleManager select terminating");
- }
- }
- });
- }
-
- /**
- * Is the IdleManager currently running? The IdleManager starts
- * running when the Executor schedules its task. The IdleManager
- * stops running after its task detects the stop request from the
- * {@link #stop stop} method, or if it terminates abnormally due
- * to an unexpected error.
- *
- * @return true if the IdleMaanger is running
- * @since JavaMail 1.5.5
- */
- public boolean isRunning() {
- return running;
- }
-
- /**
- * Watch the Folder for new messages and other events using the IMAP IDLE
- * command.
- *
- * @param folder the folder to watch
- * @exception MessagingException for errors related to the folder
- */
- public void watch(Folder folder)
- throws MessagingException {
- if (die) // XXX - should be IllegalStateException?
- throw new MessagingException("IdleManager is not running");
- if (!(folder instanceof IMAPFolder))
- throw new MessagingException("Can only watch IMAP folders");
- IMAPFolder ifolder = (IMAPFolder)folder;
- SocketChannel sc = ifolder.getChannel();
- if (sc == null) {
- if (folder.isOpen())
- throw new MessagingException(
- "Folder is not using SocketChannels");
- else
- throw new MessagingException("Folder is not open");
- }
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST, "IdleManager watching {0}",
- folderName(ifolder));
- // keep trying to start the IDLE command until we're successful.
- // may block if we're in the middle of aborting an IDLE command.
- int tries = 0;
- while (!ifolder.startIdle(this)) {
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager.watch startIdle failed for {0}",
- folderName(ifolder));
- tries++;
- }
- if (logger.isLoggable(Level.FINEST)) {
- if (tries > 0)
- logger.log(Level.FINEST,
- "IdleManager.watch startIdle succeeded for {0}" +
- " after " + tries + " tries",
- folderName(ifolder));
- else
- logger.log(Level.FINEST,
- "IdleManager.watch startIdle succeeded for {0}",
- folderName(ifolder));
- }
- synchronized (this) {
- toWatch.add(ifolder);
- selector.wakeup();
- }
- }
-
- /**
- * Request that the specified folder abort an IDLE command.
- * We can't do the abort directly because the DONE message needs
- * to be sent through the (potentially) SSL socket, which means
- * we need to be in blocking I/O mode. We can only switch to
- * blocking I/O mode when not selecting, so wake up the selector,
- * which will process this request when it wakes up.
- */
- void requestAbort(IMAPFolder folder) {
- toAbort.add(folder);
- selector.wakeup();
- }
-
- /**
- * Run the {@link java.nio.channels.Selector#select select} loop
- * to poll each watched folder for events sent from the server.
- */
- private void select() {
- die = false;
- try {
- while (!die) {
- watchAll();
- logger.finest("IdleManager waiting...");
- int ns = selector.select();
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager selected {0} channels", ns);
- if (die || Thread.currentThread().isInterrupted())
- break;
-
- /*
- * Process any selected folders. We cancel the
- * selection key for any selected folder, so if we
- * need to continue watching that folder it's added
- * to the toWatch list again. We can't actually
- * register that folder again until the previous
- * selection key is cancelled, so we call selectNow()
- * just for the side effect of cancelling the selection
- * keys. But if selectNow() selects something, we
- * process it before adding folders from the toWatch
- * queue. And so on until there is nothing to do, at
- * which point it's safe to register folders from the
- * toWatch queue. This should be "fair" since each
- * selection key is used only once before being added
- * to the toWatch list.
- */
- do {
- processKeys();
- } while (selector.selectNow() > 0 || !toAbort.isEmpty());
- }
- } catch (InterruptedIOException ex) {
- logger.log(Level.FINEST, "IdleManager interrupted", ex);
- } catch (IOException ex) {
- logger.log(Level.FINEST, "IdleManager got I/O exception", ex);
- } catch (Exception ex) {
- logger.log(Level.FINEST, "IdleManager got exception", ex);
- } finally {
- die = true; // prevent new watches in case of exception
- logger.finest("IdleManager unwatchAll");
- try {
- unwatchAll();
- selector.close();
- } catch (IOException ex2) {
- // nothing to do...
- logger.log(Level.FINEST, "IdleManager unwatch exception", ex2);
- }
- logger.fine("IdleManager exiting");
- }
- }
-
- /**
- * Register all of the folders in the queue with the selector,
- * switching them to nonblocking I/O mode first.
- */
- private void watchAll() {
- /*
- * Pull each of the folders from the toWatch queue
- * and register it.
- */
- IMAPFolder folder;
- while ((folder = toWatch.poll()) != null) {
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager adding {0} to selector", folderName(folder));
- try {
- SocketChannel sc = folder.getChannel();
- if (sc == null)
- continue;
- // has to be non-blocking to select
- sc.configureBlocking(false);
- sc.register(selector, SelectionKey.OP_READ, folder);
- } catch (IOException ex) {
- // oh well, nothing to do
- logger.log(Level.FINEST,
- "IdleManager can't register folder", ex);
- } catch (CancelledKeyException ex) {
- // this should never happen
- logger.log(Level.FINEST,
- "IdleManager can't register folder", ex);
- }
- }
- }
-
- /**
- * Process the selected keys.
- */
- private void processKeys() throws IOException {
- IMAPFolder folder;
-
- /*
- * First, process any channels with data to read.
- */
- Set selectedKeys = selector.selectedKeys();
- /*
- * XXX - this is simpler, but it can fail with
- * ConcurrentModificationException
- *
- for (SelectionKey sk : selectedKeys) {
- selectedKeys.remove(sk); // only process each key once
- ...
- }
- */
- Iterator it = selectedKeys.iterator();
- while (it.hasNext()) {
- SelectionKey sk = it.next();
- it.remove(); // only process each key once
- // have to cancel so we can switch back to blocking I/O mode
- sk.cancel();
- folder = (IMAPFolder)sk.attachment();
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager selected folder: {0}", folderName(folder));
- SelectableChannel sc = sk.channel();
- // switch back to blocking to allow normal I/O
- sc.configureBlocking(true);
- try {
- if (folder.handleIdle(false)) {
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager continue watching folder {0}",
- folderName(folder));
- // more to do with this folder, select on it again
- toWatch.add(folder);
- } else {
- // done watching this folder,
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager done watching folder {0}",
- folderName(folder));
- }
- } catch (MessagingException ex) {
- // something went wrong, stop watching this folder
- logger.log(Level.FINEST,
- "IdleManager got exception for folder: " +
- folderName(folder), ex);
- }
- }
-
- /*
- * Now, process any folders that we need to abort.
- */
- while ((folder = toAbort.poll()) != null) {
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager aborting IDLE for folder: {0}",
- folderName(folder));
- SocketChannel sc = folder.getChannel();
- if (sc == null)
- continue;
- SelectionKey sk = sc.keyFor(selector);
- // have to cancel so we can switch back to blocking I/O mode
- if (sk != null)
- sk.cancel();
- // switch back to blocking to allow normal I/O
- sc.configureBlocking(true);
-
- // if there's a read timeout, have to do the abort in a new thread
- Socket sock = sc.socket();
- if (sock != null && sock.getSoTimeout() > 0) {
- logger.finest("IdleManager requesting DONE with timeout");
- toWatch.remove(folder);
- final IMAPFolder folder0 = folder;
- es.execute(new Runnable() {
- @Override
- public void run() {
- // send the DONE and wait for the response
- folder0.idleAbortWait();
- }
- });
- } else {
- folder.idleAbort(); // send the DONE message
- // watch for OK response to DONE
- // XXX - what if we also added it above? should be a nop
- toWatch.add(folder);
- }
- }
- }
-
- /**
- * Stop watching all folders. Cancel any selection keys and,
- * most importantly, switch the channel back to blocking mode.
- * If there's any folders waiting to be watched, need to abort
- * them too.
- */
- private void unwatchAll() {
- IMAPFolder folder;
- Set keys = selector.keys();
- for (SelectionKey sk : keys) {
- // have to cancel so we can switch back to blocking I/O mode
- sk.cancel();
- folder = (IMAPFolder)sk.attachment();
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager no longer watching folder: {0}",
- folderName(folder));
- SelectableChannel sc = sk.channel();
- // switch back to blocking to allow normal I/O
- try {
- sc.configureBlocking(true);
- folder.idleAbortWait(); // send the DONE message and wait
- } catch (IOException ex) {
- // ignore it, channel might be closed
- logger.log(Level.FINEST,
- "IdleManager exception while aborting idle for folder: " +
- folderName(folder), ex);
- }
- }
-
- /*
- * Finally, process any folders waiting to be watched.
- */
- while ((folder = toWatch.poll()) != null) {
- if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINEST,
- "IdleManager aborting IDLE for unwatched folder: {0}",
- folderName(folder));
- SocketChannel sc = folder.getChannel();
- if (sc == null)
- continue;
- try {
- // channel should still be in blocking mode, but make sure
- sc.configureBlocking(true);
- folder.idleAbortWait(); // send the DONE message and wait
- } catch (IOException ex) {
- // ignore it, channel might be closed
- logger.log(Level.FINEST,
- "IdleManager exception while aborting idle for folder: " +
- folderName(folder), ex);
- }
- }
- }
-
- /**
- * Stop the IdleManager. The IdleManager can not be restarted.
- */
- public synchronized void stop() {
- die = true;
- logger.fine("IdleManager stopping");
- selector.wakeup();
- }
-
- /**
- * Return the fully qualified name of the folder, for use in log messages.
- * Essentially just the getURLName method, but ignoring the
- * MessagingException that can never happen.
- */
- private static String folderName(Folder folder) {
- try {
- return folder.getURLName().toString();
- } catch (MessagingException mex) {
- // can't happen
- return folder.getStore().toString() + "/" + folder.toString();
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/MessageCache.java b/app/src/main/java/com/sun/mail/imap/MessageCache.java
deleted file mode 100644
index 86c7899aa1..0000000000
--- a/app/src/main/java/com/sun/mail/imap/MessageCache.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the size of the 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.
- *
- * @param msgnum the message number
- * @return the message
- */
- 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.
- *
- * @param seqnum the sequence number of the message
- * @return the message
- */
- 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.
- *
- * @param seqnum the sequence number of the message to expunge
- */
- 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.
- *
- * @return the removed messages
- */
- public IMAPMessage[] removeExpungedMessages() {
- logger.fine("remove expunged messages");
- // list of expunged messages
- List mlist = new ArrayList<>();
-
- /*
- * 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.
- *
- * @param msgs the messages
- * @return the removed messages
- */
- public IMAPMessage[] removeExpungedMessages(Message[] msgs) {
- logger.fine("remove expunged messages");
- // list of expunged messages
- List mlist = new ArrayList<>();
-
- /*
- * 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.
- *
- * @param count the number of messges
- * @param newSeqNum sequence number of first message
- */
- 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.
- *
- * @param msgnum the message number
- * @return the sequence 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/app/src/main/java/com/sun/mail/imap/MessageVanishedEvent.java b/app/src/main/java/com/sun/mail/imap/MessageVanishedEvent.java
deleted file mode 100644
index 056cabc68a..0000000000
--- a/app/src/main/java/com/sun/mail/imap/MessageVanishedEvent.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.Folder;
-import javax.mail.Message;
-import javax.mail.event.MessageCountEvent;
-
-/**
- * This class provides notification of messages that have been removed
- * since the folder was last synchronized.
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-
-public class MessageVanishedEvent extends MessageCountEvent {
-
- /**
- * The message UIDs.
- */
- private long[] uids;
-
- // a reusable empty array
- private static final Message[] noMessages = { };
-
- private static final long serialVersionUID = 2142028010250024922L;
-
- /**
- * Constructor.
- *
- * @param folder the containing folder
- * @param uids the UIDs for the vanished messages
- */
- public MessageVanishedEvent(Folder folder, long[] uids) {
- super(folder, REMOVED, true, noMessages);
- this.uids = uids;
- }
-
- /**
- * Return the UIDs for this event.
- *
- * @return the UIDs
- */
- public long[] getUIDs() {
- return uids;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/ModifiedSinceTerm.java b/app/src/main/java/com/sun/mail/imap/ModifiedSinceTerm.java
deleted file mode 100644
index 7332266f25..0000000000
--- a/app/src/main/java/com/sun/mail/imap/ModifiedSinceTerm.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.Message;
-import javax.mail.search.SearchTerm;
-
-/**
- * Find messages that have been modified since a given MODSEQ value.
- * Relies on the server implementing the CONDSTORE extension
- * (RFC 4551 ).
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-public final class ModifiedSinceTerm extends SearchTerm {
-
- private long modseq;
-
- private static final long serialVersionUID = 5151457469634727992L;
-
- /**
- * Constructor.
- *
- * @param modseq modification sequence number
- */
- public ModifiedSinceTerm(long modseq) {
- this.modseq = modseq;
- }
-
- /**
- * Return the modseq.
- *
- * @return the modseq
- */
- public long getModSeq() {
- return modseq;
- }
-
- /**
- * The match method.
- *
- * @param msg the date comparator is applied to this Message's
- * MODSEQ
- * @return true if the comparison succeeds, otherwise false
- */
- @Override
- public boolean match(Message msg) {
- long m;
-
- try {
- if (msg instanceof IMAPMessage)
- m = ((IMAPMessage)msg).getModSeq();
- else
- return false;
- } catch (Exception e) {
- return false;
- }
-
- return m >= modseq;
- }
-
- /**
- * Equality comparison.
- */
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ModifiedSinceTerm))
- return false;
- return modseq == ((ModifiedSinceTerm)obj).modseq;
- }
-
- /**
- * Compute a hashCode for this object.
- */
- @Override
- public int hashCode() {
- return (int)modseq;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/OlderTerm.java b/app/src/main/java/com/sun/mail/imap/OlderTerm.java
deleted file mode 100644
index 19857c4dfd..0000000000
--- a/app/src/main/java/com/sun/mail/imap/OlderTerm.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.util.Date;
-import javax.mail.Message;
-import javax.mail.search.SearchTerm;
-
-/**
- * Find messages that are older than a given interval (in seconds).
- * Relies on the server implementing the WITHIN search extension
- * (RFC 5032 ).
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-public final class OlderTerm extends SearchTerm {
-
- private int interval;
-
- private static final long serialVersionUID = 3951078948727995682L;
-
- /**
- * Constructor.
- *
- * @param interval number of seconds older
- */
- public OlderTerm(int interval) {
- this.interval = interval;
- }
-
- /**
- * Return the interval.
- *
- * @return the interval
- */
- public int getInterval() {
- return interval;
- }
-
- /**
- * The match method.
- *
- * @param msg the date comparator is applied to this Message's
- * received date
- * @return true if the comparison succeeds, otherwise false
- */
- @Override
- public boolean match(Message msg) {
- Date d;
-
- try {
- d = msg.getReceivedDate();
- } catch (Exception e) {
- return false;
- }
-
- if (d == null)
- return false;
-
- return d.getTime() <=
- System.currentTimeMillis() - ((long)interval * 1000);
- }
-
- /**
- * Equality comparison.
- */
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof OlderTerm))
- return false;
- return interval == ((OlderTerm)obj).interval;
- }
-
- /**
- * Compute a hashCode for this object.
- */
- @Override
- public int hashCode() {
- return interval;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/ReferralException.java b/app/src/main/java/com/sun/mail/imap/ReferralException.java
deleted file mode 100644
index f4a335c163..0000000000
--- a/app/src/main/java/com/sun/mail/imap/ReferralException.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import javax.mail.AuthenticationFailedException;
-
-/**
- * A special kind of AuthenticationFailedException that indicates that
- * the reason for the failure was an IMAP REFERRAL in the response code.
- * See RFC 2221 for details.
- *
- * @since JavaMail 1.5.5
- */
-
-public class ReferralException extends AuthenticationFailedException {
-
- private String url;
- private String text;
-
- private static final long serialVersionUID = -3414063558596287683L;
-
- /**
- * Constructs an ReferralException with the specified URL and text.
- *
- * @param text the detail message
- * @param url the URL
- */
- public ReferralException(String url, String text) {
- super("[REFERRAL " + url + "] " + text);
- this.url = url;
- this.text = text;
- }
-
- /**
- * Return the IMAP URL in the referral.
- *
- * @return the IMAP URL
- */
- public String getUrl() {
- return url;
- }
-
- /**
- * Return the text sent by the server along with the referral.
- *
- * @return the text
- */
- public String getText() {
- return text;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/ResyncData.java b/app/src/main/java/com/sun/mail/imap/ResyncData.java
deleted file mode 100644
index f5c295e426..0000000000
--- a/app/src/main/java/com/sun/mail/imap/ResyncData.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import com.sun.mail.imap.protocol.UIDSet;
-
-/**
- * Resynchronization data as defined by the QRESYNC extension
- * (RFC 5162 ).
- * An instance of ResyncData is supplied to the
- * {@link com.sun.mail.imap.IMAPFolder#open(int,com.sun.mail.imap.ResyncData)
- * IMAPFolder open} method.
- * The CONDSTORE ResyncData instance is used to enable the
- * CONDSTORE extension
- * (RFC 4551 ).
- * A ResyncData instance with uidvalidity and modseq values
- * is used to enable the QRESYNC extension.
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-
-public class ResyncData {
- private long uidvalidity = -1;
- private long modseq = -1;
- private UIDSet[] uids = null;
-
- /**
- * Used to enable only the CONDSTORE extension.
- */
- public static final ResyncData CONDSTORE = new ResyncData(-1, -1);
-
- /**
- * Used to report on changes since the specified modseq.
- * If the UIDVALIDITY of the folder has changed, no message
- * changes will be reported. The application must check the
- * UIDVALIDITY of the folder after open to make sure it's
- * the expected folder.
- *
- * @param uidvalidity the UIDVALIDITY
- * @param modseq the MODSEQ
- */
- public ResyncData(long uidvalidity, long modseq) {
- this.uidvalidity = uidvalidity;
- this.modseq = modseq;
- this.uids = null;
- }
-
- /**
- * Used to limit the reported message changes to those with UIDs
- * in the specified range.
- *
- * @param uidvalidity the UIDVALIDITY
- * @param modseq the MODSEQ
- * @param uidFirst the first UID
- * @param uidLast the last UID
- */
- public ResyncData(long uidvalidity, long modseq,
- long uidFirst, long uidLast) {
- this.uidvalidity = uidvalidity;
- this.modseq = modseq;
- this.uids = new UIDSet[] { new UIDSet(uidFirst, uidLast) };
- }
-
- /**
- * Used to limit the reported message changes to those with the
- * specified UIDs.
- *
- * @param uidvalidity the UIDVALIDITY
- * @param modseq the MODSEQ
- * @param uids the UID values
- */
- public ResyncData(long uidvalidity, long modseq, long[] uids) {
- this.uidvalidity = uidvalidity;
- this.modseq = modseq;
- this.uids = UIDSet.createUIDSets(uids);
- }
-
- /**
- * Get the UIDVALIDITY value specified when this instance was created.
- *
- * @return the UIDVALIDITY value
- */
- public long getUIDValidity() {
- return uidvalidity;
- }
-
- /**
- * Get the MODSEQ value specified when this instance was created.
- *
- * @return the MODSEQ value
- */
- public long getModSeq() {
- return modseq;
- }
-
- /*
- * Package private. IMAPProtocol gets this data indirectly
- * using Utility.getResyncUIDSet().
- */
- UIDSet[] getUIDSet() {
- return uids;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/Rights.java b/app/src/main/java/com/sun/mail/imap/Rights.java
deleted file mode 100644
index d9c4ce0298..0000000000
--- a/app/src/main/java/com/sun/mail/imap/Rights.java
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param right the character representing the right
- * @return the Right object
- */
- 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];
- }
-
- @Override
- 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.
- *
- * @param right the Right to check
- * @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.
- *
- * @param rights the Rights to check
- * @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
- */
- @Override
- 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
- */
- @Override
- 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() {
- List v = new ArrayList<>();
- for (int i = 0; i < this.rights.length; i++)
- if (this.rights[i])
- v.add(Right.getInstance((char)i));
- return v.toArray(new Right[v.size()]);
- }
-
- /**
- * Returns a clone of this Rights object.
- */
- @Override
- 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;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- 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/app/src/main/java/com/sun/mail/imap/SortTerm.java b/app/src/main/java/com/sun/mail/imap/SortTerm.java
deleted file mode 100644
index 6f624e8434..0000000000
--- a/app/src/main/java/com/sun/mail/imap/SortTerm.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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;
- }
-
- @Override
- public String toString() {
- return term;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/Utility.java b/app/src/main/java/com/sun/mail/imap/Utility.java
deleted file mode 100644
index 43f19983e3..0000000000
--- a/app/src/main/java/com/sun/mail/imap/Utility.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-import javax.mail.*;
-
-import com.sun.mail.imap.protocol.MessageSet;
-import com.sun.mail.imap.protocol.UIDSet;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 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.
- *
- * @param msgs the messages
- * @param cond the condition to check
- * @return the MessageSet array
- */
- public static MessageSet[] toMessageSet(Message[] msgs, Condition cond) {
- List v = new ArrayList<>(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.add(set);
- }
-
- if (v.isEmpty()) // No valid messages
- return null;
- else {
- return v.toArray(new MessageSet[v.size()]);
- }
- }
- /**
- * Sort (a copy of) the given array of messages and then
- * run thru the sorted 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.
- *
- * @param msgs the messages
- * @param cond the condition to check
- * @return the MessageSet array
- * @since JavaMail 1.5.4
- */
- public static MessageSet[] toMessageSetSorted(Message[] msgs,
- Condition cond) {
- /*
- * XXX - This is quick and dirty. A more efficient strategy would be
- * to generate an array of message numbers by applying the condition
- * (with zero indicating the message doesn't satisfy the condition),
- * sort it, and then convert it to a MessageSet skipping all the zeroes.
- */
- msgs = msgs.clone();
- Arrays.sort(msgs,
- new Comparator() {
- @Override
- public int compare(Message msg1, Message msg2) {
- return msg1.getMessageNumber() - msg2.getMessageNumber();
- }
- });
- return toMessageSet(msgs, cond);
- }
-
- /**
- * Return UIDSets for the messages. Note that the UIDs
- * must have already been fetched for the messages.
- *
- * @param msgs the messages
- * @return the UIDSet array
- */
- public static UIDSet[] toUIDSet(Message[] msgs) {
- List v = new ArrayList<>(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.add(set);
- }
-
- if (v.isEmpty()) // No valid messages
- return null;
- else {
- return v.toArray(new UIDSet[v.size()]);
- }
- }
-
- /**
- * Make the ResyncData UIDSet available to IMAPProtocol,
- * which is in a different package. Note that this class
- * is not included in the public javadocs, thus "hiding"
- * this method.
- *
- * @param rd the ResyncData
- * @return the UIDSet array
- * @since JavaMail 1.5.1
- */
- public static UIDSet[] getResyncUIDSet(ResyncData rd) {
- return rd.getUIDSet();
- }
-
- /**
- * This interface defines the test to be executed in
- * toMessageSet().
- */
- public static interface Condition {
- public boolean test(IMAPMessage message);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/YoungerTerm.java b/app/src/main/java/com/sun/mail/imap/YoungerTerm.java
deleted file mode 100644
index e8264aed89..0000000000
--- a/app/src/main/java/com/sun/mail/imap/YoungerTerm.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap;
-
-import java.util.Date;
-import javax.mail.Message;
-import javax.mail.search.SearchTerm;
-
-/**
- * Find messages that are younger than a given interval (in seconds).
- * Relies on the server implementing the WITHIN search extension
- * (RFC 5032 ).
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-public final class YoungerTerm extends SearchTerm {
-
- private int interval;
-
- private static final long serialVersionUID = 1592714210688163496L;
-
- /**
- * Constructor.
- *
- * @param interval number of seconds younger
- */
- public YoungerTerm(int interval) {
- this.interval = interval;
- }
-
- /**
- * Return the interval.
- *
- * @return the interval
- */
- public int getInterval() {
- return interval;
- }
-
- /**
- * The match method.
- *
- * @param msg the date comparator is applied to this Message's
- * received date
- * @return true if the comparison succeeds, otherwise false
- */
- @Override
- public boolean match(Message msg) {
- Date d;
-
- try {
- d = msg.getReceivedDate();
- } catch (Exception e) {
- return false;
- }
-
- if (d == null)
- return false;
-
- return d.getTime() >=
- System.currentTimeMillis() - ((long)interval * 1000);
- }
-
- /**
- * Equality comparison.
- */
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof YoungerTerm))
- return false;
- return interval == ((YoungerTerm)obj).interval;
- }
-
- /**
- * Compute a hashCode for this object.
- */
- @Override
- public int hashCode() {
- return interval;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/package.html b/app/src/main/java/com/sun/mail/imap/package.html
deleted file mode 100644
index 1103d26e8d..0000000000
--- a/app/src/main/java/com/sun/mail/imap/package.html
+++ /dev/null
@@ -1,1002 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.imap package
-
-
-
-
-An IMAP protocol provider for the Jakarta Mail API
-that provides access to an IMAP message store.
-Both the IMAP4 and IMAP4rev1 protocols are supported.
-Refer to
-RFC 3501
-for more information.
-The IMAP protocol provider also supports many IMAP extensions (described below).
-Note that the server needs to support these extensions (and not all servers do)
-in order to use the support in the IMAP provider.
-You can query the server for support of these extensions using the
-{@link com.sun.mail.imap.IMAPStore#hasCapability IMAPStore hasCapability}
-method using the capability name defined by the extension
-(see the appropriate RFC) after connecting to the server.
-
-UIDPLUS Support
-
-The IMAP UIDPLUS extension
-(RFC 4315 )
-is supported via the IMAPFolder methods
-{@link com.sun.mail.imap.IMAPFolder#addMessages addMessages},
-{@link com.sun.mail.imap.IMAPFolder#appendUIDMessages appendUIDMessages}, and
-{@link com.sun.mail.imap.IMAPFolder#copyUIDMessages copyUIDMessages}.
-
-MOVE Support
-
-The IMAP MOVE extension
-(RFC 6851 )
-is supported via the IMAPFolder methods
-{@link com.sun.mail.imap.IMAPFolder#moveMessages moveMessages} and
-{@link com.sun.mail.imap.IMAPFolder#moveUIDMessages moveUIDMessages}.
-
-SASL Support
-
-The IMAP protocol provider can use SASL
-(RFC 4422 )
-authentication mechanisms on systems that support the
-javax.security.sasl APIs.
-The SASL-IR
-(RFC 4959 )
-capability is also supported.
-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.
-
-OAuth 2.0 Support
-
-Support for OAuth 2.0 authentication via the
-
-XOAUTH2 authentication mechanism is provided either through the SASL
-support described above or as a built-in authentication mechanism in the
-IMAP provider.
-The OAuth 2.0 Access Token should be passed as the password for this mechanism.
-See
-OAuth2 Support for details.
-
-Connection Pool
-
-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.
-
-QUOTA Support
-
-The IMAP QUOTA extension
-(RFC 2087 )
-is supported via the
-{@link javax.mail.QuotaAwareStore QuotaAwareStore} interface implemented by
-{@link com.sun.mail.imap.IMAPStore IMAPStore}, and the
-{@link com.sun.mail.imap.IMAPFolder#getQuota IMAPFolder getQuota} and
-{@link com.sun.mail.imap.IMAPFolder#setQuota IMAPFolder setQuota} methods.
-ACL Support
-
-The IMAP ACL extension
-(RFC 2086 )
-is supported via the
-{@link com.sun.mail.imap.Rights Rights} class and the IMAPFolder methods
-{@link com.sun.mail.imap.IMAPFolder#getACL getACL},
-{@link com.sun.mail.imap.IMAPFolder#addACL addACL},
-{@link com.sun.mail.imap.IMAPFolder#removeACL removeACL},
-{@link com.sun.mail.imap.IMAPFolder#addRights addRights},
-{@link com.sun.mail.imap.IMAPFolder#removeRights removeRights},
-{@link com.sun.mail.imap.IMAPFolder#listRights listRights}, and
-{@link com.sun.mail.imap.IMAPFolder#myRights myRights}.
-
-SORT Support
-
-The IMAP SORT extension
-(RFC 5256 )
-is supported via the
-{@link com.sun.mail.imap.SortTerm SortTerm} class and the IMAPFolder
-{@link com.sun.mail.imap.IMAPFolder#getSortedMessages getSortedMessages}
-methods.
-
-CONDSTORE and QRESYNC Support
-
-Basic support is provided for the IMAP CONDSTORE
-(RFC 4551 )
-and QRESYNC
-(RFC 5162 )
-extensions for the purpose of resynchronizing a folder after offline operation.
-Of course, the server must support these extensions.
-Use of these extensions is enabled by using the new
-{@link com.sun.mail.imap.IMAPFolder#open(int,com.sun.mail.imap.ResyncData)
-IMAPFolder open} method and supplying an appropriate
-{@link com.sun.mail.imap.ResyncData ResyncData} instance.
-Using
-{@link com.sun.mail.imap.ResyncData#CONDSTORE ResyncData.CONDSTORE}
-enables the CONDSTORE extension, which allows you to discover the
-modification sequence number (modseq) of messages using the
-{@link com.sun.mail.imap.IMAPMessage#getModSeq IMAPMessage getModSeq}
-method and the
-{@link com.sun.mail.imap.IMAPFolder#getHighestModSeq
-IMAPFolder getHighestModSeq} method.
-Using a
-{@link com.sun.mail.imap.ResyncData ResyncData} instance with appropriate
-values also allows the server to report any changes in messages since the last
-resynchronization.
-The changes are reported as a list of
-{@link javax.mail.event.MailEvent MailEvent} instances.
-The special
-{@link com.sun.mail.imap.MessageVanishedEvent MessageVanishedEvent} reports on
-UIDs of messages that have been removed since the last resynchronization.
-A
-{@link javax.mail.event.MessageChangedEvent MessageChangedEvent} reports on
-changes to flags of messages.
-For example:
-
-
- Folder folder = store.getFolder("whatever");
- IMAPFolder ifolder = (IMAPFolder)folder;
- List<MailEvent> events = ifolder.open(Folder.READ_WRITE,
- new ResyncData(prevUidValidity, prevModSeq));
- for (MailEvent ev : events) {
- if (ev instanceOf MessageChangedEvent) {
- // process flag changes
- } else if (ev instanceof MessageVanishedEvent) {
- // process messages that were removed
- }
- }
-
-
-See the referenced RFCs for more details on these IMAP extensions.
-
-WITHIN Search Support
-
-The IMAP WITHIN search extension
-(RFC 5032 )
-is supported via the
-{@link com.sun.mail.imap.YoungerTerm YoungerTerm} and
-{@link com.sun.mail.imap.OlderTerm OlderTerm}
-{@link javax.mail.search.SearchTerm SearchTerms}, which can be used as follows:
-
-
- // search for messages delivered in the last day
- Message[] msgs = folder.search(new YoungerTerm(24 * 60 * 60));
-
-LOGIN-REFERRAL Support
-
-The IMAP LOGIN-REFERRAL extension
-(RFC 2221 )
-is supported.
-If a login referral is received when connecting or when authentication fails, a
-{@link com.sun.mail.imap.ReferralException ReferralException} is thrown.
-A referral can also occur when login succeeds. By default, no exception is
-thrown in this case. To force an exception to be thrown and the authentication
-to fail, set the mail.imap.referralexception property to "true".
-
-COMPRESS Support
-
-The IMAP COMPRESS extension
-(RFC 4978 )
-is supported.
-If the server supports the extension and the
-mail.imap.compress.enable property is set to "true",
-compression will be enabled.
-
-UTF-8 Support
-
-The IMAP UTF8 extension
-(RFC 6855 )
-is supported.
-If the server supports the extension, the client will enable use of UTF-8,
-allowing use of UTF-8 in IMAP protocol strings such as folder names.
-
-Properties
-
-The IMAP protocol provider supports the following properties,
-which may be set in the Jakarta Mail 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.*".
-
-
-IMAP properties
-
-Name
-Type
-Description
-
-
-
-mail.imap.user
-String
-Default user name for IMAP.
-
-
-
-mail.imap.host
-String
-The IMAP server to connect to.
-
-
-
-mail.imap.port
-int
-The IMAP server port to connect to, if the connect() method doesn't
-explicitly specify one. Defaults to 143.
-
-
-
-mail.imap.partialfetch
-boolean
-Controls whether the IMAP partial-fetch capability should be used.
-Defaults to true.
-
-
-
-mail.imap.fetchsize
-int
-Partial fetch size in bytes. Defaults to 16K.
-
-
-
-mail.imap.peek
-boolean
-
-If set to true, use the IMAP PEEK option when fetching body parts,
-to avoid setting the SEEN flag on messages.
-Defaults to false.
-Can be overridden on a per-message basis by the
-{@link com.sun.mail.imap.IMAPMessage#setPeek setPeek}
-method on IMAPMessage.
-
-
-
-
-mail.imap.ignorebodystructuresize
-boolean
-The 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.connectiontimeout
-int
-Socket connection timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.imap.timeout
-int
-Socket read timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.imap.writetimeout
-int
-Socket write timeout value in milliseconds.
-This timeout is implemented by using a
-java.util.concurrent.ScheduledExecutorService per connection
-that schedules a thread to close the socket if the timeout expires.
-Thus, the overhead of using this timeout is one thread per connection.
-Default is infinite timeout.
-
-
-
-mail.imap.statuscachetimeout
-int
-Timeout value in milliseconds for cache of STATUS command response.
-Default is 1000 (1 second). Zero disables cache.
-
-
-
-mail.imap.appendbuffersize
-int
-
-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.connectionpoolsize
-int
-Maximum number of available connections in the connection pool.
-Default is 1.
-
-
-
-mail.imap.connectionpooltimeout
-int
-Timeout value in milliseconds for connection pool connections. Default
-is 45000 (45 seconds).
-
-
-
-mail.imap.separatestoreconnection
-boolean
-Flag to indicate whether to use a dedicated store connection for store
-commands. Default is false.
-
-
-
-mail.imap.allowreadonlyselect
-boolean
-If 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.mechanisms
-String
-
-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 "PLAIN LOGIN NTLM", which includes all
-the authentication mechanisms supported by the current implementation
-except XOAUTH2.
-
-
-
-
-mail.imap.auth.login.disable
-boolean
-If true, prevents use of the non-standard AUTHENTICATE LOGIN
-command, instead using the plain LOGIN command.
-Default is false.
-
-
-
-mail.imap.auth.plain.disable
-boolean
-If true, prevents use of the AUTHENTICATE PLAIN command.
-Default is false.
-
-
-
-mail.imap.auth.ntlm.disable
-boolean
-If true, prevents use of the AUTHENTICATE NTLM command.
-Default is false.
-
-
-
-mail.imap.auth.ntlm.domain
-String
-
-The NTLM authentication domain.
-
-
-
-
-mail.imap.auth.ntlm.flags
-int
-
-NTLM protocol-specific flags.
-See
-http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details.
-
-
-
-
-mail.imap.auth.xoauth2.disable
-boolean
-If true, prevents use of the AUTHENTICATE XOAUTH2 command.
-Because the OAuth 2.0 protocol requires a special access token instead of
-a password, this mechanism is disabled by default. Enable it by explicitly
-setting this property to "false" or by setting the "mail.imap.auth.mechanisms"
-property to "XOAUTH2".
-
-
-
-mail.imap.proxyauth.user
-String
-If 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.localaddress
-String
-
-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.localport
-int
-
-Local port number to bind to when creating the IMAP socket.
-Defaults to the port number picked by the Socket class.
-
-
-
-
-mail.imap.sasl.enable
-boolean
-
-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.mechanisms
-String
-
-A space or comma separated list of SASL mechanism names to try
-to use.
-
-
-
-
-mail.imap.sasl.authorizationid
-String
-
-The authorization ID to use in the SASL authentication.
-If not set, the authentication ID (user name) is used.
-
-
-
-
-mail.imap.sasl.realm
-String
-The realm to use with SASL authentication mechanisms that
-require a realm, such as DIGEST-MD5.
-
-
-
-mail.imap.sasl.usecanonicalhostname
-boolean
-
-If set to true, the canonical host name returned by
-{@link java.net.InetAddress#getCanonicalHostName InetAddress.getCanonicalHostName}
-is passed to the SASL mechanism, instead of the host name used to connect.
-Defaults to false.
-
-
-
-
-mail.imap.sasl. xgwtrustedapphack.enable
-boolean
-
-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.socketFactory
-SocketFactory
-
-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.class
-String
-
-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.fallback
-boolean
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.imap.usesocketchannels
-boolean
-
-If set to true, use SocketChannels instead of Sockets for connecting
-to the server. Required if using the IdleManager.
-Ignored if a socket factory is set.
-Defaults to false.
-
-
-
-
-mail.imap.ssl.enable
-boolean
-
-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.checkserveridentity
-boolean
-
-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.trust
-String
-
-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.socketFactory
-SSLSocketFactory
-
-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.class
-String
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.imap.ssl.protocols
-string
-
-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.ciphersuites
-string
-
-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.enable
-boolean
-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.
-If the server does not support STARTTLS, the connection continues without
-the use of TLS; see the
-mail.imap.starttls.required
-property to fail if STARTTLS isn't supported.
-Note that an appropriate trust store must configured so that the client
-will trust the server's certificate.
-Default is false.
-
-
-
-mail.imap.starttls.required
-boolean
-
-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.proxy.host
-string
-
-Specifies the host name of an HTTP web proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.imap.proxy.port
-string
-
-Specifies the port number for the HTTP web proxy server.
-Defaults to port 80.
-
-
-
-
-mail.imap.proxy.user
-string
-
-Specifies the user name to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.imap.proxy.password
-string
-
-Specifies the password to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.imap.socks.host
-string
-
-Specifies the host name of a SOCKS5 proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.imap.socks.port
-string
-
-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.minidletime
-int
-
-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.enableresponseevents
-boolean
-
-Enable special IMAP-specific events to be delivered to the Store's
-ConnectionListener. If true, IMAP OK, NO, BAD, or BYE responses
-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.enableimapevents
-boolean
-
-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.throwsearchexception
-boolean
-
-If set to true and a {@link javax.mail.search.SearchTerm SearchTerm}
-passed to the
-{@link javax.mail.Folder#search Folder.search}
-method is too complex for the IMAP protocol, throw a
-{@link javax.mail.search.SearchException SearchException}.
-For example, the IMAP protocol only supports less-than and greater-than
-comparisons for a {@link javax.mail.search.SizeTerm SizeTerm}.
-If false, the search will be done locally by fetching the required
-message data and comparing it locally.
-Defaults to false.
-
-
-
-
-mail.imap.folder.class
-String
-
-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)
-
-
-
-
-mail.imap.closefoldersonstorefailure
-boolean
-
-In some cases, a failure of the Store connection indicates a failure of the
-server, and all Folders associated with that Store should also be closed.
-In other cases, a Store connection failure may be a transient failure, and
-Folders may continue to operate normally.
-If this property is true (the default), failures in the Store connection cause
-all associated Folders to be closed.
-Set this property to false to better handle transient failures in the Store
-connection.
-
-
-
-
-mail.imap.finalizecleanclose
-boolean
-
-When the finalizer for IMAPStore is called,
-should the connection to the server be closed cleanly, as if the
-application called the close method?
-Or should the connection to the server be closed without sending
-any commands to the server?
-Defaults to false, the connection is not closed cleanly.
-
-
-
-
-mail.imap.referralexception
-boolean
-
-If set to true and an IMAP login referral is returned when the authentication
-succeeds, fail the connect request and throw a
-{@link com.sun.mail.imap.ReferralException ReferralException}.
-Defaults to false.
-
-
-
-
-mail.imap.compress.enable
-boolean
-
-If set to true and the IMAP server supports the COMPRESS=DEFLATE extension,
-compression will be enabled.
-Defaults to false.
-
-
-
-
-mail.imap.compress.level
-int
-
-The compression level to be used, in the range -1 to 9.
-See the {@link java.util.zip.Deflater Deflater} class for details.
-
-
-
-
-mail.imap.compress.strategy
-int
-
-The compression strategy to be used, in the range 0 to 2.
-See the {@link java.util.zip.Deflater Deflater} class for details.
-
-
-
-
-mail.imap.reusetagprefix
-boolean
-
-If true, always use "A" for the IMAP command tag prefix.
-If false, the IMAP command tag prefix is different for each connection,
-from "A" through "ZZZ" and then wrapping around to "A".
-Applications should never need to set this.
-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 the
-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.
-
-Loggers
-
-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:
-
-
-IMAP Loggers
-
-Logger Name
-Logging Level
-Purpose
-
-
-
-com.sun.mail.imap
-CONFIG
-Configuration of the IMAPStore
-
-
-
-com.sun.mail.imap
-FINE
-General debugging output
-
-
-
-com.sun.mail.imap.connectionpool
-CONFIG
-Configuration of the IMAP connection pool
-
-
-
-com.sun.mail.imap.connectionpool
-FINE
-Debugging output related to the IMAP connection pool
-
-
-
-com.sun.mail.imap.messagecache
-CONFIG
-Configuration of the IMAP message cache
-
-
-
-com.sun.mail.imap.messagecache
-FINE
-Debugging output related to the IMAP message cache
-
-
-
-com.sun.mail.imap.protocol
-FINEST
-Complete protocol trace
-
-
-
-WARNING
-
-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/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java b/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java
deleted file mode 100644
index af35020e9c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java b/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java
deleted file mode 100644
index 1013baf021..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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 "-
- * ").
- *
- * 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
- *
- * @param what where to write the encoded name
- */
- 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/app/src/main/java/com/sun/mail/imap/protocol/BODY.java b/app/src/main/java/com/sun/mail/imap/protocol/BODY.java
deleted file mode 100644
index 5f04edc2c5..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/BODY.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- * @author Bill Shannon
- */
-
-public class BODY implements Item {
-
- static final char[] name = {'B','O','D','Y'};
-
- private final int msgno;
- private final ByteArray data;
- private final String section;
- private final int origin;
- private final boolean isHeader;
-
- /**
- * Constructor
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- public BODY(FetchResponse r) throws ParsingException {
- msgno = r.getNumber();
-
- r.skipSpaces();
-
- if (r.readByte() != '[')
- throw new ParsingException(
- "BODY parse error: missing ``['' at section start");
- section = r.readString(']');
- if (r.readByte() != ']')
- throw new ParsingException(
- "BODY parse error: missing ``]'' at section end");
- isHeader = section.regionMatches(true, 0, "HEADER", 0, 6);
-
- if (r.readByte() == '<') { // origin
- origin = r.readNumber();
- r.skip(1); // skip '>';
- } else
- origin = -1;
-
- data = r.readByteArray();
- }
-
- public ByteArray getByteArray() {
- return data;
- }
-
- public ByteArrayInputStream getByteArrayInputStream() {
- if (data != null)
- return data.toByteArrayInputStream();
- else
- return null;
- }
-
- public boolean isHeader() {
- return isHeader;
- }
-
- public String getSection() {
- return section;
- }
-
- /**
- * @since Jakarta Mail 1.6.4
- */
- public int getOrigin() {
- return origin;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java b/app/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
deleted file mode 100644
index 3a5945cc0e..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-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 final 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;
- List v = new ArrayList<>(1);
- int i = 1;
- do {
- v.add(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 = v.toArray(new BODYSTRUCTURE[v.size()]);
-
- subtype = r.readString(); // subtype
- if (parseDebug)
- System.out.println("DEBUG IMAP: subtype " + subtype);
-
- if (r.isNextNonSpace(')')) { // 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.isNextNonSpace(')')) { // done
- if (parseDebug)
- System.out.println("DEBUG IMAP: body parameters DONE");
- return;
- }
-
- // Disposition
- byte b = r.peekByte();
- if (b == '(') {
- if (parseDebug)
- System.out.println("DEBUG IMAP: parse disposition");
- r.readByte();
- disposition = r.readString();
- if (parseDebug)
- System.out.println("DEBUG IMAP: disposition " +
- disposition);
- dParams = parseParameters(r);
- if (!r.isNextNonSpace(')')) // 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(3); // skip 'NIL'
- } else {
- /*
- throw new ParsingException(
- "BODYSTRUCTURE parse error: " +
- type + "/" + subtype + ": " +
- "bad multipart disposition, b " + b);
- */
- if (parseDebug)
- System.out.println("DEBUG IMAP: bad multipart disposition" +
- ", applying Exchange bug workaround");
- description = r.readString();
- if (parseDebug)
- System.out.println("DEBUG IMAP: multipart description " +
- description);
- // Throw away whatever comes after it, since we have no
- // idea what it's supposed to be
- while (r.readByte() == ' ')
- parseBodyExtension(r);
- return;
- }
-
- // RFC3501 allows no body-fld-lang after body-fld-disp,
- // even though RFC2060 required it
- if (r.isNextNonSpace(')')) {
- if (parseDebug)
- System.out.println("DEBUG IMAP: no body-fld-lang");
- return; // done
- }
-
- // 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 if (r.peekByte() == ')') { // (illegal) empty body
- /*
- * Domino will fail to return the body structure of nested messages.
- * Fake it by providing an empty message. Could probably do better
- * with more work...
- */
- /*
- * XXX - this prevents the exception, but without the exception
- * the application has no way to know the data from the message
- * is missing.
- *
- if (parseDebug)
- System.out.println("DEBUG IMAP: empty body, fake it");
- r.readByte();
- type = "text";
- subtype = "plain";
- lines = 0;
- size = 0;
- */
- throw new ParsingException(
- "BODYSTRUCTURE parse error: missing body content");
- } 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")) {
- if (parseDebug)
- System.out.println("DEBUG IMAP: NIL encoding" +
- ", applying Exchange bug workaround");
- encoding = null;
- }
- /*
- * XXX - Work around bug in office365.com that returns
- * a string with a trailing space in some cases.
- */
- if (encoding != null)
- encoding = encoding.trim();
- 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.isNextNonSpace(')')) {
- if (parseDebug)
- System.out.println("DEBUG IMAP: parse DONE");
- return; // done
- }
-
- // Optional extension data
-
- // MD5
- md5 = r.readString();
- if (r.isNextNonSpace(')')) {
- 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.isNextNonSpace(')')) // 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.isNextNonSpace(')')) {
- 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);
- if (value == null) { // work around buggy servers
- if (parseDebug)
- System.out.println("DEBUG IMAP: NIL parameter value" +
- ", applying Exchange bug workaround");
- value = "";
- }
- list.set(name, value);
- } while (!r.isNextNonSpace(')'));
- 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.isNextNonSpace(')'));
- } else if (Character.isDigit((char)b)) // number
- r.readNumber();
- else // nstring
- r.readString();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java b/app/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java
deleted file mode 100644
index eeafd143e2..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-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.*;
-import com.sun.mail.util.PropUtil;
-
-/**
- * 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 final MailDateFormat mailDateFormat = new MailDateFormat();
-
- // special debugging output to debug parsing errors
- private static final boolean parseDebug =
- PropUtil.getBooleanSystemProperty("mail.imap.parse.debug", false);
-
- public ENVELOPE(FetchResponse r) throws ParsingException {
- if (parseDebug)
- System.out.println("parse ENVELOPE");
- msgno = r.getNumber();
-
- r.skipSpaces();
-
- if (r.readByte() != '(')
- throw new ParsingException("ENVELOPE parse error");
-
- String s = r.readString();
- if (s != null) {
- try {
- synchronized (mailDateFormat) {
- date = mailDateFormat.parse(s);
- }
- } catch (ParseException pex) {
- }
- }
- if (parseDebug)
- System.out.println(" Date: " + date);
-
- subject = r.readString();
- if (parseDebug)
- System.out.println(" Subject: " + subject);
- if (parseDebug)
- System.out.println(" From addresses:");
- from = parseAddressList(r);
- if (parseDebug)
- System.out.println(" Sender addresses:");
- sender = parseAddressList(r);
- if (parseDebug)
- System.out.println(" Reply-To addresses:");
- replyTo = parseAddressList(r);
- if (parseDebug)
- System.out.println(" To addresses:");
- to = parseAddressList(r);
- if (parseDebug)
- System.out.println(" Cc addresses:");
- cc = parseAddressList(r);
- if (parseDebug)
- System.out.println(" Bcc addresses:");
- bcc = parseAddressList(r);
- inReplyTo = r.readString();
- if (parseDebug)
- System.out.println(" In-Reply-To: " + inReplyTo);
- messageId = r.readString();
- if (parseDebug)
- System.out.println(" Message-ID: " + messageId);
-
- if (!r.isNextNonSpace(')'))
- throw new ParsingException("ENVELOPE parse error");
- }
-
- private InternetAddress[] parseAddressList(Response r)
- throws ParsingException {
- r.skipSpaces(); // skip leading spaces
-
- byte b = r.readByte();
- if (b == '(') {
- /*
- * Some broken servers (e.g., Yahoo Mail) return an empty
- * list instead of NIL. Handle that here even though it
- * doesn't conform to the IMAP spec.
- */
- if (r.isNextNonSpace(')'))
- return null;
-
- List v = new ArrayList<>();
-
- do {
- IMAPAddress a = new IMAPAddress(r);
- if (parseDebug)
- System.out.println(" Address: " + a);
- // if we see an end-of-group address at the top, ignore it
- if (!a.isEndOfGroup())
- v.add(a);
- } while (!r.isNextNonSpace(')'));
-
- return v.toArray(new InternetAddress[v.size()]);
- } 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.isNextNonSpace(')')) // 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 List and the corresponding string
- // representation of the group is accumulated in a StringBuilder.
- StringBuilder sb = new StringBuilder();
- sb.append(groupname).append(':');
- List v = new ArrayList<>();
- 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.add(a);
- }
- sb.append(';');
- address = sb.toString();
- grouplist = v.toArray(new IMAPAddress[v.size()]);
- } 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;
- }
-
- @Override
- public boolean isGroup() {
- return group;
- }
-
- @Override
- public InternetAddress[] getGroup(boolean strict) throws AddressException {
- if (grouplist == null)
- return null;
- return grouplist.clone();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/FLAGS.java b/app/src/main/java/com/sun/mail/imap/protocol/FLAGS.java
deleted file mode 100644
index 5621aa81c3..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/FLAGS.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the IMAPResponse
- * @exception ParsingException for parsing failures
- */
- 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/app/src/main/java/com/sun/mail/imap/protocol/FetchItem.java b/app/src/main/java/com/sun/mail/imap/protocol/FetchItem.java
deleted file mode 100644
index 1eaae6c1c7..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/FetchItem.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the response
- * @return the fetch item
- * @exception ParsingException for parsing failures
- */
- public abstract Object parseItem(FetchResponse r) throws ParsingException;
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java b/app/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
deleted file mode 100644
index 6477ba5b2d..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.io.*;
-import java.util.*;
-import com.sun.mail.util.ASCIIUtility;
-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.
- *
- * @param r the IMAPResponse
- * @param fitems the fetch items
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- * @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 T getItem(Class c) {
- for (int i = 0; i < items.length; i++) {
- if (c.isInstance(items[i]))
- return c.cast(items[i]);
- }
-
- return null;
- }
-
- /**
- * Return the first fetch response item of the given class
- * for the given message number.
- *
- * @param r the responses
- * @param msgno the message number
- * @param c the class
- * @param the type of fetch item
- * @return the fetch item
- */
- public static T 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 c.cast(f.items[j]);
- }
- }
-
- return null;
- }
-
- /**
- * Return all fetch response items of the given class
- * for the given message number.
- *
- * @param r the responses
- * @param msgno the message number
- * @param c the class
- * @param the type of fetch items
- * @return the list of fetch items
- * @since JavaMail 1.5.2
- */
- public static List getItems(Response[] r, int msgno,
- Class c) {
- List items = new ArrayList<>();
-
- if (r == null)
- return items;
-
- 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]))
- items.add(c.cast(f.items[j]));
- }
- }
-
- return items;
- }
-
- /**
- * 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.
- *
- * @return Map of extension items, or null if none
- * @since JavaMail 1.4.6
- */
- public Map getExtensionItems() {
- 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 {
- if (!isNextNonSpace('('))
- throw new ParsingException(
- "error in FETCH parsing, missing '(' at index " + index);
-
- List- v = new ArrayList<>();
- Item i = null;
- skipSpaces();
- do {
-
- if (index >= size)
- throw new ParsingException(
- "error in FETCH parsing, ran off end of buffer, size " + size);
-
- i = parseItem();
- if (i != null)
- v.add(i);
- else if (!parseExtensionItem())
- throw new ParsingException(
- "error in FETCH parsing, unrecognized item at index " +
- index + ", starts with \"" + next20() + "\"");
- } while (!isNextNonSpace(')'));
-
- items = v.toArray(new Item[v.size()]);
- }
-
- /**
- * Return the next 20 characters in the buffer, for exception messages.
- */
- private String next20() {
- if (index + 20 > size)
- return ASCIIUtility.toString(buffer, index, size);
- else
- return ASCIIUtility.toString(buffer, index, index + 20) + "...";
- }
-
- /**
- * 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.
- */
- @SuppressWarnings("empty")
- 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)) {
- boolean isHeader = false;
- if (match(HEADER))
- isHeader = true; // skip ".HEADER"
- else if (match(TEXT))
- isHeader = false; // skip ".TEXT"
- return new RFC822DATA(this, isHeader);
- }
- break;
- case 'U': case 'u':
- if (match(UID.name))
- return new UID(this);
- break;
- case 'M': case 'm':
- if (match(MODSEQ.name))
- return new MODSEQ(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())) {
- if (extensionItems == null)
- extensionItems = new HashMap<>();
- extensionItems.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/app/src/main/java/com/sun/mail/imap/protocol/ID.java b/app/src/main/java/com/sun/mail/imap/protocol/ID.java
deleted file mode 100644
index 66cb865fcc..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/ID.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.*;
-import com.sun.mail.iap.*;
-
-/**
- * This class represents the response to the ID command.
- *
- * See RFC 2971 .
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-
-public class ID {
-
- private Map serverParams = null;
-
- /**
- * Parse the server parameter list out of the response.
- *
- * @param r the response
- * @exception ProtocolException for protocol failures
- */
- public ID(Response r) throws ProtocolException {
- // id_response ::= "ID" SPACE id_params_list
- // id_params_list ::= "(" #(string SPACE nstring) ")" / nil
- // ;; list of field value pairs
-
- r.skipSpaces();
- int c = r.peekByte();
- if (c == 'N' || c == 'n') // assume NIL
- return;
-
- if (c != '(')
- throw new ProtocolException("Missing '(' at start of ID");
-
- serverParams = new HashMap<>();
-
- String[] v = r.readStringList();
- if (v != null) {
- for (int i = 0; i < v.length; i += 2) {
- String name = v[i];
- if (name == null)
- throw new ProtocolException("ID field name null");
- if (i + 1 >= v.length)
- throw new ProtocolException("ID field without value: " +
- name);
- String value = v[i + 1];
- serverParams.put(name, value);
- }
- }
- serverParams = Collections.unmodifiableMap(serverParams);
- }
-
- /**
- * Return the parsed server params.
- */
- Map getServerParams() {
- return serverParams;
- }
-
- /**
- * Convert the client parameters into an argument list for the ID command.
- */
- static Argument getArgumentList(Map clientParams) {
- Argument arg = new Argument();
- if (clientParams == null) {
- arg.writeAtom("NIL");
- return arg;
- }
- Argument list = new Argument();
- // add params to list
- for (Map.Entry e : clientParams.entrySet()) {
- list.writeNString(e.getKey()); // assume these are ASCII only
- list.writeNString(e.getValue());
- }
- arg.writeArgument(list);
- return arg;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java b/app/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
deleted file mode 100644
index a8a88b6c01..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
+++ /dev/null
@@ -1,3294 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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 java.nio.charset.StandardCharsets;
-
-import javax.mail.*;
-import javax.mail.internet.*;
-import javax.mail.search.*;
-
-import com.sun.mail.util.PropUtil;
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.ASCIIUtility;
-import com.sun.mail.util.BASE64EncoderStream;
-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.CopyUID;
-import com.sun.mail.imap.SortTerm;
-import com.sun.mail.imap.ResyncData;
-import com.sun.mail.imap.Utility;
-
-/**
- * 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 referralException; // throw exception for IMAP REFERRAL?
- 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.
- private boolean utf8; // UTF-8 support enabled?
-
- protected SearchSequence searchSequence;
- protected String[] searchCharsets; // array of search charsets
-
- protected Set enabled; // enabled capabilities - RFC 5161
-
- private String name;
- private SaslAuthenticator saslAuthenticator; // if SASL is being used
- private String proxyAuthUser; // user name used with PROXYAUTH
-
- 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 name the protocol name
- * @param host host to connect to
- * @param port port number to connect to
- * @param props Properties object used by this protocol
- * @param isSSL true if SSL should be used
- * @param logger the MailLogger to use for debug output
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- 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);
-
- // in case it was not initialized in processGreeting
- referralException = PropUtil.getBooleanProperty(props,
- prefix + ".referralexception", 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();
- }
- }
-
- /**
- * Constructor for debugging.
- *
- * @param in the InputStream from which to read
- * @param out the PrintStream to which to write
- * @param props Properties object used by this protocol
- * @param debug true to enable debugging output
- * @exception IOException for I/O errors
- */
- public IMAPProtocol(InputStream in, PrintStream out,
- Properties props, boolean debug)
- throws IOException {
- super(in, out, props, debug);
-
- this.name = "imap";
- noauthdebug =
- !PropUtil.getBooleanProperty(props, "mail.debug.auth", false);
-
- if (capabilities == null)
- capabilities = new HashMap<>();
-
- 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
- }
-
- /**
- * 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.
- *
- * @return an array of FetchItem objects
- * @since JavaMail 1.4.6
- */
- public FetchItem[] getFetchItems() {
- return fetchItems;
- }
-
- /**
- * CAPABILITY command.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.1.1"
- */
- public void capability() throws ProtocolException {
- // Check CAPABILITY
- Response[] r = command("CAPABILITY", null);
- Response response = r[r.length-1];
-
- if (response.isOK())
- handleCapabilityResponse(r);
- handleResult(response);
- }
-
- /**
- * Handle any untagged CAPABILITY response in the Response array.
- *
- * @param r the responses
- */
- public void handleCapabilityResponse(Response[] r) {
- boolean first = true;
- 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")) {
- if (first) {
- // clear out current when first response seen
- capabilities = new HashMap<>(10);
- authmechs = new ArrayList<>(5);
- first = false;
- }
- parseCapabilities(ir);
- }
- }
- }
-
- /**
- * If the response contains a CAPABILITY response code, extract
- * it and save the capabilities.
- *
- * @param r the response
- */
- 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.
- *
- * @param r the CAPABILITY 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.
- *
- * @param r the greeting response
- * @exception ProtocolException for protocol failures
- */
- @Override
- protected void processGreeting(Response r) throws ProtocolException {
- if (r.isBYE()) {
- checkReferral(r); // may throw exception
- throw new ConnectionException(this, r);
- }
- if (r.isOK()) { // check if it's OK
- // XXX - is a REFERRAL response code really allowed here?
- // XXX - referralException hasn't been initialized in c'tor yet
- referralException = PropUtil.getBooleanProperty(props,
- prefix + ".referralexception", false);
- if (referralException)
- checkReferral(r);
- setCapabilities(r);
- return;
- }
- // only other choice is PREAUTH
- assert r instanceof IMAPResponse;
- IMAPResponse ir = (IMAPResponse)r;
- if (ir.keyEquals("PREAUTH")) {
- authenticated = true;
- setCapabilities(r);
- } else {
- disconnect();
- throw new ConnectionException(this, r);
- }
- }
-
- /**
- * Check for an IMAP login REFERRAL response code.
- *
- * @exception IMAPReferralException if REFERRAL response code found
- * @see "RFC 2221"
- */
- private void checkReferral(Response r) throws IMAPReferralException {
- String s = r.getRest(); // get the text after the response
- if (s.startsWith("[")) { // a response code
- int i = s.indexOf(' ');
- if (i > 0 && s.substring(1, i).equalsIgnoreCase("REFERRAL")) {
- String url, msg;
- int j = s.indexOf(']');
- if (j > 0) { // should always be true;
- url = s.substring(i + 1, j);
- msg = s.substring(j + 1).trim();
- } else {
- url = s.substring(i + 1);
- msg = "";
- }
- if (r.isBYE())
- disconnect();
- throw new IMAPReferralException(msg, url);
- }
- }
- }
-
- /**
- * Returns true if the connection has been authenticated,
- * either due to a successful login, or due to a PREAUTH greeting response.
- *
- * @return true if the connection has been authenticated
- */
- public boolean isAuthenticated() {
- return authenticated;
- }
-
- /**
- * Returns true if this is an IMAP4rev1 server
- *
- * @return true if this is an IMAP4rev1 server
- */
- public boolean isREV1() {
- return rev1;
- }
-
- /**
- * Returns whether this Protocol supports non-synchronizing literals.
- *
- * @return true if non-synchronizing literals are supported
- */
- @Override
- protected boolean supportsNonSyncLiterals() {
- return hasCapability("LITERAL+");
- }
-
- /**
- * Read a response from the server.
- *
- * @return the response
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- @Override
- 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.
- *
- * @param c the capability name
- * @return true if the server has the capability
- */
- 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 (it.next().startsWith(c))
- return true;
- }
- return false;
- }
- return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH));
- }
-
- /**
- * Return the map of capabilities returned by the server.
- *
- * @return the Map of capabilities
- * @since JavaMail 1.4.1
- */
- public Map getCapabilities() {
- return capabilities;
- }
-
- /**
- * Does the server support UTF-8?
- *
- * @since JavaMail 1.6.0
- */
- public boolean supportsUtf8() {
- return utf8;
- }
-
- /**
- * Close socket connection.
- *
- * This method just makes the Protocol.disconnect() method
- * public.
- */
- @Override
- public void disconnect() {
- super.disconnect();
- authenticated = false; // just in case
- }
-
- /**
- * The NOOP command.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.1.2"
- */
- public void noop() throws ProtocolException {
- logger.fine("IMAPProtocol noop");
- simpleCommand("NOOP", null);
- }
-
- /**
- * LOGOUT Command.
- *
- * @exception ProtocolException for protocol failures
- * @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.
- *
- * @param u the username
- * @param p the password
- * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
- * @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();
- }
-
- // handle an illegal but not uncommon untagged CAPABILTY response
- handleCapabilityResponse(r);
-
- // dispatch untagged responses
- notifyResponseHandlers(r);
-
- // Handle result of this command
- if (noauthdebug && isTracing())
- logger.fine("LOGIN command result: " + r[r.length-1]);
- handleLoginResult(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
- *
- * @param u the username
- * @param p the password
- * @throws ProtocolException as thrown by {@link Protocol#handleResult}.
- * @see "RFC2060, section 6.2.1"
- */
- public synchronized void authlogin(String u, String p)
- throws ProtocolException {
- List v = new ArrayList<>();
- 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(s.getBytes(StandardCharsets.UTF_8));
- 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;
- // hmm .. unsolicited response here ?!
- } catch (Exception ioex) {
- // convert this into a BYE response
- r = Response.byeResponse(ioex);
- done = true;
- }
- v.add(r);
- }
-
- } finally {
- resumeTracing();
- }
-
- Response[] responses = v.toArray(new Response[v.size()]);
-
- // handle an illegal but not uncommon untagged CAPABILTY response
- handleCapabilityResponse(responses);
-
- /*
- * 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.
- */
- notifyResponseHandlers(responses);
-
- // Handle the final OK, NO, BAD or BYE response
- if (noauthdebug && isTracing())
- logger.fine("AUTHENTICATE LOGIN command result: " + r);
- handleLoginResult(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 {
- List v = new ArrayList<>();
- 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(s.getBytes(StandardCharsets.UTF_8));
- 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;
- // hmm .. unsolicited response here ?!
- } catch (Exception ioex) {
- // convert this into a BYE response
- r = Response.byeResponse(ioex);
- done = true;
- }
- v.add(r);
- }
-
- } finally {
- resumeTracing();
- }
-
- Response[] responses = v.toArray(new Response[v.size()]);
-
- // handle an illegal but not uncommon untagged CAPABILTY response
- handleCapabilityResponse(responses);
-
- /*
- * 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.
- */
- notifyResponseHandlers(responses);
-
- // Handle the final OK, NO, BAD or BYE response
- if (noauthdebug && isTracing())
- logger.fine("AUTHENTICATE PLAIN command result: " + r);
- handleLoginResult(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 {
- List v = new ArrayList<>();
- String tag = null;
- Response r = null;
- boolean done = false;
-
- String type1Msg = null;
- int flags = PropUtil.getIntProperty(props,
- "mail." + name + ".auth.ntlm.flags", 0);
- boolean v2 = PropUtil.getBooleanProperty(props,
- "mail." + name + ".auth.ntlm.v2", true);
- 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, v2);
- first = false;
- } else {
- s = ntlm.generateType3Msg(r.getRest());
- }
-
- os.write(s.getBytes(StandardCharsets.UTF_8));
- 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;
- // hmm .. unsolicited response here ?!
- } catch (Exception ioex) {
- // convert this into a BYE response
- r = Response.byeResponse(ioex);
- done = true;
- }
- v.add(r);
- }
-
- } finally {
- resumeTracing();
- }
-
- Response[] responses = v.toArray(new Response[v.size()]);
-
- // handle an illegal but not uncommon untagged CAPABILTY response
- handleCapabilityResponse(responses);
-
- /*
- * 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.
- */
- notifyResponseHandlers(responses);
-
- // Handle the final OK, NO, BAD or BYE response
- if (noauthdebug && isTracing())
- logger.fine("AUTHENTICATE NTLM command result: " + r);
- handleLoginResult(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=XOAUTH2 authentication scheme.
- * This is based heavly on the {@link #authlogin} method.
- *
- * @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.5.5
- */
- public synchronized void authoauth2(String u, String p)
- throws ProtocolException {
- List v = new ArrayList<>();
- String tag = null;
- Response r = null;
- boolean done = false;
-
- try {
-
- if (noauthdebug && isTracing()) {
- logger.fine("AUTHENTICATE XOAUTH2 command trace suppressed");
- suspendTracing();
- }
-
- try {
- Argument args = new Argument();
- args.writeAtom("XOAUTH2");
- if (hasCapability("SASL-IR")) {
- String resp = "user=" + u + "\001auth=Bearer " + p + "\001\001";
- byte[] ba = BASE64EncoderStream.encode(
- resp.getBytes(StandardCharsets.UTF_8));
- String irs = ASCIIUtility.toString(ba, 0, ba.length);
- args.writeAtom(irs);
- }
- tag = writeCommand("AUTHENTICATE", args);
- } catch (Exception ex) {
- // Convert this into a BYE response
- r = Response.byeResponse(ex);
- done = true;
- }
-
- OutputStream os = getOutputStream(); // stream to IMAP server
-
- while (!done) { // loop till we are done
- try {
- r = readResponse();
- if (r.isContinuation()) {
- // Server challenge ..
- String resp = "user=" + u + "\001auth=Bearer " +
- p + "\001\001";
- byte[] b = BASE64EncoderStream.encode(
- resp.getBytes(StandardCharsets.UTF_8));
- os.write(b); // write out response
- 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;
- // hmm .. unsolicited response here ?!
- } catch (Exception ioex) {
- // convert this into a BYE response
- r = Response.byeResponse(ioex);
- done = true;
- }
- v.add(r);
- }
-
- } finally {
- resumeTracing();
- }
-
- Response[] responses = v.toArray(new Response[v.size()]);
-
- // handle an illegal but not uncommon untagged CAPABILTY response
- handleCapabilityResponse(responses);
-
- /*
- * 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.
- */
- notifyResponseHandlers(responses);
-
- // Handle the final OK, NO, BAD or BYE response
- if (noauthdebug && isTracing())
- logger.fine("AUTHENTICATE XOAUTH2 command result: " + r);
- handleLoginResult(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.
- *
- * @param allowed the SASL mechanisms we're allowed to use
- * @param realm the SASL realm
- * @param authzid the authorization id
- * @param u the username
- * @param p the password
- * @exception ProtocolException for protocol failures
- */
- public void sasllogin(String[] allowed, String realm, String authzid,
- String u, String p) throws ProtocolException {
- boolean useCanonicalHostName = PropUtil.getBooleanProperty(props,
- "mail." + name + ".sasl.usecanonicalhostname", false);
- String serviceHost;
- if (useCanonicalHostName)
- serviceHost = getInetAddress().getCanonicalHostName();
- else
- serviceHost = host;
- 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,
- serviceHost
- });
- } 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 = 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();
- }
-
- /**
- * Handle the result response for a LOGIN or AUTHENTICATE command.
- * Look for IMAP login REFERRAL.
- *
- * @param r the response
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.5.5
- */
- protected void handleLoginResult(Response r) throws ProtocolException {
- if (hasCapability("LOGIN-REFERRALS") &&
- (!r.isOK() || referralException))
- checkReferral(r);
- handleResult(r);
- }
-
- /**
- * PROXYAUTH Command.
- *
- * @param u the PROXYAUTH user name
- * @exception ProtocolException for protocol failures
- * @see "Netscape/iPlanet/SunONE Messaging Server extension"
- */
- public void proxyauth(String u) throws ProtocolException {
- Argument args = new Argument();
- args.writeString(u);
-
- simpleCommand("PROXYAUTH", args);
- proxyAuthUser = u;
- }
-
- /**
- * Get the user name used with the PROXYAUTH command.
- * Returns null if PROXYAUTH was not used.
- *
- * @return the PROXYAUTH user name
- * @since JavaMail 1.5.1
- */
- public String getProxyAuthUser() {
- return proxyAuthUser;
- }
-
- /**
- * UNAUTHENTICATE Command.
- *
- * @exception ProtocolException for protocol failures
- * @see "Netscape/iPlanet/SunONE Messaging Server extension"
- * @since JavaMail 1.5.1
- */
- public void unauthenticate() throws ProtocolException {
- if (!hasCapability("X-UNAUTHENTICATE"))
- throw new BadCommandException("UNAUTHENTICATE not supported");
- simpleCommand("UNAUTHENTICATE", null);
- authenticated = false;
- }
-
- /**
- * ID Command, for Yahoo! Mail IMAP server.
- *
- * @param guid the GUID
- * @exception ProtocolException for protocol failures
- * @deprecated As of JavaMail 1.5.1, replaced by
- * {@link #id(Map) id(Map<String,String>)}
- * @since JavaMail 1.4.4
- */
- @Deprecated
- public void id(String guid) throws ProtocolException {
- // support this for now, but remove it soon
- Map gmap = new HashMap<>();
- gmap.put("GUID", guid);
- id(gmap);
- }
-
- /**
- * STARTTLS Command.
- *
- * @exception ProtocolException for protocol failures
- * @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);
- }
- }
-
- /**
- * COMPRESS Command. Only supports DEFLATE.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC 4978"
- */
- public void compress() throws ProtocolException {
- try {
- super.startCompression("COMPRESS DEFLATE");
- } catch (ProtocolException pex) {
- logger.log(Level.FINE, "COMPRESS 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, "COMPRESS 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("COMPRESS failure", ex);
- }
- }
-
- /**
- * Encode a mailbox name appropriately depending on whether or not
- * the server supports UTF-8, and add the encoded name to the
- * Argument.
- *
- * @param args the arguments
- * @param name the name to encode
- * @since JavaMail 1.6.0
- */
- protected void writeMailboxName(Argument args, String name) {
- if (utf8)
- args.writeString(name, StandardCharsets.UTF_8);
- else
- // encode the mbox as per RFC2060
- args.writeString(BASE64MailboxEncoder.encode(name));
- }
-
- /**
- * SELECT Command.
- *
- * @param mbox the mailbox name
- * @return MailboxInfo if successful
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.1"
- */
- public MailboxInfo select(String mbox) throws ProtocolException {
- return select(mbox, null);
- }
-
- /**
- * SELECT Command with QRESYNC data.
- *
- * @param mbox the mailbox name
- * @param rd the ResyncData
- * @return MailboxInfo if successful
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.1"
- * @see "RFC5162, section 3.1"
- * @since JavaMail 1.5.1
- */
- public MailboxInfo select(String mbox, ResyncData rd)
- throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- if (rd != null) {
- if (rd == ResyncData.CONDSTORE) {
- if (!hasCapability("CONDSTORE"))
- throw new BadCommandException("CONDSTORE not supported");
- args.writeArgument(new Argument().writeAtom("CONDSTORE"));
- } else {
- if (!hasCapability("QRESYNC"))
- throw new BadCommandException("QRESYNC not supported");
- args.writeArgument(resyncArgs(rd));
- }
- }
-
- 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.
- *
- * @param mbox the mailbox name
- * @return MailboxInfo if successful
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.2"
- */
- public MailboxInfo examine(String mbox) throws ProtocolException {
- return examine(mbox, null);
- }
-
- /**
- * EXAMINE Command with QRESYNC data.
- *
- * @param mbox the mailbox name
- * @param rd the ResyncData
- * @return MailboxInfo if successful
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.2"
- * @see "RFC5162, section 3.1"
- * @since JavaMail 1.5.1
- */
- public MailboxInfo examine(String mbox, ResyncData rd)
- throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- if (rd != null) {
- if (rd == ResyncData.CONDSTORE) {
- if (!hasCapability("CONDSTORE"))
- throw new BadCommandException("CONDSTORE not supported");
- args.writeArgument(new Argument().writeAtom("CONDSTORE"));
- } else {
- if (!hasCapability("QRESYNC"))
- throw new BadCommandException("QRESYNC not supported");
- args.writeArgument(resyncArgs(rd));
- }
- }
-
- 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;
- }
-
- /**
- * Generate a QRESYNC argument list based on the ResyncData.
- */
- private static Argument resyncArgs(ResyncData rd) {
- Argument cmd = new Argument();
- cmd.writeAtom("QRESYNC");
- Argument args = new Argument();
- args.writeNumber(rd.getUIDValidity());
- args.writeNumber(rd.getModSeq());
- UIDSet[] uids = Utility.getResyncUIDSet(rd);
- if (uids != null)
- args.writeString(UIDSet.toString(uids));
- cmd.writeArgument(args);
- return cmd;
- }
-
- /**
- * ENABLE Command.
- *
- * @param cap the name of the capability to enable
- * @exception ProtocolException for protocol failures
- * @see "RFC 5161"
- * @since JavaMail 1.5.1
- */
- public void enable(String cap) throws ProtocolException {
- if (!hasCapability("ENABLE"))
- throw new BadCommandException("ENABLE not supported");
- Argument args = new Argument();
- args.writeAtom(cap);
- simpleCommand("ENABLE", args);
- if (enabled == null)
- enabled = new HashSet<>();
- enabled.add(cap.toUpperCase(Locale.ENGLISH));
-
- // update the utf8 flag
- utf8 = isEnabled("UTF8=ACCEPT");
- }
-
- /**
- * Is the capability/extension enabled?
- *
- * @param cap the capability name
- * @return true if enabled
- * @see "RFC 5161"
- * @since JavaMail 1.5.1
- */
- public boolean isEnabled(String cap) {
- if (enabled == null)
- return false;
- else
- return enabled.contains(cap.toUpperCase(Locale.ENGLISH));
- }
-
- /**
- * UNSELECT Command.
- *
- * @exception ProtocolException for protocol failures
- * @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.
- *
- * @param mbox the mailbox
- * @param items the STATUS items to request
- * @return STATUS results
- * @exception ProtocolException for protocol failures
- * @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");
-
- Argument args = new Argument();
- writeMailboxName(args, 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.
- *
- * @param mbox the mailbox to create
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.3"
- */
- public void create(String mbox) throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- simpleCommand("CREATE", args);
- }
-
- /**
- * DELETE Command.
- *
- * @param mbox the mailbox to delete
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.4"
- */
- public void delete(String mbox) throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- simpleCommand("DELETE", args);
- }
-
- /**
- * RENAME Command.
- *
- * @param o old mailbox name
- * @param n new mailbox name
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.5"
- */
- public void rename(String o, String n) throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, o);
- writeMailboxName(args, n);
-
- simpleCommand("RENAME", args);
- }
-
- /**
- * SUBSCRIBE Command.
- *
- * @param mbox the mailbox
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.6"
- */
- public void subscribe(String mbox) throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- simpleCommand("SUBSCRIBE", args);
- }
-
- /**
- * UNSUBSCRIBE Command.
- *
- * @param mbox the mailbox
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.7"
- */
- public void unsubscribe(String mbox) throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- simpleCommand("UNSUBSCRIBE", args);
- }
-
- /**
- * LIST Command.
- *
- * @param ref reference string
- * @param pattern pattern to list
- * @return LIST results
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.3.8"
- */
- public ListInfo[] list(String ref, String pattern)
- throws ProtocolException {
- return doList("LIST", ref, pattern);
- }
-
- /**
- * LSUB Command.
- *
- * @param ref reference string
- * @param pattern pattern to list
- * @return LSUB results
- * @exception ProtocolException for protocol failures
- * @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.
- *
- * @param cmd the list command
- * @param ref the reference string
- * @param pat the pattern
- * @return array of ListInfo results
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.4.6
- */
- protected ListInfo[] doList(String cmd, String ref, String pat)
- throws ProtocolException {
- Argument args = new Argument();
- writeMailboxName(args, ref);
- writeMailboxName(args, pat);
-
- Response[] r = command(cmd, args);
-
- ListInfo[] linfo = null;
- Response response = r[r.length-1];
-
- if (response.isOK()) { // command succesful
- List v = new ArrayList<>(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.add(new ListInfo(ir));
- r[i] = null;
- }
- }
- if (v.size() > 0) {
- linfo = v.toArray(new ListInfo[v.size()]);
- }
- }
-
- // Dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return linfo;
- }
-
- /**
- * APPEND Command.
- *
- * @param mbox the mailbox
- * @param f the message Flags
- * @param d the message date
- * @param data the message data
- * @exception ProtocolException for protocol failures
- * @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.
- *
- * @param mbox the mailbox
- * @param f the message Flags
- * @param d the message date
- * @param data the message data
- * @return APPENDUID data
- * @exception ProtocolException for protocol failures
- * @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 {
- Argument args = new Argument();
- writeMailboxName(args, 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.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.4.1"
- */
- public void check() throws ProtocolException {
- simpleCommand("CHECK", null);
- }
-
- /**
- * CLOSE Command.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.4.2"
- */
- public void close() throws ProtocolException {
- simpleCommand("CLOSE", null);
- }
-
- /**
- * EXPUNGE Command.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2060, section 6.4.3"
- */
- public void expunge() throws ProtocolException {
- simpleCommand("EXPUNGE", null);
- }
-
- /**
- * UID EXPUNGE Command.
- *
- * @param set UIDs to expunge
- * @exception ProtocolException for protocol failures
- * @see "RFC4315, section 2"
- */
- 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.
- *
- * @param msgno the message number
- * @return the BODYSTRUCTURE item
- * @exception ProtocolException for protocol failures
- */
- public BODYSTRUCTURE fetchBodyStructure(int msgno)
- throws ProtocolException {
- Response[] r = fetch(msgno, "BODYSTRUCTURE");
- notifyResponseHandlers(r);
-
- Response response = r[r.length-1];
- if (response.isOK())
- return 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.
- *
- * @param msgno the message number
- * @param section the body section
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- public BODY peekBody(int msgno, String section)
- throws ProtocolException {
- return fetchBody(msgno, section, true);
- }
-
- /**
- * Fetch given BODY section.
- *
- * @param msgno the message number
- * @param section the body section
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- 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 (section == null)
- section = "";
- String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]";
- return fetchSectionBody(msgno, section, body);
- }
-
- /**
- * Partial FETCH of given BODY section, without setting SEEN flag.
- *
- * @param msgno the message number
- * @param section the body section
- * @param start starting byte count
- * @param size number of bytes to fetch
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param msgno the message number
- * @param section the body section
- * @param start starting byte count
- * @param size number of bytes to fetch
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param msgno the message number
- * @param section the body section
- * @param start starting byte count
- * @param size number of bytes to fetch
- * @param ba the buffer into which to read the response
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param msgno the message number
- * @param section the body section
- * @param start starting byte count
- * @param size number of bytes to fetch
- * @param ba the buffer into which to read the response
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- 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
- if (section == null)
- section = "";
- String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]<" +
- String.valueOf(start) + "." +
- String.valueOf(size) + ">";
- return fetchSectionBody(msgno, section, body);
- }
-
- /**
- * Fetch the given body section of the given message, using the
- * body string "body".
- *
- * @param msgno the message number
- * @param section the body section
- * @param body the body string
- * @return the BODY item
- * @exception ProtocolException for protocol failures
- */
- protected BODY fetchSectionBody(int msgno, String section, String body)
- throws ProtocolException {
- Response[] r;
-
- r = fetch(msgno, body);
- notifyResponseHandlers(r);
-
- Response response = r[r.length-1];
- if (response.isOK()) {
- List bl = FetchResponse.getItems(r, msgno, BODY.class);
- if (bl.size() == 1)
- return bl.get(0); // the common case
- if (logger.isLoggable(Level.FINEST))
- logger.finest("got " + bl.size() +
- " BODY responses for section " + section);
- // more then one BODY response, have to find the right one
- for (BODY br : bl) {
- if (logger.isLoggable(Level.FINEST))
- logger.finest("got BODY section " + br.getSection());
- if (br.getSection().equalsIgnoreCase(section))
- return br; // that's the one!
- }
- return null; // couldn't find it
- } 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.
- *
- * @return the buffer to use
- */
- @Override
- 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.
- *
- * @param msgno the message number
- * @param what the item to fetch
- * @return the RFC822DATA item
- * @exception ProtocolException for protocol failures
- */
- 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 FetchResponse.getItem(r, msgno, RFC822DATA.class);
- else if (response.isNO())
- return null;
- else {
- handleResult(response);
- return null;
- }
- }
-
- /**
- * Fetch the FLAGS for the given message.
- *
- * @param msgno the message number
- * @return the Flags
- * @exception ProtocolException for protocol failures
- */
- 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 = 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.
- *
- * @param msgno the message number
- * @return the UID
- * @exception ProtocolException for protocol failures
- */
- 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 FetchResponse.getItem(r, msgno, UID.class);
- else if (response.isNO()) // XXX: Issue NOOP ?
- return null;
- else {
- handleResult(response);
- return null; // NOTREACHED
- }
- }
-
- /**
- * Fetch the IMAP MODSEQ for the given message.
- *
- * @param msgno the message number
- * @return the MODSEQ
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.5.1
- */
- public MODSEQ fetchMODSEQ(int msgno) throws ProtocolException {
- Response[] r = fetch(msgno, "MODSEQ");
-
- // dispatch untagged responses
- notifyResponseHandlers(r);
-
- Response response = r[r.length-1];
- if (response.isOK())
- return FetchResponse.getItem(r, msgno, MODSEQ.class);
- else if (response.isNO()) // XXX: Issue NOOP ?
- return null;
- else {
- handleResult(response);
- return null; // NOTREACHED
- }
- }
-
- /**
- * Get the sequence number for the given UID. Nothing is returned;
- * the FETCH UID response must be handled by the reponse handler,
- * along with any possible EXPUNGE responses, to ensure that the
- * UID is matched with the correct sequence number.
- *
- * @param uid the UID
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.5.3
- */
- public void fetchSequenceNumber(long uid) throws ProtocolException {
- Response[] r = fetch(String.valueOf(uid), "UID", true);
-
- notifyResponseHandlers(r);
- handleResult(r[r.length-1]);
- }
-
- /**
- * Get the sequence numbers for UIDs ranging from start till end.
- * Since the range may be large and sparse, an array of the UIDs actually
- * found is returned. The caller must map these to messages after
- * the FETCH UID responses have been handled by the reponse handler,
- * along with any possible EXPUNGE responses, to ensure that the
- * UIDs are matched with the correct sequence numbers.
- *
- * @param start first UID
- * @param end last UID
- * @return array of sequence numbers
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.5.3
- */
- public long[] fetchSequenceNumbers(long start, long end)
- throws ProtocolException {
- Response[] r = fetch(String.valueOf(start) + ":" +
- (end == UIDFolder.LASTUID ? "*" :
- String.valueOf(end)),
- "UID", true);
-
- UID u;
- List v = new ArrayList<>();
- 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 = fr.getItem(UID.class)) != null)
- v.add(u);
- }
-
- notifyResponseHandlers(r);
- handleResult(r[r.length-1]);
-
- long[] lv = new long[v.size()];
- for (int i = 0; i < v.size(); i++)
- lv[i] = v.get(i).uid;
- return lv;
- }
-
- /**
- * Get the sequence numbers for UIDs specified in the array.
- * Nothing is returned. The caller must map the UIDs to messages after
- * the FETCH UID responses have been handled by the reponse handler,
- * along with any possible EXPUNGE responses, to ensure that the
- * UIDs are matched with the correct sequence numbers.
- *
- * @param uids the UIDs
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.5.3
- */
- public void fetchSequenceNumbers(long[] uids) throws ProtocolException {
- StringBuilder sb = new StringBuilder();
- 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);
-
- notifyResponseHandlers(r);
- handleResult(r[r.length-1]);
- }
-
- /**
- * Get the sequence numbers for messages changed since the given
- * modseq and with UIDs ranging from start till end.
- * Also, prefetch the flags for the returned messages.
- *
- * @param start first UID
- * @param end last UID
- * @param modseq the MODSEQ
- * @return array of sequence numbers
- * @exception ProtocolException for protocol failures
- * @see "RFC 4551"
- * @since JavaMail 1.5.1
- */
- public int[] uidfetchChangedSince(long start, long end, long modseq)
- throws ProtocolException {
- String msgSequence = String.valueOf(start) + ":" +
- (end == UIDFolder.LASTUID ? "*" :
- String.valueOf(end));
- Response[] r = command("UID FETCH " + msgSequence +
- " (FLAGS) (CHANGEDSINCE " + String.valueOf(modseq) + ")", null);
-
- List v = new ArrayList<>();
- for (int i = 0, len = r.length; i < len; i++) {
- if (r[i] == null || !(r[i] instanceof FetchResponse))
- continue;
-
- FetchResponse fr = (FetchResponse)r[i];
- v.add(Integer.valueOf(fr.getNumber()));
- }
-
- notifyResponseHandlers(r);
- handleResult(r[r.length-1]);
-
- // Copy the list into 'matches'
- int vsize = v.size();
- int[] matches = new int[vsize];
- for (int i = 0; i < vsize; i++)
- matches[i] = v.get(i).intValue();
- return matches;
- }
-
- 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.
- *
- * @param msgsets the messages to copy
- * @param mbox the mailbox to copy them to
- * @exception ProtocolException for protocol failures
- */
- public void copy(MessageSet[] msgsets, String mbox)
- throws ProtocolException {
- copyuid(MessageSet.toString(msgsets), mbox, false);
- }
-
- /**
- * COPY command.
- *
- * @param start start message number
- * @param end end message number
- * @param mbox the mailbox to copy them to
- * @exception ProtocolException for protocol failures
- */
- public void copy(int start, int end, String mbox)
- throws ProtocolException {
- copyuid(String.valueOf(start) + ":" + String.valueOf(end),
- mbox, false);
- }
-
- /**
- * COPY command, return uid from COPYUID response code.
- *
- * @param msgsets the messages to copy
- * @param mbox the mailbox to copy them to
- * @return COPYUID response data
- * @exception ProtocolException for protocol failures
- * @see "RFC 4315, section 3"
- */
- public CopyUID copyuid(MessageSet[] msgsets, String mbox)
- throws ProtocolException {
- return copyuid(MessageSet.toString(msgsets), mbox, true);
- }
-
- /**
- * COPY command, return uid from COPYUID response code.
- *
- * @param start start message number
- * @param end end message number
- * @param mbox the mailbox to copy them to
- * @return COPYUID response data
- * @exception ProtocolException for protocol failures
- * @see "RFC 4315, section 3"
- */
- public CopyUID copyuid(int start, int end, String mbox)
- throws ProtocolException {
- return copyuid(String.valueOf(start) + ":" + String.valueOf(end),
- mbox, true);
- }
-
- private CopyUID copyuid(String msgSequence, String mbox, boolean uid)
- throws ProtocolException {
- if (uid && !hasCapability("UIDPLUS"))
- throw new BadCommandException("UIDPLUS not supported");
-
- Argument args = new Argument();
- args.writeAtom(msgSequence);
- writeMailboxName(args, mbox);
-
- Response[] r = command("COPY", args);
-
- // dispatch untagged responses
- notifyResponseHandlers(r);
-
- // Handle result of this command
- handleResult(r[r.length-1]);
-
- if (uid)
- return getCopyUID(r);
- else
- return null;
- }
-
- /**
- * MOVE command.
- *
- * @param msgsets the messages to move
- * @param mbox the mailbox to move them to
- * @exception ProtocolException for protocol failures
- * @see "RFC 6851"
- * @since JavaMail 1.5.4
- */
- public void move(MessageSet[] msgsets, String mbox)
- throws ProtocolException {
- moveuid(MessageSet.toString(msgsets), mbox, false);
- }
-
- /**
- * MOVE command.
- *
- * @param start start message number
- * @param end end message number
- * @param mbox the mailbox to move them to
- * @exception ProtocolException for protocol failures
- * @see "RFC 6851"
- * @since JavaMail 1.5.4
- */
- public void move(int start, int end, String mbox)
- throws ProtocolException {
- moveuid(String.valueOf(start) + ":" + String.valueOf(end),
- mbox, false);
- }
-
- /**
- * MOVE Command, return uid from COPYUID response code.
- *
- * @param msgsets the messages to move
- * @param mbox the mailbox to move them to
- * @return COPYUID response data
- * @exception ProtocolException for protocol failures
- * @see "RFC 6851"
- * @see "RFC 4315, section 3"
- * @since JavaMail 1.5.4
- */
- public CopyUID moveuid(MessageSet[] msgsets, String mbox)
- throws ProtocolException {
- return moveuid(MessageSet.toString(msgsets), mbox, true);
- }
-
- /**
- * MOVE Command, return uid from COPYUID response code.
- *
- * @param start start message number
- * @param end end message number
- * @param mbox the mailbox to move them to
- * @return COPYUID response data
- * @exception ProtocolException for protocol failures
- * @see "RFC 6851"
- * @see "RFC 4315, section 3"
- * @since JavaMail 1.5.4
- */
- public CopyUID moveuid(int start, int end, String mbox)
- throws ProtocolException {
- return moveuid(String.valueOf(start) + ":" + String.valueOf(end),
- mbox, true);
- }
-
- /**
- * MOVE Command, return uid from COPYUID response code.
- *
- * @see "RFC 6851"
- * @see "RFC 4315, section 3"
- * @since JavaMail 1.5.4
- */
- private CopyUID moveuid(String msgSequence, String mbox, boolean uid)
- throws ProtocolException {
- if (!hasCapability("MOVE"))
- throw new BadCommandException("MOVE not supported");
- if (uid && !hasCapability("UIDPLUS"))
- throw new BadCommandException("UIDPLUS not supported");
-
- Argument args = new Argument();
- args.writeAtom(msgSequence);
- writeMailboxName(args, mbox);
-
- Response[] r = command("MOVE", args);
-
- // dispatch untagged responses
- notifyResponseHandlers(r);
-
- // Handle result of this command
- handleResult(r[r.length-1]);
-
- if (uid)
- return getCopyUID(r);
- else
- return null;
- }
-
- /**
- * If the response contains a COPYUID response code, extract
- * it and return a CopyUID object with the information.
- *
- * @param rr the responses to examine
- * @return the COPYUID response code data, or null if not found
- * @since JavaMail 1.5.4
- */
- protected CopyUID getCopyUID(Response[] rr) {
- // most likely in the last response, so start there and work backward
- for (int i = rr.length - 1; i >= 0; i--) {
- Response r = rr[i];
- if (r == null || !r.isOK())
- continue;
- byte b;
- while ((b = r.readByte()) > 0 && b != (byte)'[')
- ;
- if (b == 0)
- continue;
- String s;
- s = r.readAtom();
- if (!s.equalsIgnoreCase("COPYUID"))
- continue;
-
- // XXX - need to merge more than one response for MOVE?
- long uidvalidity = r.readLong();
- String src = r.readAtom();
- String dst = r.readAtom();
- return new CopyUID(uidvalidity,
- UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst));
- }
- return null;
- }
-
- 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.
- *
- * @param msg the message number
- * @param flags the flags
- * @param set true to set, false to clear
- * @exception ProtocolException for protocol failures
- */
- 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.
- *
- * @param flags the flags
- * @return the IMAP flag_list
- * @since JavaMail 1.5.4
- */
- protected String createFlagList(Flags flags) {
- StringBuilder sb = new StringBuilder("("); // 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.
- * @exception ProtocolException for protocol failures
- * @exception SearchException for search failures
- */
- 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.
- * @exception ProtocolException for protocol failures
- * @exception SearchException for search failures
- */
- 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,
- // or if utf8 support has been enabled (in which case CHARSET
- // is not allowed; see RFC 6855, section 3, last paragraph)
- if (supportsUtf8() || SearchSequence.isAscii(term)) {
- try {
- return issueSearch(msgSequence, term, null);
- } catch (IOException ioex) { /* will not happen */ }
- }
-
- /*
- * The search "text" terms do contain non-ASCII chars and utf8
- * support has not been enabled. 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
- List v = new ArrayList<>();
- 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.add(Integer.valueOf(num));
- r[i] = null;
- }
- }
-
- // Copy the list into 'matches'
- int vsize = v.size();
- matches = new int[vsize];
- for (int i = 0; i < vsize; i++)
- matches[i] = v.get(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.
- *
- * @return the SearchSequence
- * @since JavaMail 1.4.6
- */
- protected SearchSequence getSearchSequence() {
- if (searchSequence == null)
- searchSequence = new SearchSequence(this);
- 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.
- * @exception ProtocolException for protocol failures
- * @exception SearchException for search failures
- *
- * @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
- List v = new ArrayList<>();
- 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.add(Integer.valueOf(num));
- r[i] = null;
- }
- }
-
- // Copy the list into 'matches'
- int vsize = v.size();
- matches = new int[vsize];
- for (int i = 0; i < vsize; i++)
- matches[i] = v.get(i).intValue();
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return matches;
- }
-
- /**
- * NAMESPACE Command.
- *
- * @return the namespaces
- * @exception ProtocolException for protocol failures
- * @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.
- *
- * @param mbox the mailbox
- * @return array of Quota objects
- * @exception ProtocolException for protocol failures
- * @see "RFC2087"
- */
- public Quota[] getQuotaRoot(String mbox) throws ProtocolException {
- if (!hasCapability("QUOTA"))
- throw new BadCommandException("GETQUOTAROOT not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- Response[] r = command("GETQUOTAROOT", args);
-
- Response response = r[r.length-1];
-
- Map tab = new HashMap<>();
-
- // 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 = tab.get(quota.quotaRoot);
- if (q != null && q.resources != null) {
- // merge resources
- int newl = q.resources.length + quota.resources.length;
- Quota.Resource[] newr = new Quota.Resource[newl];
- System.arraycopy(q.resources, 0, newr, 0,
- q.resources.length);
- System.arraycopy(quota.resources, 0,
- newr, q.resources.length, quota.resources.length);
- quota.resources = newr;
- }
- tab.put(quota.quotaRoot, quota);
- r[i] = null;
- }
- }
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
-
- return tab.values().toArray(new Quota[tab.size()]);
- }
-
- /**
- * GETQUOTA Command.
- *
- * Returns an array of Quota objects, representing the quotas
- * for this quotaroot.
- *
- * @param root the quotaroot
- * @return the quotas
- * @exception ProtocolException for protocol failures
- * @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); // XXX - could be UTF-8?
-
- Response[] r = command("GETQUOTA", args);
-
- Quota quota = null;
- List v = new ArrayList<>();
- 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.add(quota);
- r[i] = null;
- }
- }
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return v.toArray(new Quota[v.size()]);
- }
-
- /**
- * SETQUOTA Command.
- *
- * Set the indicated quota on the corresponding quotaroot.
- *
- * @param quota the quota to set
- * @exception ProtocolException for protocol failures
- * @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); // XXX - could be UTF-8?
- 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;
- List v = new ArrayList();
-
- // 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.add(quota);
- r[i] = null;
- }
- }
- }
- */
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- /*
- return v.toArray(new Quota[v.size()]);
- */
- }
-
- /**
- * 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");
-
- List v = new ArrayList<>();
- while (!r.isNextNonSpace(')')) {
- // 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.add(res);
- }
- }
- q.resources = v.toArray(new Quota.Resource[v.size()]);
- return q;
- }
-
-
- /**
- * SETACL Command.
- *
- * @param mbox the mailbox
- * @param modifier the ACL modifier
- * @param acl the ACL
- * @exception ProtocolException for protocol failures
- * @see "RFC2086"
- */
- public void setACL(String mbox, char modifier, ACL acl)
- throws ProtocolException {
- if (!hasCapability("ACL"))
- throw new BadCommandException("ACL not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, 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.
- *
- * @param mbox the mailbox
- * @param user the user
- * @exception ProtocolException for protocol failures
- * @see "RFC2086"
- */
- public void deleteACL(String mbox, String user) throws ProtocolException {
- if (!hasCapability("ACL"))
- throw new BadCommandException("ACL not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, mbox);
- args.writeString(user); // XXX - could be UTF-8?
-
- Response[] r = command("DELETEACL", args);
- Response response = r[r.length-1];
-
- // dispatch untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- }
-
- /**
- * GETACL Command.
- *
- * @param mbox the mailbox
- * @return the ACL array
- * @exception ProtocolException for protocol failures
- * @see "RFC2086"
- */
- public ACL[] getACL(String mbox) throws ProtocolException {
- if (!hasCapability("ACL"))
- throw new BadCommandException("ACL not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, mbox);
-
- Response[] r = command("GETACL", args);
- Response response = r[r.length-1];
-
- // Grab all ACL responses
- List v = new ArrayList<>();
- 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.add(acl);
- }
- r[i] = null;
- }
- }
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return v.toArray(new ACL[v.size()]);
- }
-
- /**
- * LISTRIGHTS Command.
- *
- * @param mbox the mailbox
- * @param user the user rights to return
- * @return the rights array
- * @exception ProtocolException for protocol failures
- * @see "RFC2086"
- */
- public Rights[] listRights(String mbox, String user)
- throws ProtocolException {
- if (!hasCapability("ACL"))
- throw new BadCommandException("ACL not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, mbox);
- args.writeString(user); // XXX - could be UTF-8?
-
- Response[] r = command("LISTRIGHTS", args);
- Response response = r[r.length-1];
-
- // Grab LISTRIGHTS response
- List v = new ArrayList<>();
- 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.add(new Rights(rights));
- r[i] = null;
- }
- }
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return v.toArray(new Rights[v.size()]);
- }
-
- /**
- * MYRIGHTS Command.
- *
- * @param mbox the mailbox
- * @return the rights
- * @exception ProtocolException for protocol failures
- * @see "RFC2086"
- */
- public Rights myRights(String mbox) throws ProtocolException {
- if (!hasCapability("ACL"))
- throw new BadCommandException("ACL not supported");
-
- Argument args = new Argument();
- writeMailboxName(args, 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.
- *
- * @exception ProtocolException for protocol failures
- * @see "RFC2177"
- * @since JavaMail 1.4.1
- */
- public synchronized void idleStart() throws ProtocolException {
- if (!hasCapability("IDLE"))
- throw new BadCommandException("IDLE not supported");
-
- List v = new ArrayList<>();
- boolean done = false;
- Response r = null;
-
- // write the command
- try {
- idleTag = writeCommand("IDLE", null);
- } catch (LiteralException lex) {
- v.add(lex.getResponse());
- done = true;
- } catch (Exception ex) {
- // Convert this into a BYE response
- v.add(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.add(r);
-
- if (r.isContinuation() || r.isBYE())
- done = true;
- }
-
- Response[] responses = v.toArray(new Response[v.size()]);
- 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.
- *
- * @return the response
- * @since JavaMail 1.4.1
- */
- public synchronized Response readIdleResponse() {
- if (idleTag == null)
- return null; // IDLE not in progress
- Response r = null;
- try {
- r = readResponse();
- } 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.
- *
- * @param r the response
- * @return true if IDLE is done
- * @exception ProtocolException for protocol failures
- * @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() {
- OutputStream os = getOutputStream();
- try {
- os.write(DONE);
- os.flush();
- } catch (Exception ex) {
- // nothing to do, hope to detect it again later
- logger.log(Level.FINEST, "Exception aborting IDLE", ex);
- }
- }
-
- /**
- * ID Command.
- *
- * @param clientParams map of names and values
- * @return map of names and values from server
- * @exception ProtocolException for protocol failures
- * @see "RFC 2971"
- * @since JavaMail 1.5.1
- */
- public Map id(Map clientParams)
- throws ProtocolException {
- if (!hasCapability("ID"))
- throw new BadCommandException("ID not supported");
-
- Response[] r = command("ID", ID.getArgumentList(clientParams));
-
- ID id = null;
- Response response = r[r.length-1];
-
- // Grab ID 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("ID")) {
- if (id == null)
- id = new ID(ir);
- r[i] = null;
- }
- }
- }
-
- // dispatch remaining untagged responses
- notifyResponseHandlers(r);
- handleResult(response);
- return id == null ? null : id.getServerParams();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/IMAPReferralException.java b/app/src/main/java/com/sun/mail/imap/protocol/IMAPReferralException.java
deleted file mode 100644
index 82f1ea39d1..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/IMAPReferralException.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import com.sun.mail.iap.ProtocolException;
-
-/**
- * A ProtocolException that includes IMAP login referral information.
- *
- * @since JavaMail 1.5.5
- */
-
-public class IMAPReferralException extends ProtocolException {
-
- private String url;
-
- private static final long serialVersionUID = 2578770669364251968L;
-
- /**
- * Constructs an IMAPReferralException with the specified detail message.
- * and URL.
- *
- * @param s the detail message
- * @param url the URL
- */
- public IMAPReferralException(String s, String url) {
- super(s);
- this.url = url;
- }
-
- /**
- * Return the IMAP URL in the referral.
- *
- * @return the IMAP URL
- */
- public String getUrl() {
- return url;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java b/app/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java
deleted file mode 100644
index 4e6be4e1b3..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/IMAPResponse.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.io.*;
-import java.util.*;
-import com.sun.mail.util.ASCIIUtility;
-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.
- *
- * @param r the IMAPResponse to copy
- */
- public IMAPResponse(IMAPResponse r) {
- super((Response)r);
- key = r.key;
- number = r.number;
- }
-
- /**
- * For testing.
- *
- * @param r the response string
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- */
- public IMAPResponse(String r) throws IOException, ProtocolException {
- this(r, true);
- }
-
- /**
- * For testing.
- *
- * @param r the response string
- * @param utf8 UTF-8 allowed?
- * @exception IOException for I/O errors
- * @exception ProtocolException for protocol failures
- * @since JavaMail 1.6.0
- */
- public IMAPResponse(String r, boolean utf8)
- throws IOException, ProtocolException {
- super(r, utf8);
- 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. Each item is expected to be an atom, possibly preceeded
- * by a backslash, but we aren't that strict; we just look for strings
- * separated by spaces and terminated by a right paren. We assume items
- * are always ASCII.
- *
- * @return the list items as a String array
- */
- public String[] readSimpleList() {
- skipSpaces();
-
- if (buffer[index] != '(') // not what we expected
- return null;
- index++; // skip '('
-
- List v = new ArrayList<>();
- int start;
- for (start = index; buffer[index] != ')'; index++) {
- if (buffer[index] == ' ') { // got one item
- v.add(ASCIIUtility.toString(buffer, start, index));
- start = index+1; // index gets incremented at the top
- }
- }
- if (index > start) // get the last item
- v.add(ASCIIUtility.toString(buffer, start, index));
- index++; // skip ')'
-
- int size = v.size();
- if (size > 0)
- return v.toArray(new String[size]);
- 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/app/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java b/app/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java
deleted file mode 100644
index 7f432ff51c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/INTERNALDATE.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.*;
-
-
-/**
- * An INTERNALDATE FETCH item.
- *
- * @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 final MailDateFormat mailDateFormat = new MailDateFormat();
-
- /**
- * Constructor.
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- 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 {
- synchronized (mailDateFormat) {
- 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
- *
- * @param d the Date
- * @return 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/app/src/main/java/com/sun/mail/imap/protocol/Item.java b/app/src/main/java/com/sun/mail/imap/protocol/Item.java
deleted file mode 100644
index 8d604ab939..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/Item.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/imap/protocol/ListInfo.java b/app/src/main/java/com/sun/mail/imap/protocol/ListInfo.java
deleted file mode 100644
index 0db1f028e8..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/ListInfo.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-
-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();
-
- List v = new ArrayList<>(); // 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.add(s[i]);
- }
- }
- attrs = v.toArray(new String[v.size()]);
-
- 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();
-
- if (!r.supportsUtf8())
- // decode the name (using RFC2060's modified UTF7)
- name = BASE64MailboxDecoder.decode(name);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java b/app/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java
deleted file mode 100644
index f800569309..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import com.sun.mail.iap.*;
-
-/**
- * This class represents the MODSEQ data item.
- *
- * @since JavaMail 1.5.1
- * @author Bill Shannon
- */
-
-public class MODSEQ implements Item {
-
- static final char[] name = {'M','O','D','S','E','Q'};
- public int seqnum;
-
- public long modseq;
-
- /**
- * Constructor.
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- public MODSEQ(FetchResponse r) throws ParsingException {
- seqnum = r.getNumber();
- r.skipSpaces();
-
- if (r.readByte() != '(')
- throw new ParsingException("MODSEQ parse error");
-
- modseq = r.readLong();
-
- if (!r.isNextNonSpace(')'))
- throw new ParsingException("MODSEQ parse error");
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java b/app/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java
deleted file mode 100644
index ccff0b6964..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/MailboxInfo.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import javax.mail.Flags;
-
-import com.sun.mail.iap.*;
-
-/**
- * Information collected when opening a mailbox.
- *
- * @author John Mani
- * @author Bill Shannon
- */
-
-public class MailboxInfo {
- /** The available flags. */
- public Flags availableFlags = null;
- /** The permanent flags. */
- public Flags permanentFlags = null;
- /** The total number of messages. */
- public int total = -1;
- /** The number of recent messages. */
- public int recent = -1;
- /** The first unseen message. */
- public int first = -1;
- /** The UIDVALIDITY. */
- public long uidvalidity = -1;
- /** The next UID value to be assigned. */
- public long uidnext = -1;
- /** UIDs are not sticky. */
- public boolean uidNotSticky = false; // RFC 4315
- /** The highest MODSEQ value. */
- public long highestmodseq = -1; // RFC 4551 - CONDSTORE
- /** Folder.READ_WRITE or Folder.READ_ONLY, set by IMAPProtocol. */
- public int mode;
- /** VANISHED or FETCH responses received while opening the mailbox. */
- public List responses;
-
- /**
- * Collect the information about this mailbox from the
- * responses to a SELECT or EXAMINE.
- *
- * @param r the responses
- * @exception ParsingException for errors parsing the responses
- */
- 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.keyEquals("VANISHED")) {
- if (responses == null)
- responses = new ArrayList<>();
- responses.add(ir);
- r[i] = null; // remove this response
- } else if (ir.keyEquals("FETCH")) {
- if (responses == null)
- responses = new ArrayList<>();
- responses.add(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]
- * * OK [HIGHESTMODSEQ 103]
- */
- 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 if (s.equalsIgnoreCase("HIGHESTMODSEQ"))
- highestmodseq = ir.readLong();
- else
- handled = false; // possibly an ALERT
-
- if (handled)
- r[i] = null; // remove this response
- else
- ir.reset(); // so ALERT can be read
- } else if (ir.isUnTagged() && ir.isNO()) {
- /*
- * should be one of:
- * * NO [UIDNOTSTICKY]
- */
- ir.skipSpaces();
-
- if (ir.readByte() != '[') { // huh ???
- ir.reset();
- continue;
- }
-
- boolean handled = true;
- String s = ir.readAtom();
- if (s.equalsIgnoreCase("UIDNOTSTICKY"))
- uidNotSticky = true;
- 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/app/src/main/java/com/sun/mail/imap/protocol/MessageSet.java b/app/src/main/java/com/sun/mail/imap/protocol/MessageSet.java
deleted file mode 100644
index a7e6cc6b5b..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/MessageSet.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * 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
- *
- * @return how many messages in this MessageSet
- */
- public int size() {
- return end - start + 1;
- }
-
- /**
- * Convert an array of integers into an array of MessageSets
- *
- * @param msgs the messages
- * @return array of MessageSet objects
- */
- public static MessageSet[] createMessageSets(int[] msgs) {
- List v = new ArrayList<>();
- 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.add(ms);
- i = j-1; // i gets incremented @ top of the loop
- }
- return v.toArray(new MessageSet[v.size()]);
- }
-
- /**
- * Convert an array of MessageSets into an IMAP sequence range
- *
- * @param msgsets the MessageSets
- * @return IMAP sequence string
- */
- public static String toString(MessageSet[] msgsets) {
- if (msgsets == null || msgsets.length == 0) // Empty msgset
- return null;
-
- int i = 0; // msgset index
- StringBuilder s = new StringBuilder();
- 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/app/src/main/java/com/sun/mail/imap/protocol/Namespaces.java b/app/src/main/java/com/sun/mail/imap/protocol/Namespaces.java
deleted file mode 100644
index df54e67e2c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/Namespaces.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the Response to parse
- * @exception ProtocolException for any protocol errors
- */
- public Namespace(Response r) throws ProtocolException {
- // Namespace_Element = "(" string SP (<"> QUOTED_CHAR <"> / nil)
- // *(Namespace_Response_Extension) ")"
- if (!r.isNextNonSpace('('))
- throw new ProtocolException(
- "Missing '(' at start of Namespace");
- // first, the prefix
- prefix = r.readString();
- if (!r.supportsUtf8())
- prefix = BASE64MailboxDecoder.decode(prefix);
- 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.isNextNonSpace(')'))
- return;
-
- // otherwise, must be a Namespace_Response_Extension
- // Namespace_Response_Extension = SP string SP
- // "(" string *(SP string) ")"
- r.readString();
- r.skipSpaces();
- r.readStringList();
- if (!r.isNextNonSpace(')'))
- 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.
- *
- * @param r the Response to parse
- * @throws ProtocolException for any protocol errors
- */
- 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 {
- // Namespace = nil / "(" 1*( Namespace_Element) ")"
- if (r.isNextNonSpace('(')) {
- List v = new ArrayList<>();
- do {
- Namespace ns = new Namespace(r);
- v.add(ns);
- } while (!r.isNextNonSpace(')'));
- return v.toArray(new Namespace[v.size()]);
- } 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/app/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java b/app/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java
deleted file mode 100644
index bda754f61d..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.io.ByteArrayInputStream;
-import com.sun.mail.iap.*;
-import com.sun.mail.util.ASCIIUtility;
-
-/**
- * The RFC822 response data item.
- *
- * @author John Mani
- * @author Bill Shannon
- */
-
-public class RFC822DATA implements Item {
-
- static final char[] name = {'R','F','C','8','2','2'};
- private final int msgno;
- private final ByteArray data;
- private final boolean isHeader;
-
- /**
- * Constructor, header flag is false.
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- public RFC822DATA(FetchResponse r) throws ParsingException {
- this(r, false);
- }
-
- /**
- * Constructor, specifying header flag.
- *
- * @param r the FetchResponse
- * @param isHeader just header information?
- * @exception ParsingException for parsing failures
- */
- public RFC822DATA(FetchResponse r, boolean isHeader)
- throws ParsingException {
- this.isHeader = isHeader;
- 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;
- }
-
- public boolean isHeader() {
- return isHeader;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java b/app/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java
deleted file mode 100644
index 59118f1583..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/RFC822SIZE.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import com.sun.mail.iap.*;
-
-/**
- * An RFC822SIZE FETCH item.
- *
- * @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 long size;
-
- /**
- * Constructor.
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- public RFC822SIZE(FetchResponse r) throws ParsingException {
- msgno = r.getNumber();
- r.skipSpaces();
- size = r.readLong();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java b/app/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java
deleted file mode 100644
index 4c31c76715..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/SaslAuthenticator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java b/app/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java
deleted file mode 100644
index c1388373ca..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/SearchSequence.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.*;
-import com.sun.mail.imap.OlderTerm;
-import com.sun.mail.imap.YoungerTerm;
-import com.sun.mail.imap.ModifiedSinceTerm;
-
-/**
- * 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
- * @author Bill Shannon
- */
-public class SearchSequence {
-
- private IMAPProtocol protocol; // for hasCapability checks; may be null
-
- /**
- * Create a SearchSequence for this IMAPProtocol.
- *
- * @param p the IMAPProtocol object for the server
- * @since JavaMail 1.6.0
- */
- public SearchSequence(IMAPProtocol p) {
- protocol = p;
- }
-
- /**
- * Create a SearchSequence.
- */
- @Deprecated
- public SearchSequence() {
- }
-
- /**
- * Generate the IMAP search sequence for the given search expression.
- *
- * @param term the search term
- * @param charset charset for the search
- * @return the SEARCH Argument
- * @exception SearchException for failures
- * @exception IOException for I/O errors
- */
- 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 OlderTerm) // RFC 5032 OLDER
- return older((OlderTerm)term);
- else if (term instanceof YoungerTerm) // RFC 5032 YOUNGER
- return younger((YoungerTerm)term);
- else if (term instanceof MessageIDTerm) // MessageID
- return messageid((MessageIDTerm)term, charset);
- else if (term instanceof ModifiedSinceTerm) // RFC 4551 MODSEQ
- return modifiedSince((ModifiedSinceTerm)term);
- else
- throw new SearchException("Search too complex");
- }
-
- /**
- * Check if the "text" terms in the given SearchTerm contain
- * non US-ASCII characters.
- *
- * @param term the search term
- * @return true if only ASCII
- */
- public static boolean isAscii(SearchTerm term) {
- if (term instanceof AndTerm)
- return isAscii(((AndTerm)term).getTerms());
- else if (term instanceof OrTerm)
- return isAscii(((OrTerm)term).getTerms());
- 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;
- }
-
- /**
- * Check if any of the "text" terms in the given SearchTerms contain
- * non US-ASCII characters.
- *
- * @param terms the search terms
- * @return true if only ASCII
- */
- public static boolean isAscii(SearchTerm[] terms) {
- for (int i = 0; i < terms.length; i++)
- if (!isAscii(terms[i])) // outta here !
- return false;
- return true;
- }
-
- /**
- * Does this string contain only ASCII characters?
- *
- * @param s the string
- * @return true if only ASCII
- */
- 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 ...
-
- // NOTE: The built-in IMAP date comparisons are equivalent to
- // "<" (BEFORE), "=" (ON), and ">=" (SINCE)!!!
- // There is no built-in greater-than comparison!
-
- /**
- * 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) {
- StringBuilder s = new StringBuilder();
-
- 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("NOT SENTON " + date + " SENTSINCE " + date);
- break;
- case ComparisonTerm.EQ:
- result.writeAtom("SENTON " + date);
- break;
- case ComparisonTerm.LT:
- result.writeAtom("SENTBEFORE " + date);
- break;
- case ComparisonTerm.GE:
- result.writeAtom("SENTSINCE " + 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("NOT ON " + date + " SINCE " + date);
- break;
- case ComparisonTerm.EQ:
- result.writeAtom("ON " + date);
- break;
- case ComparisonTerm.LT:
- result.writeAtom("BEFORE " + date);
- break;
- case ComparisonTerm.GE:
- result.writeAtom("SINCE " + 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;
- }
-
- /**
- * Generate argument for OlderTerm.
- *
- * @param term the search term
- * @return the SEARCH Argument
- * @exception SearchException for failures
- * @since JavaMail 1.5.1
- */
- protected Argument older(OlderTerm term) throws SearchException {
- if (protocol != null && !protocol.hasCapability("WITHIN"))
- throw new SearchException("Server doesn't support OLDER searches");
- Argument result = new Argument();
- result.writeAtom("OLDER");
- result.writeNumber(term.getInterval());
- return result;
- }
-
- /**
- * Generate argument for YoungerTerm.
- *
- * @param term the search term
- * @return the SEARCH Argument
- * @exception SearchException for failures
- * @since JavaMail 1.5.1
- */
- protected Argument younger(YoungerTerm term) throws SearchException {
- if (protocol != null && !protocol.hasCapability("WITHIN"))
- throw new SearchException("Server doesn't support YOUNGER searches");
- Argument result = new Argument();
- result.writeAtom("YOUNGER");
- result.writeNumber(term.getInterval());
- return result;
- }
-
- /**
- * Generate argument for ModifiedSinceTerm.
- *
- * @param term the search term
- * @return the SEARCH Argument
- * @exception SearchException for failures
- * @since JavaMail 1.5.1
- */
- protected Argument modifiedSince(ModifiedSinceTerm term)
- throws SearchException {
- if (protocol != null && !protocol.hasCapability("CONDSTORE"))
- throw new SearchException("Server doesn't support MODSEQ searches");
- Argument result = new Argument();
- result.writeAtom("MODSEQ");
- result.writeNumber(term.getModSeq());
- return result;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/Status.java b/app/src/main/java/com/sun/mail/imap/protocol/Status.java
deleted file mode 100644
index 76d29c358b..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/Status.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Locale;
-
-import com.sun.mail.iap.*;
-
-/**
- * STATUS response.
- *
- * @author John Mani
- * @author Bill Shannon
- */
-
-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;
- public long highestmodseq = -1;
- public Map items; // any unknown items
-
- static final String[] standardItems =
- { "MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY" };
-
- public Status(Response r) throws ParsingException {
- // mailbox := astring
- mbox = r.readAtomString();
- if (!r.supportsUtf8())
- mbox = BASE64MailboxDecoder.decode(mbox);
-
- // Workaround buggy IMAP servers that don't quote folder names
- // with spaces.
- final StringBuilder buffer = new StringBuilder();
- 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 == null)
- throw new ParsingException("parse error in STATUS");
- 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();
- else if (attr.equalsIgnoreCase("HIGHESTMODSEQ"))
- highestmodseq = r.readLong();
- else {
- if (items == null)
- items = new HashMap<>();
- items.put(attr.toUpperCase(Locale.ENGLISH),
- Long.valueOf(r.readLong()));
- }
- } while (!r.isNextNonSpace(')'));
- }
-
- /**
- * Get the value for the STATUS item.
- *
- * @param item the STATUS item
- * @return the value
- * @since JavaMail 1.5.2
- */
- public long getItem(String item) {
- item = item.toUpperCase(Locale.ENGLISH);
- Long v;
- long ret = -1;
- if (items != null && (v = items.get(item)) != null)
- ret = v.longValue();
- else if (item.equals("MESSAGES"))
- ret = total;
- else if (item.equals("RECENT"))
- ret = recent;
- else if (item.equals("UIDNEXT"))
- ret = uidnext;
- else if (item.equals("UIDVALIDITY"))
- ret = uidvalidity;
- else if (item.equals("UNSEEN"))
- ret = unseen;
- else if (item.equals("HIGHESTMODSEQ"))
- ret = highestmodseq;
- return ret;
- }
-
- 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;
- if (s2.highestmodseq != -1)
- s1.highestmodseq = s2.highestmodseq;
- if (s1.items == null)
- s1.items = s2.items;
- else if (s2.items != null)
- s1.items.putAll(s2.items);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/UID.java b/app/src/main/java/com/sun/mail/imap/protocol/UID.java
deleted file mode 100644
index 58082c2104..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/UID.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param r the FetchResponse
- * @exception ParsingException for parsing failures
- */
- public UID(FetchResponse r) throws ParsingException {
- seqnum = r.getNumber();
- r.skipSpaces();
- uid = r.readLong();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/UIDSet.java b/app/src/main/java/com/sun/mail/imap/protocol/UIDSet.java
deleted file mode 100644
index 222858155c..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/UIDSet.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.imap.protocol;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.StringTokenizer;
-
-/**
- * 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
- *
- * @return the number of elements
- */
- public long size() {
- return end - start + 1;
- }
-
- /**
- * Convert an array of longs into an array of UIDSets
- *
- * @param uids the UIDs
- * @return array of UIDSet objects
- */
- public static UIDSet[] createUIDSets(long[] uids) {
- if (uids == null)
- return null;
- List v = new ArrayList<>();
- int i,j;
-
- for (i=0; i < uids.length; i++) {
- UIDSet ms = new UIDSet();
- ms.start = uids[i];
-
- // Look for contiguous elements
- for (j=i+1; j < uids.length; j++) {
- if (uids[j] != uids[j-1] +1)
- break;
- }
- ms.end = uids[j-1];
- v.add(ms);
- i = j-1; // i gets incremented @ top of the loop
- }
- UIDSet[] uidset = new UIDSet[v.size()];
- return v.toArray(uidset);
- }
-
- /**
- * Parse a string in IMAP UID range format.
- *
- * @param uids UID string
- * @return array of UIDSet objects
- * @since JavaMail 1.5.1
- */
- public static UIDSet[] parseUIDSets(String uids) {
- if (uids == null)
- return null;
- List v = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(uids, ",:", true);
- long start = -1;
- UIDSet cur = null;
- try {
- while(st.hasMoreTokens()) {
- String s = st.nextToken();
- if (s.equals(",")) {
- if (cur != null)
- v.add(cur);
- cur = null;
- } else if (s.equals(":")) {
- // nothing to do, wait for next number
- } else { // better be a number
- long n = Long.parseLong(s);
- if (cur != null)
- cur.end = n;
- else
- cur = new UIDSet(n, n);
- }
- }
- } catch (NumberFormatException nex) {
- // give up and return what we have so far
- }
- if (cur != null)
- v.add(cur);
- UIDSet[] uidset = new UIDSet[v.size()];
- return v.toArray(uidset);
- }
-
- /**
- * Convert an array of UIDSets into an IMAP sequence range.
- *
- * @param uidset the UIDSets
- * @return the IMAP sequence string
- */
- public static String toString(UIDSet[] uidset) {
- if (uidset == null)
- return null;
- if (uidset.length == 0) // Empty uidset
- return "";
-
- int i = 0; // uidset index
- StringBuilder s = new StringBuilder();
- int size = uidset.length;
- long start, end;
-
- for (;;) {
- start = uidset[i].start;
- end = uidset[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();
- }
-
- /**
- * Convert an array of UIDSets into a array of long UIDs.
- *
- * @param uidset the UIDSets
- * @return arrray of UIDs
- * @since JavaMail 1.5.1
- */
- public static long[] toArray(UIDSet[] uidset) {
- //return toArray(uidset, -1);
- if (uidset == null)
- return null;
- long[] uids = new long[(int)UIDSet.size(uidset)];
- int i = 0;
- for (UIDSet u : uidset) {
- for (long n = u.start; n <= u.end; n++)
- uids[i++] = n;
- }
- return uids;
- }
-
- /**
- * Convert an array of UIDSets into a array of long UIDs.
- * Don't include any UIDs larger than uidmax.
- *
- * @param uidset the UIDSets
- * @param uidmax maximum UID
- * @return arrray of UIDs
- * @since JavaMail 1.5.1
- */
- public static long[] toArray(UIDSet[] uidset, long uidmax) {
- if (uidset == null)
- return null;
- long[] uids = new long[(int)UIDSet.size(uidset, uidmax)];
- int i = 0;
- for (UIDSet u : uidset) {
- for (long n = u.start; n <= u.end; n++) {
- if (uidmax >= 0 && n > uidmax)
- break;
- uids[i++] = n;
- }
- }
- return uids;
- }
-
- /**
- * Count the total number of elements in an array of UIDSets.
- *
- * @param uidset the UIDSets
- * @return the number of elements
- */
- public static long size(UIDSet[] uidset) {
- long count = 0;
-
- if (uidset != null)
- for (UIDSet u : uidset)
- count += u.size();
-
- return count;
- }
-
- /**
- * Count the total number of elements in an array of UIDSets.
- * Don't count UIDs greater then uidmax.
- *
- * @since JavaMail 1.5.1
- */
- private static long size(UIDSet[] uidset, long uidmax) {
- long count = 0;
-
- if (uidset != null)
- for (UIDSet u : uidset) {
- if (uidmax < 0)
- count += u.size();
- else if (u.start <= uidmax) {
- if (u.end < uidmax)
- count += u.end - u.start + 1;
- else
- count += uidmax - u.start + 1;
- }
- }
-
- return count;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/imap/protocol/package.html b/app/src/main/java/com/sun/mail/imap/protocol/package.html
deleted file mode 100644
index 370726e0fa..0000000000
--- a/app/src/main/java/com/sun/mail/imap/protocol/package.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.imap.protocol package
-
-
-
-
-This package includes internal IMAP support classes and
-SHOULD NOT BE USED DIRECTLY BY APPLICATIONS .
-
-
-
-
diff --git a/app/src/main/java/com/sun/mail/pop3/AppendStream.java b/app/src/main/java/com/sun/mail/pop3/AppendStream.java
deleted file mode 100644
index cc56ce3569..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/AppendStream.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-
-/**
- * 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);
- }
-
- @Override
- public void write(int b) throws IOException {
- raf.write(b);
- }
-
- @Override
- public void write(byte[] b) throws IOException {
- raf.write(b);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- raf.write(b, off, len);
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/pop3/DefaultFolder.java b/app/src/main/java/com/sun/mail/pop3/DefaultFolder.java
deleted file mode 100644
index eaf9d6391c..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/DefaultFolder.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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);
- }
-
- @Override
- public String getName() {
- return "";
- }
-
- @Override
- public String getFullName() {
- return "";
- }
-
- @Override
- public Folder getParent() {
- return null;
- }
-
- @Override
- public boolean exists() {
- return true;
- }
-
- @Override
- public Folder[] list(String pattern) throws MessagingException {
- Folder[] f = { getInbox() };
- return f;
- }
-
- @Override
- public char getSeparator() {
- return '/';
- }
-
- @Override
- public int getType() {
- return HOLDS_FOLDERS;
- }
-
- @Override
- public boolean create(int type) throws MessagingException {
- return false;
- }
-
- @Override
- public boolean hasNewMessages() throws MessagingException {
- return false;
- }
-
- @Override
- 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");
- }
-
-
- @Override
- public boolean delete(boolean recurse) throws MessagingException {
- throw new MethodNotSupportedException("delete");
- }
-
- @Override
- public boolean renameTo(Folder f) throws MessagingException {
- throw new MethodNotSupportedException("renameTo");
- }
-
- @Override
- public void open(int mode) throws MessagingException {
- throw new MethodNotSupportedException("open");
- }
-
- @Override
- public void close(boolean expunge) throws MessagingException {
- throw new MethodNotSupportedException("close");
- }
-
- @Override
- public boolean isOpen() {
- return false;
- }
-
- @Override
- public Flags getPermanentFlags() {
- return new Flags(); // empty flags object
- }
-
- @Override
- public int getMessageCount() throws MessagingException {
- return 0;
- }
-
- @Override
- public Message getMessage(int msgno) throws MessagingException {
- throw new MethodNotSupportedException("getMessage");
- }
-
- @Override
- public void appendMessages(Message[] msgs) throws MessagingException {
- throw new MethodNotSupportedException("Append not supported");
- }
-
- @Override
- public Message[] expunge() throws MessagingException {
- throw new MethodNotSupportedException("expunge");
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/POP3Folder.java b/app/src/main/java/com/sun/mail/pop3/POP3Folder.java
deleted file mode 100644
index a045a24ae2..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3Folder.java
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import javax.mail.*;
-import javax.mail.event.*;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.EOFException;
-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;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 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 POP3Message[] message_cache;
- private boolean doneUidl = false;
- private volatile TempFile fileCache = null;
- private boolean forceClose;
-
- MailLogger logger; // package private, for POP3Message
-
- protected 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().getDebug(), store.getSession().getDebugOut());
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getFullName() {
- return name;
- }
-
- @Override
- 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
- */
- @Override
- public boolean exists() {
- return exists;
- }
-
- /**
- * Always throws MessagingException because no POP3 folders
- * can contain subfolders.
- *
- * @exception MessagingException always
- */
- @Override
- 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
- */
- @Override
- public char getSeparator() {
- return '\0';
- }
-
- /**
- * Always returns Folder.HOLDS_MESSAGES.
- *
- * @return Folder.HOLDS_MESSAGES
- */
- @Override
- public int getType() {
- return HOLDS_MESSAGES;
- }
-
- /**
- * Always returns false; the POP3 protocol doesn't
- * support creating folders.
- *
- * @return false
- */
- @Override
- 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
- */
- @Override
- public boolean hasNewMessages() throws MessagingException {
- return false; // no way to know
- }
-
- /**
- * Always throws MessagingException because no POP3 folders
- * can contain subfolders.
- *
- * @exception MessagingException always
- */
- @Override
- 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
- */
- @Override
- 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
- */
- @Override
- 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 AuthenticationFailedException authentication failures
- * @exception MessagingException other open failures
- */
- @Override
- 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 array of appropriate size
- message_cache = new POP3Message[total];
- doneUidl = false;
-
- notifyConnectionListeners(ConnectionEvent.OPENED);
- }
-
- @Override
- 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 && !forceClose)
- port.rset();
- POP3Message m;
- if (expunge && mode == READ_WRITE && !forceClose) {
- // find all messages marked deleted and issue DELE commands
- for (int i = 0; i < message_cache.length; i++) {
- if ((m = message_cache[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.length; i++) {
- if ((m = message_cache[i]) != null)
- m.invalidate(true);
- }
-
- if (forceClose)
- port.close();
- else
- 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;
- }
- }
- }
-
- @Override
- 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
- }
- return false;
- }
- return true;
- }
-
- /**
- * Always returns an empty Flags object because
- * the POP3 protocol doesn't support any permanent flags.
- *
- * @return empty Flags object
- */
- @Override
- 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.
- */
- @Override
- public synchronized int getMessageCount() throws MessagingException {
- if (!opened)
- return -1;
- checkReadable();
- return total;
- }
-
- @Override
- public synchronized Message getMessage(int msgno)
- throws MessagingException {
- checkOpen();
-
- POP3Message m;
-
- // Assuming that msgno is <= total
- if ((m = message_cache[msgno-1]) == null) {
- m = createMessage(this, msgno);
- message_cache[msgno-1] = m;
- }
- 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, Integer.valueOf(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
- */
- @Override
- 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
- */
- @Override
- 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.
- */
- @Override
- 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.length];
- 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.
- *
- * @param msg the message
- * @return unique ID string
- * @exception MessagingException for failures
- */
- public synchronized String getUID(Message msg) throws MessagingException {
- checkOpen();
- if (!(msg instanceof POP3Message))
- throw new MessagingException("message is not a POP3Message");
- 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
- * @exception MessagingException for other failures
- */
- 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
- * @exception MessagingException for other failures
- * @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 (RuntimeException 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
- * @exception IOException for I/O errors talking to the server
- * @exception MessagingException for other errors
- * @since JavaMail 1.3.3
- */
- public synchronized InputStream listCommand()
- throws MessagingException, IOException {
- checkOpen();
- return port.list();
- }
-
- /**
- * Close the folder when we're finalized.
- */
- @Override
- protected void finalize() throws Throwable {
- forceClose = !store.finalizeCleanClose;
- try {
- if (opened)
- close(false);
- } finally {
- super.finalize();
- forceClose = 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.
- */
- @Override
- protected void notifyMessageChangedListeners(int type, Message m) {
- super.notifyMessageChangedListeners(type, m);
- }
-
- /**
- * Used by POP3Message.
- */
- TempFile getFileCache() {
- return fileCache;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/POP3Message.java b/app/src/main/java/com/sun/mail/pop3/POP3Message.java
deleted file mode 100644
index 7bc9c9fabe..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3Message.java
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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);
- assert folder instanceof POP3Folder;
- 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
- */
- @Override
- 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 for failures
- */
- @Override
- 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 = rawData.get();
- if (rawcontent == null) {
- TempFile cache = folder.getFileCache();
- if (cache != null) {
- 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
- */
- @Override
- 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
- */
- @Override
- 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 = 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
- * @exception MessagingException for failures
- */
- 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 for failures
- * @see javax.mail.internet.MimeUtility
- */
- @Override
- 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 for failures
- */
- @Override
- 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.
- * @exception MessagingException for other failures
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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 for failures
- * @see javax.mail.internet.MimeUtility
- */
- @Override
- 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 for failures
- */
- @Override
- 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 for failures
- */
- @Override
- 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.
- */
- @Override
- 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 for failures
- */
- @Override
- 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 for failures
- */
- @Override
- 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 for failures
- */
- @Override
- 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
- */
- @Override
- 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 IOException if an error occurs writing to the stream
- * or if an error is generated by the
- * javax.activation layer.
- * @exception MessagingException for other failures
- * @see javax.activation.DataHandler#writeTo
- */
- @Override
- public synchronized void writeTo(OutputStream os, String[] ignoreList)
- throws IOException, MessagingException {
- InputStream rawcontent = rawData.get();
- if (rawcontent == null && ignoreList == null &&
- !((POP3Store)(folder.getStore())).cacheWriteTo) {
- 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/app/src/main/java/com/sun/mail/pop3/POP3Provider.java b/app/src/main/java/com/sun/mail/pop3/POP3Provider.java
deleted file mode 100644
index cf73a6c333..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3Provider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The POP3 protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class POP3Provider extends Provider {
- public POP3Provider() {
- super(Provider.Type.STORE, "pop3", POP3Store.class.getName(),
- "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/POP3SSLProvider.java b/app/src/main/java/com/sun/mail/pop3/POP3SSLProvider.java
deleted file mode 100644
index 4831c50e86..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3SSLProvider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The POP3 SSL protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class POP3SSLProvider extends Provider {
- public POP3SSLProvider() {
- super(Provider.Type.STORE, "pop3s", POP3SSLStore.class.getName(),
- "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/POP3SSLStore.java b/app/src/main/java/com/sun/mail/pop3/POP3SSLStore.java
deleted file mode 100644
index fa0054c508..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3SSLStore.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/pop3/POP3Store.java b/app/src/main/java/com/sun/mail/pop3/POP3Store.java
deleted file mode 100644
index c9818a1de7..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/POP3Store.java
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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;
- volatile boolean finalizeCleanClose = 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.getDebug(), session.getDebugOut());
-
- if (!isSSL)
- isSSL = PropUtil.getBooleanProperty(session.getProperties(),
- "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");
-
- // mail.pop3.finalizecleanclose requires clean close when finalizing
- finalizeCleanClose = getBoolProp("finalizecleanclose");
-
- 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.getBooleanProperty(session.getProperties(),
- prop, false);
- if (logger.isLoggable(Level.CONFIG))
- logger.config(prop + ": " + val);
- return val;
- }
-
- /**
- * Get a reference to the session.
- */
- synchronized Session getSession() {
- return session;
- }
-
- @Override
- 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.getIntProperty(session.getProperties(),
- "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.
- */
- @Override
- 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
- }
- 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");
- throw cleanupAndThrow(p,
- new EOFException("STLS required but failed"));
- }
- } else if (requireStartTLS) {
- logger.fine("STLS required but not supported");
- throw cleanupAndThrow(p,
- 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) {
- throw cleanupAndThrow(p, 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;
- }
-
- private static IOException cleanupAndThrow(Protocol p, IOException ife) {
- try {
- p.quit();
- } catch (Throwable thr) {
- if (isRecoverable(thr)) {
- ife.addSuppressed(thr);
- } else {
- thr.addSuppressed(ife);
- if (thr instanceof Error) {
- throw (Error) thr;
- }
- if (thr instanceof RuntimeException) {
- throw (RuntimeException) thr;
- }
- throw new RuntimeException("unexpected exception", thr);
- }
- }
- return ife;
- }
-
- private static boolean isRecoverable(Throwable t) {
- return (t instanceof Exception) || (t instanceof LinkageError);
- }
-
- synchronized void closePort(POP3Folder owner) {
- if (portOwner == owner) {
- port = null;
- portOwner = null;
- }
- }
-
- @Override
- public synchronized void close() throws MessagingException {
- close(false);
- }
-
- synchronized void close(boolean force) throws MessagingException {
- try {
- if (port != null) {
- if (force)
- port.close();
- else
- port.quit();
- }
- } catch (IOException ioex) {
- } finally {
- port = null;
-
- // to set the state and send the closed connection event
- super.close();
- }
- }
-
- @Override
- public Folder getDefaultFolder() throws MessagingException {
- checkConnected();
- return new DefaultFolder(this);
- }
-
- /**
- * Only the name "INBOX" is supported.
- */
- @Override
- public Folder getFolder(String name) throws MessagingException {
- checkConnected();
- return new POP3Folder(this, name);
- }
-
- @Override
- 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
- * @exception MessagingException for failures
- * @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.emptyMap();
- }
-
- /**
- * Is this POP3Store using SSL to connect to the server?
- *
- * @return true if using SSL
- * @since JavaMail 1.4.6
- */
- public synchronized boolean isSSL() {
- return usingSSL;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (port != null) // don't force a connection attempt
- close(!finalizeCleanClose);
- } finally {
- super.finalize();
- }
- }
-
- private void checkConnected() throws MessagingException {
- if (!super.isConnected())
- throw new MessagingException("Not connected");
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/Protocol.java b/app/src/main/java/com/sun/mail/pop3/Protocol.java
deleted file mode 100644
index 85c9a0f953..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/Protocol.java
+++ /dev/null
@@ -1,862 +0,0 @@
-/*
- * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.PropUtil;
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.SocketFetcher;
-import com.sun.mail.util.LineInputStream;
-import com.sun.mail.util.TraceInputStream;
-import com.sun.mail.util.TraceOutputStream;
-import com.sun.mail.util.SharedByteArrayOutputStream;
-
-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 BufferedReader 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) {
- throw cleanupAndThrow(socket, ioe);
- }
-
- if (!r.ok) {
- throw cleanupAndThrow(socket, new IOException("Connect failed"));
- }
- if (enableAPOP && r.data != null) {
- 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");
- }
-
- private static IOException cleanupAndThrow(Socket socket, IOException ife) {
- try {
- socket.close();
- } catch (Throwable thr) {
- if (isRecoverable(thr)) {
- ife.addSuppressed(thr);
- } else {
- thr.addSuppressed(ife);
- if (thr instanceof Error) {
- throw (Error) thr;
- }
- if (thr instanceof RuntimeException) {
- throw (RuntimeException) thr;
- }
- throw new RuntimeException("unexpected exception", thr);
- }
- }
- return ife;
- }
-
- private static boolean isRecoverable(Throwable t) {
- return (t instanceof Exception) || (t instanceof LinkageError);
- }
-
- /**
- * 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);
-
- // should be US-ASCII, but not all JDK's support it so use iso-8859-1
- input = new BufferedReader(new InputStreamReader(traceInput,
- "iso-8859-1"));
- output = new PrintWriter(
- new BufferedWriter(
- new OutputStreamWriter(traceOutput, "iso-8859-1")));
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (socket != null) // Forgot to logout ?!
- quit();
- } finally {
- super.finalize();
- }
- }
-
- /**
- * 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";
- readResponse(); // read and ignore PASS response
- 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 {
- close();
- }
- return ok;
- }
-
- /**
- * Close the connection without sending any commands.
- */
- void close() {
- try {
- if (socket != null)
- socket.close();
- } catch (IOException ex) {
- // ignore it
- } finally {
- socket = null;
- input = null;
- output = null;
- }
- }
-
- /**
- * 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 (RuntimeException 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 (RuntimeException e) {
- // ignore it
- }
- }
- 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 (RuntimeException 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 (RuntimeException 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) {
- // ignore it
- }
- 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();
- } 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/app/src/main/java/com/sun/mail/pop3/Status.java b/app/src/main/java/com/sun/mail/pop3/Status.java
deleted file mode 100644
index bbfbd9cd9c..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/Status.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/pop3/TempFile.java b/app/src/main/java/com/sun/mail/pop3/TempFile.java
deleted file mode 100644
index 0d769e4aa5..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/TempFile.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import java.io.*;
-
-/**
- * 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();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- close();
- } finally {
- super.finalize();
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/WritableSharedFile.java b/app/src/main/java/com/sun/mail/pop3/WritableSharedFile.java
deleted file mode 100644
index 8daa0c5378..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/WritableSharedFile.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.pop3;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import javax.mail.util.SharedFileInputStream;
-
-/**
- * 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.
- */
- @Override
- 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;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/pop3/package.html b/app/src/main/java/com/sun/mail/pop3/package.html
deleted file mode 100644
index a3ea13b8eb..0000000000
--- a/app/src/main/java/com/sun/mail/pop3/package.html
+++ /dev/null
@@ -1,674 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.pop3 package
-
-
-
-A POP3 protocol provider for the Jakarta Mail 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 Jakarta Mail 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 Jakarta Mail 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 Jakarta Mail 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.
-
-Properties
-
-The POP3 protocol provider supports the following properties,
-which may be set in the Jakarta Mail 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.*".
-
-
-POP3 properties
-
-Name
-Type
-Description
-
-
-
-mail.pop3.user
-String
-Default user name for POP3.
-
-
-
-mail.pop3.host
-String
-The POP3 server to connect to.
-
-
-
-mail.pop3.port
-int
-The POP3 server port to connect to, if the connect() method doesn't
-explicitly specify one. Defaults to 110.
-
-
-
-mail.pop3.connectiontimeout
-int
-Socket connection timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.pop3.timeout
-int
-Socket read timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.pop3.writetimeout
-int
-Socket write timeout value in milliseconds.
-This timeout is implemented by using a
-java.util.concurrent.ScheduledExecutorService per connection
-that schedules a thread to close the socket if the timeout expires.
-Thus, the overhead of using this timeout is one thread per connection.
-Default is infinite timeout.
-
-
-
-mail.pop3.rsetbeforequit
-boolean
-
-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.class
-String
-
-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.localaddress
-String
-
-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.localport
-int
-
-Local port number to bind to when creating the POP3 socket.
-Defaults to the port number picked by the Socket class.
-
-
-
-
-mail.pop3.apop.enable
-boolean
-
-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.socketFactory
-SocketFactory
-
-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.class
-String
-
-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.fallback
-boolean
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.pop3.ssl.enable
-boolean
-
-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.checkserveridentity
-boolean
-
-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.trust
-String
-
-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.socketFactory
-SSLSocketFactory
-
-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.class
-String
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.pop3.ssl.protocols
-string
-
-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.ciphersuites
-string
-
-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.enable
-boolean
-
-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.
-If the server does not support STARTTLS, the connection continues without
-the use of TLS; see the
-mail.pop3.starttls.required
-property to fail if STARTTLS isn't supported.
-Note that an appropriate trust store must configured so that the client
-will trust the server's certificate.
-Defaults to false.
-
-
-
-
-mail.pop3.starttls.required
-boolean
-
-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.proxy.host
-string
-
-Specifies the host name of an HTTP web proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.pop3.proxy.port
-string
-
-Specifies the port number for the HTTP web proxy server.
-Defaults to port 80.
-
-
-
-
-mail.pop3.proxy.user
-string
-
-Specifies the user name to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.pop3.proxy.password
-string
-
-Specifies the password to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.pop3.socks.host
-string
-
-Specifies the host name of a SOCKS5 proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.pop3.socks.port
-string
-
-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.disabletop
-boolean
-
-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.disablecapa
-boolean
-
-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.
-
-
-
-
-
-boolean
-
-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.enable
-boolean
-
-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.dir
-String
-
-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.cachewriteto
-boolean
-
-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.keepmessagecontent
-boolean
-
-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 Jakarta Mail.)
-Defaults to false.
-
-
-
-
-mail.pop3.finalizecleanclose
-boolean
-
-When the finalizer for POP3Store or POP3Folder is called,
-should the connection to the server be closed cleanly, as if the
-application called the close method?
-Or should the connection to the server be closed without sending
-any commands to the server?
-Defaults to false, the connection is not closed cleanly.
-
-
-
-
-
-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:
-
-
-POP3 Loggers
-
-Logger Name
-Logging Level
-Purpose
-
-
-
-com.sun.mail.pop3
-CONFIG
-Configuration of the POP3Store
-
-
-
-com.sun.mail.pop3
-FINE
-General debugging output
-
-
-
-com.sun.mail.pop3.protocol
-FINEST
-Complete 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/app/src/main/java/com/sun/mail/smtp/DigestMD5.java b/app/src/main/java/com/sun/mail/smtp/DigestMD5.java
deleted file mode 100644
index 401834a0ce..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/DigestMD5.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.smtp;
-
-import java.io.*;
-import java.util.*;
-import java.util.logging.Level;
-import java.security.*;
-import java.nio.charset.StandardCharsets;
-
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.ASCIIUtility;
-import com.sun.mail.util.BASE64EncoderStream;
-import com.sun.mail.util.BASE64DecoderStream;
-
-/**
- * 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.
- *
- * @param host the host name
- * @param user the user name
- * @param passwd the user's password
- * @param realm the security realm
- * @param serverChallenge the challenge from the server
- * @return byte array with client's response
- * @exception IOException for I/O errors
- */
- 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());
- }
- StringBuilder result = new StringBuilder();
-
- 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
- Map map = tokenize(serverChallenge);
-
- if (realm == null) {
- String text = map.get("realm");
- realm = text != null ? new StringTokenizer(text, ",").nextToken()
- : host;
- }
-
- // server challenge random value
- String nonce = map.get("nonce");
-
- // Does server support UTF-8 usernames and passwords?
- String charset = map.get("charset");
- boolean utf8 = charset != null && charset.equalsIgnoreCase("utf-8");
-
- 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)
- if (utf8) {
- String up = user + ":" + realm + ":" + passwd;
- md5.update(md5.digest(up.getBytes(StandardCharsets.UTF_8)));
- } else
- 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 + "\"");
- if (utf8)
- result.append(",charset=\"utf-8\"");
- 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.
- *
- * @param serverResponse the response that was received from the server
- * @return true if server is authenticated
- * @exception IOException for character conversion failures
- */
- public boolean authServer(String serverResponse) throws IOException {
- Map 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(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 Map containing key/value pairs from server
- */
- @SuppressWarnings("fallthrough")
- private Map tokenize(String serverResponse)
- throws IOException {
- Map map = new HashMap<>();
- 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;
- default: // XXX - should never happen?
- 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/app/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java b/app/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java
deleted file mode 100644
index 234820e7cb..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPAddressFailedException.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the address
- */
- public InternetAddress getAddress() {
- return addr;
- }
-
- /**
- * Return the command that failed.
- *
- * @return the command
- */
- 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.
- *
- * @return the return code
- */
- public int getReturnCode() {
- return rc;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java b/app/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java
deleted file mode 100644
index 2ddb29a7eb..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPAddressSucceededException.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the address
- */
- public InternetAddress getAddress() {
- return addr;
- }
-
- /**
- * Return the command that succeeded.
- *
- * @return the command
- */
- 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.
- *
- * @return the return code
- */
- public int getReturnCode() {
- return rc;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPMessage.java b/app/src/main/java/com/sun/mail/smtp/SMTPMessage.java
deleted file mode 100644
index 826ea44ce0..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPMessage.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param session the Session
- */
- 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 for failures
- */
- 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 for failures
- */
- 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";
- StringBuilder sb = new StringBuilder();
- 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");
- *
- *
- * @param extension the extension string
- * @since JavaMail 1.3.2
- */
- public void setMailExtension(String extension) {
- this.extension = extension;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java b/app/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java
deleted file mode 100644
index d2fd75b911..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPOutputStream.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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);
- }
-
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- public void flush() {
- // do nothing
- }
-
- /**
- * Ensure we're at the beginning of a line.
- * Write CRLF if not.
- *
- * @exception IOException if the write fails
- */
- public void ensureAtBOL() throws IOException {
- if (!atBOL)
- super.writeln();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPProvider.java b/app/src/main/java/com/sun/mail/smtp/SMTPProvider.java
deleted file mode 100644
index 71f5703f43..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPProvider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.smtp;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The SMTP protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class SMTPProvider extends Provider {
- public SMTPProvider() {
- super(Provider.Type.TRANSPORT, "smtp", SMTPTransport.class.getName(),
- "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPSSLProvider.java b/app/src/main/java/com/sun/mail/smtp/SMTPSSLProvider.java
deleted file mode 100644
index fe4d09e603..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPSSLProvider.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.smtp;
-
-import javax.mail.Provider;
-
-import com.sun.mail.util.DefaultProvider;
-
-/**
- * The SMTP SSL protocol provider.
- */
-@DefaultProvider // Remove this annotation if you copy this provider
-public class SMTPSSLProvider extends Provider {
- public SMTPSSLProvider() {
- super(Provider.Type.TRANSPORT, "smtps",
- SMTPSSLTransport.class.getName(), "Oracle", null);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java b/app/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java
deleted file mode 100644
index f3a7300849..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPSSLTransport.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param session the Session
- * @param urlname the URLName of this transport
- */
- public SMTPSSLTransport(Session session, URLName urlname) {
- super(session, urlname, "smtps", true);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java b/app/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java
deleted file mode 100644
index d8cf3e66a8..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPSendFailedException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- * @param ex a chained exception
- * @param vs the valid addresses the message was sent to
- * @param vus the valid addresses the message was not sent to
- * @param inv the invalid addresses
- */
- 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.
- *
- * @return the command
- */
- 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.
- *
- * @return the return code
- */
- public int getReturnCode() {
- return rc;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java b/app/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java
deleted file mode 100644
index 3139c79ff3..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPSenderFailedException.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the address
- */
- public InternetAddress getAddress() {
- return addr;
- }
-
- /**
- * Return the command that failed.
- *
- * @return the command
- */
- 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.
- *
- * @return the return code
- */
- public int getReturnCode() {
- return rc;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java b/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java
deleted file mode 100644
index efbab723ab..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java
+++ /dev/null
@@ -1,2831 +0,0 @@
-/*
- * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.smtp;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.logging.Level;
-import java.lang.reflect.*;
-import java.nio.charset.StandardCharsets;
-import javax.net.ssl.SSLSocket;
-
-import javax.mail.*;
-import javax.mail.event.*;
-import javax.mail.internet.*;
-
-import com.sun.mail.util.PropUtil;
-import com.sun.mail.util.MailLogger;
-import com.sun.mail.util.ASCIIUtility;
-import com.sun.mail.util.SocketFetcher;
-import com.sun.mail.util.MailConnectException;
-import com.sun.mail.util.SocketConnectException;
-import com.sun.mail.util.BASE64EncoderStream;
-import com.sun.mail.util.LineInputStream;
-import com.sun.mail.util.TraceInputStream;
-import com.sun.mail.util.TraceOutputStream;
-import com.sun.mail.auth.Ntlm;
-
-/**
- * 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 boolean quitOnSessionReject = false; // true if we should send quit when session initiation is rejected
-
- private String saslRealm = UNKNOWN;
- private String authorizationID = UNKNOWN;
- private boolean enableSASL = false; // enable SASL authentication
- private boolean useCanonicalHostName = false; // use canonical host name?
- 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
- private boolean debugusername; // include username in debug output?
- private boolean debugpassword; // include password in debug output?
- private boolean allowutf8; // allow UTF-8 usernames and passwords?
- private int chunkSize; // chunk size if CHUNKING supported
-
- /** 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.
- *
- * @param session the Session
- * @param urlname the URLName of this transport
- */
- public SMTPTransport(Session session, URLName urlname) {
- this(session, urlname, "smtp", false);
- }
-
- /**
- * Constructor used by this class and by SMTPSSLTransport subclass.
- *
- * @param session the Session
- * @param urlname the URLName of this transport
- * @param name the protocol name of this transport
- * @param isSSL use SSL to connect?
- */
- protected SMTPTransport(Session session, URLName urlname,
- String name, boolean isSSL) {
- super(session, urlname);
- Properties props = session.getProperties();
-
- logger = new MailLogger(this.getClass(), "DEBUG SMTP",
- session.getDebug(), session.getDebugOut());
- traceLogger = logger.getSubLogger("protocol", null);
- noauthdebug = !PropUtil.getBooleanProperty(props,
- "mail.debug.auth", false);
- debugusername = PropUtil.getBooleanProperty(props,
- "mail.debug.auth.username", true);
- debugpassword = PropUtil.getBooleanProperty(props,
- "mail.debug.auth.password", false);
- if (urlname != null)
- name = urlname.getProtocol();
- this.name = name;
- if (!isSSL)
- isSSL = PropUtil.getBooleanProperty(props,
- "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.getBooleanProperty(props,
- "mail." + name + ".quitwait", true);
-
- // setting mail.smtp.quitonsessionreject to false causes us to directly
- // close the socket without sending a QUIT command
- quitOnSessionReject = PropUtil.getBooleanProperty(props,
- "mail." + name + ".quitonsessionreject", false);
-
- // mail.smtp.reportsuccess causes us to throw an exception on success
- reportSuccess = PropUtil.getBooleanProperty(props,
- "mail." + name + ".reportsuccess", false);
-
- // mail.smtp.starttls.enable enables use of STARTTLS command
- useStartTLS = PropUtil.getBooleanProperty(props,
- "mail." + name + ".starttls.enable", false);
-
- // mail.smtp.starttls.required requires use of STARTTLS command
- requireStartTLS = PropUtil.getBooleanProperty(props,
- "mail." + name + ".starttls.required", false);
-
- // mail.smtp.userset causes us to use RSET instead of NOOP
- // for isConnected
- useRset = PropUtil.getBooleanProperty(props,
- "mail." + name + ".userset", false);
-
- // mail.smtp.noop.strict requires 250 response to indicate success
- noopStrict = PropUtil.getBooleanProperty(props,
- "mail." + name + ".noop.strict", true);
-
- // check if SASL is enabled
- enableSASL = PropUtil.getBooleanProperty(props,
- "mail." + name + ".sasl.enable", false);
- if (enableSASL)
- logger.config("enable SASL");
- useCanonicalHostName = PropUtil.getBooleanProperty(props,
- "mail." + name + ".sasl.usecanonicalhostname", false);
- if (useCanonicalHostName)
- logger.config("use canonical host name");
-
- allowutf8 = PropUtil.getBooleanProperty(props,
- "mail.mime.allowutf8", false);
- if (allowutf8)
- logger.config("allow UTF-8");
-
- chunkSize = PropUtil.getIntProperty(props,
- "mail." + name + ".chunksize", -1);
- if (chunkSize > 0 && logger.isLoggable(Level.CONFIG))
- logger.config("chunk size " + chunkSize);
-
- // created here, because they're inner classes that reference "this"
- Authenticator[] a = new Authenticator[] {
- new LoginAuthenticator(),
- new PlainAuthenticator(),
- new DigestMD5Authenticator(),
- new NtlmAuthenticator(),
- new OAuth2Authenticator()
- };
- StringBuilder sb = new StringBuilder();
- 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.
- *
- * @return the local host name
- */
- 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.
- *
- * @param localhost the local host name
- * @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.
- *
- * @param socket the already connected socket
- * @exception MessagingException for failures
- * @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;
- }
-
- /**
- * Should SASL use the canonical host name?
- *
- * @return true if SASL should use the canonical host name
- *
- * @since JavaMail 1.5.2
- */
- public synchronized boolean getUseCanonicalHostName() {
- return useCanonicalHostName;
- }
-
- /**
- * Set whether SASL should use the canonical host name.
- *
- * @param useCanonicalHostName should SASL use the canonical host name?
- *
- * @since JavaMail 1.5.2
- */
- public synchronized void setUseCanonicalHostName(
- boolean useCanonicalHostName) {
- this.useCanonicalHostName = useCanonicalHostName;
- }
-
- /**
- * 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 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 = 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 synchronized 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 password the user's password
- * @return true if connection successful, false if authentication failed
- * @exception MessagingException for non-authentication failures
- */
- @Override
- protected synchronized boolean protocolConnect(String host, int port,
- String user, String password)
- throws MessagingException {
- Properties props = session.getProperties();
-
- // setting mail.smtp.auth to true enables attempts to use AUTH
- boolean useAuth = PropUtil.getBooleanProperty(props,
- "mail." + name + ".auth", false);
-
- /*
- * 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 || password == null)) {
- if (logger.isLoggable(Level.FINE)) {
- logger.fine("need username and password for authentication");
- logger.fine("protocolConnect returning false" +
- ", host=" + host +
- ", user=" + traceUser(user) +
- ", password=" + tracePassword(password));
- }
- return false;
- }
-
- // setting mail.smtp.ehlo to false disables attempts to use EHLO
- boolean useEhlo = PropUtil.getBooleanProperty(props,
- "mail." + name + ".ehlo", true);
- if (logger.isLoggable(Level.FINE))
- logger.fine("useEhlo " + useEhlo + ", useAuth " + useAuth);
-
- /*
- * 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.getIntProperty(props,
- "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 (allowutf8 && !supportsExtension("SMTPUTF8"))
- logger.log(Level.INFO, "mail.mime.allowutf8 set " +
- "but server doesn't advertise SMTPUTF8 support");
-
- if ((useAuth || (user != null && password != null)) &&
- (supportsExtension("AUTH") ||
- supportsExtension("AUTH=LOGIN"))) {
- if (logger.isLoggable(Level.FINE))
- logger.fine("protocolConnect login" +
- ", host=" + host +
- ", user=" + traceUser(user) +
- ", password=" + tracePassword(password));
- connected = authenticate(user, password);
- 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");
- try {
- if (sasllogin(getSASLMechanisms(), getSASLRealm(), authzid,
- user, passwd)) {
- return true; // success
- } else {
- logger.fine("SASL authentication failed");
- return false;
- }
- } catch (UnsupportedOperationException ex) {
- logger.log(Level.FINE, "SASL support failed", ex);
- // if the SASL support fails, fall back to non-SASL
- }
- }
-
- 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();
- m = m.toUpperCase(Locale.ENGLISH);
- Authenticator a = authenticators.get(m);
- if (a == null) {
- logger.log(Level.FINE, "no authenticator for mechanism {0}", m);
- continue;
- }
-
- if (!supportsAuthentication(m)) {
- logger.log(Level.FINE, "mechanism {0} not supported by server",
- m);
- continue;
- }
-
- /*
- * If using the default mechanisms, check if this one is disabled.
- */
- if (mechs == defaultAuthenticationMechanisms) {
- String dprop = "mail." + name + ".auth." +
- m.toLowerCase(Locale.ENGLISH) + ".disable";
- boolean disabled = PropUtil.getBooleanProperty(
- session.getProperties(),
- dprop, !a.enabled());
- if (disabled) {
- if (logger.isLoggable(Level.FINE))
- logger.fine("mechanism " + m +
- " disabled by property: " + dprop);
- continue;
- }
- }
-
- // only the first supported and enabled mechanism is used
- logger.log(Level.FINE, "Using mechanism {0}", m);
- return a.authenticate(host, authzid, user, passwd);
- }
-
- // if no authentication mechanism found, fail
- throw new AuthenticationFailedException(
- "No authentication mechanisms 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 final String mech; // the mechanism name, set in the constructor
- private final boolean enabled; // is this mechanism enabled by default?
-
- Authenticator(String mech) {
- this(mech, true);
- }
-
- Authenticator(String mech, boolean enabled) {
- this.mech = mech.toUpperCase(Locale.ENGLISH);
- this.enabled = enabled;
- }
-
- String getMechanism() {
- return mech;
- }
-
- boolean enabled() {
- return enabled;
- }
-
- /**
- * 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 {
- Throwable thrown = null;
- 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);
- } catch (Throwable t) { // crypto can't be initialized?
- logger.log(Level.FINE, "AUTH " + mech + " failed", t);
- thrown = t;
- } finally {
- if (noauthdebug && isTracing())
- logger.fine("AUTH " + mech + " " +
- (resp == 235 ? "succeeded" : "failed"));
- resumeTracing();
- if (resp != 235) {
- closeConnection();
- if (thrown != null) {
- if (thrown instanceof Error)
- throw (Error)thrown;
- if (thrown instanceof Exception)
- throw new AuthenticationFailedException(
- getLastServerResponse(),
- (Exception)thrown);
- assert false : "unknown Throwable"; // can't happen
- }
- 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");
- }
-
- @Override
- void doAuth(String host, String authzid, String user, String passwd)
- throws MessagingException, IOException {
- // send username
- resp = simpleCommand(BASE64EncoderStream.encode(
- user.getBytes(StandardCharsets.UTF_8)));
- if (resp == 334) {
- // send passwd
- resp = simpleCommand(BASE64EncoderStream.encode(
- passwd.getBytes(StandardCharsets.UTF_8)));
- }
- }
- }
-
- /**
- * Perform the authentication handshake for PLAIN authentication.
- */
- private class PlainAuthenticator extends Authenticator {
- PlainAuthenticator() {
- super("PLAIN");
- }
-
- @Override
- 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(authzid.getBytes(StandardCharsets.UTF_8));
- b64os.write(0);
- b64os.write(user.getBytes(StandardCharsets.UTF_8));
- b64os.write(0);
- b64os.write(passwd.getBytes(StandardCharsets.UTF_8));
- b64os.flush(); // complete the encoding
-
- return ASCIIUtility.toString(bos.toByteArray());
- }
-
- @Override
- 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;
- }
-
- @Override
- void doAuth(String host, String authzid, String user, String passwd)
- throws MessagingException, IOException {
- DigestMD5 md5 = getMD5();
- assert md5 != null;
-
- 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;
-
- NtlmAuthenticator() {
- super("NTLM");
- }
-
- @Override
- String getInitialResponse(String host, String authzid, String user,
- String passwd) throws MessagingException, IOException {
- ntlm = new Ntlm(getNTLMDomain(), getLocalHost(),
- user, passwd, logger);
-
- int flags = PropUtil.getIntProperty(
- session.getProperties(),
- "mail." + name + ".auth.ntlm.flags", 0);
- boolean v2 = PropUtil.getBooleanProperty(
- session.getProperties(),
- "mail." + name + ".auth.ntlm.v2", true);
-
- String type1 = ntlm.generateType1Msg(flags, v2);
- return type1;
- }
-
- @Override
- void doAuth(String host, String authzid, String user, String passwd)
- throws MessagingException, IOException {
- assert ntlm != null;
- String type3 = ntlm.generateType3Msg(
- getLastServerResponse().substring(4).trim());
-
- resp = simpleCommand(type3);
- }
- }
-
- /**
- * Perform the authentication handshake for XOAUTH2 authentication.
- */
- private class OAuth2Authenticator extends Authenticator {
-
- OAuth2Authenticator() {
- super("XOAUTH2", false); // disabled by default
- }
-
- @Override
- String getInitialResponse(String host, String authzid, String user,
- String passwd) throws MessagingException, IOException {
- String resp = "user=" + user + "\001auth=Bearer " +
- passwd + "\001\001";
- byte[] b = BASE64EncoderStream.encode(
- resp.getBytes(StandardCharsets.UTF_8));
- return ASCIIUtility.toString(b);
- }
-
- @Override
- void doAuth(String host, String authzid, String user, String passwd)
- throws MessagingException, IOException {
- // should never get here
- throw new AuthenticationFailedException("OAUTH2 asked for more");
- }
- }
-
- /**
- * SASL-based login.
- *
- * @param allowed the allowed SASL mechanisms
- * @param realm the SASL realm
- * @param authzid the authorization ID
- * @param u the user name for authentication
- * @param p the password for authentication
- * @return true for success
- * @exception MessagingException for failures
- */
- private boolean sasllogin(String[] allowed, String realm, String authzid,
- String u, String p) throws MessagingException {
- String serviceHost;
- if (useCanonicalHostName)
- serviceHost = serverSocket.getInetAddress().getCanonicalHostName();
- else
- serviceHost = host;
- 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,
- serviceHost
- });
- } 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 = extMap.get("AUTH");
- if (a != null) {
- StringTokenizer st = new StringTokenizer(a);
- while (st.hasMoreTokens())
- v.add(st.nextToken());
- }
- }
- }
- String[] mechs = 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.smtp.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.smtp.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.
- */
- @Override
- 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");
- }
- if (addresses == null || addresses.length == 0) {
- throw new SendFailedException("No recipient addresses");
- }
- for (int i = 0; i < addresses.length; i++) {
- if (!(addresses[i] instanceof InternetAddress)) {
- throw new MessagingException(addresses[i] +
- " is not an InternetAddress");
- }
- }
-
- 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.getBooleanProperty(session.getProperties(),
- "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();
- if (chunkSize > 0 && supportsExtension("CHUNKING")) {
- /*
- * Use BDAT to send the data in chunks.
- * Note that even though the BDAT command is able to send
- * messages that contain binary data, we can't use it to
- * do that because a) we still need to canonicalize the
- * line terminators for text data, which we can't tell apart
- * from the message content, and b) the message content is
- * encoded before we even know that we can use BDAT.
- */
- this.message.writeTo(bdat(), ignoreList);
- finishBdat();
- } else {
- 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);
- }
- logger.fine("message successfully delivered to mail server");
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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
- */
- @Override
- 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() {
- List
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 ArrayList<>();
- for (int k = 0; k < i; k++)
- groups.add(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.add(ia[j]);
- } else
- groups.add(a);
- } catch (ParseException pex) {
- // parse failed, add the whole thing
- groups.add(a);
- }
- } else {
- // if we've started accumulating a list, add this to it
- if (groups != null)
- groups.add(a);
- }
- }
-
- // if we have a new list, convert it back to an array
- if (groups != null) {
- InternetAddress[] newa = new InternetAddress[groups.size()];
- groups.toArray(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;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- closeConnection();
- } catch (MessagingException mex) {
- // ignore it
- } finally {
- super.finalize();
- }
- }
-
- ///////////////////// 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
- * @exception MessagingException for failures
- * @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
- * @exception MessagingException for failures
- * @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:
- *
- * SMTPMessage.getEnvelopeFrom()
- * mail.smtp.from property
- * From: header in the message
- * System username using the
- * InternetAddress.getLocalAddress() method
- *
- *
- * @exception MessagingException for failures
- * @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);
-
- if (allowutf8 && supportsExtension("SMTPUTF8"))
- cmd += " SMTPUTF8";
-
- // 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,
- allowutf8 && supportsExtension("SMTPUTF8"));
- 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;
- default:
- 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.
- *
- * @exception MessagingException for failures
- * @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 lists 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 {
- List valid = new ArrayList<>();
- List validUnsent = new ArrayList<>();
- List invalid = new ArrayList<>();
- 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.getBooleanProperty(session.getProperties(),
- "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.add(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.add(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.add(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.add(ia);
- } else if (retCode >= 500 && retCode <= 599) {
- // assume address is invalid, although we don't really know
- invalid.add(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 lists into appropriate arrays
- if (sendFailed) {
- // copy invalid addrs
- invalidAddr = new Address[invalid.size()];
- invalid.toArray(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.get(j);
- for (int j = 0; j < validUnsent.size(); j++)
- validUnsentAddr[i++] = (Address)validUnsent.get(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.toArray(invalidAddr);
-
- // copy valid unsent addresses to validUnsent
- validUnsentAddr = new Address[validUnsent.size()];
- validUnsent.toArray(validUnsentAddr);
-
- // copy valid addresses to validSent
- validSentAddr = new Address[valid.size()];
- valid.toArray(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.
- *
- * @return the stream to write to
- * @exception MessagingException for failures
- * @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.
- *
- * @exception IOException for I/O errors
- * @exception MessagingException for other failures
- * @since JavaMail 1.4.1
- */
- protected void finishData() throws IOException, MessagingException {
- assert Thread.holdsLock(this);
- dataStream.ensureAtBOL();
- issueSendCommand(".", 250);
- }
-
- /**
- * Return a stream that will use the SMTP BDAT command to send data.
- *
- * @return the stream to write to
- * @exception MessagingException for failures
- * @since JavaMail 1.6.0
- */
- protected OutputStream bdat() throws MessagingException {
- assert Thread.holdsLock(this);
- dataStream = new BDATOutputStream(serverOutput, chunkSize);
- return dataStream;
- }
-
- /**
- * Terminate the sent data.
- *
- * @exception IOException for I/O errors
- * @exception MessagingException for other failures
- * @since JavaMail 1.6.0
- */
- protected void finishBdat() throws IOException, MessagingException {
- assert Thread.holdsLock(this);
- dataStream.ensureAtBOL();
- dataStream.close(); // doesn't close underlying socket
- }
-
- /**
- * Issue the STARTTLS command and switch the socket to
- * TLS mode if it succeeds.
- *
- * @exception MessagingException for failures
- * @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) {
- String failResponse = lastServerResponse;
- try {
- if (quitOnSessionReject) {
- sendCommand("QUIT");
- if (quitWait) {
- int resp = readServerResponse();
- if (resp != 221 && resp != -1 &&
- logger.isLoggable(Level.FINE))
- logger.fine("QUIT failed with " + resp);
- }
- }
- } catch (Exception e) {
- if (logger.isLoggable(Level.FINE))
- logger.log(Level.FINE, "QUIT failed", e);
- } finally {
- 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: " + failResponse);
- throw new MessagingException(
- "Got bad greeting from SMTP host: " + host +
- ", port: " + port +
- ", response: " + failResponse);
- } else {
- if (logger.isLoggable(Level.FINE))
- logger.fine("connected to host \"" +
- host + "\", port: " + port);
- }
- } 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) {
- try {
- if (quitOnSessionReject) {
- sendCommand("QUIT");
- if (quitWait) {
- int resp = readServerResponse();
- if (resp != 221 && resp != -1 &&
- logger.isLoggable(Level.FINE))
- logger.fine("QUIT failed with " + resp);
- }
- }
- } catch (Exception e) {
- if (logger.isLoggable(Level.FINE))
- logger.log(Level.FINE, "QUIT failed", e);
- } finally {
- 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);
- 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);
- }
- } 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.getBooleanProperty(session.getProperties(),
- "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)
- * @exception MessagingException for failures
- * @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.
- *
- * @param cmd the command
- * @return the response code
- * @exception MessagingException for failures
- * @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.
- *
- * @param cmd the command
- * @return the response code
- * @exception MessagingException for failures
- * @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.
- *
- * @param cmd the command
- * @exception MessagingException for failures
- * @since JavaMail 1.4.1
- */
- protected void sendCommand(String cmd) throws MessagingException {
- sendCommand(toBytes(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
- * @exception MessagingException for failures
- * @since JavaMail 1.4.1
- */
- protected int readServerResponse() throws MessagingException {
- assert Thread.holdsLock(this);
- String serverResponse = "";
- int returnCode = 0;
- StringBuilder buf = new StringBuilder(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 :
- 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 = 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
- *
- *
- * @param s the string to convert
- * @return the xtext format string
- * @since JavaMail 1.4.1
- */
- // XXX - keeping this around only for compatibility
- protected static String xtext(String s) {
- return xtext(s, false);
- }
-
- /**
- * Like xtext(s), but allow UTF-8 strings.
- *
- * @param s the string to convert
- * @param utf8 convert string to UTF-8 first?
- * @return the xtext format string
- * @since JavaMail 1.6.0
- */
- protected static String xtext(String s, boolean utf8) {
- StringBuilder sb = null;
- byte[] bytes;
- if (utf8)
- bytes = s.getBytes(StandardCharsets.UTF_8);
- else
- bytes = ASCIIUtility.getBytes(s);
- for (int i = 0; i < bytes.length; i++) {
- char c = (char)(((int)bytes[i])&0xff);
- if (!utf8 && c >= 128) // not ASCII
- throw new IllegalArgumentException(
- "Non-ASCII character in SMTP submitter: " + s);
- if (c < '!' || c > '~' || c == '+' || c == '=') {
- // not printable ASCII
- if (sb == null) {
- sb = new StringBuilder(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;
- }
-
- private String traceUser(String user) {
- return debugusername ? user : "";
- }
-
- private String tracePassword(String password) {
- return debugpassword ? password :
- (password == null ? "" : "");
- }
-
- /**
- * Convert the String to either ASCII or UTF-8 bytes
- * depending on allowutf8.
- */
- private byte[] toBytes(String s) {
- if (allowutf8)
- return s.getBytes(StandardCharsets.UTF_8);
- else
- // don't use StandardCharsets.US_ASCII because it rejects non-ASCII
- return ASCIIUtility.getBytes(s);
- }
-
- /*
- * Probe points for GlassFish monitoring.
- */
- private void sendMessageStart(String subject) { }
- private void sendMessageEnd() { }
-
-
- /**
- * An SMTPOutputStream that wraps a ChunkedOutputStream.
- */
- private class BDATOutputStream extends SMTPOutputStream {
-
- /**
- * Create a BDATOutputStream that wraps a ChunkedOutputStream
- * of the given size and built on top of the specified
- * underlying output stream.
- *
- * @param out the underlying output stream
- * @param size the chunk size
- */
- public BDATOutputStream(OutputStream out, int size) {
- super(new ChunkedOutputStream(out, size));
- }
-
- /**
- * Close this output stream.
- *
- * @exception IOException for I/O errors
- */
- @Override
- public void close() throws IOException {
- out.close();
- }
- }
-
- /**
- * An OutputStream that buffers data in chunks and uses the
- * RFC 3030 BDAT SMTP command to send each chunk.
- */
- private class ChunkedOutputStream extends OutputStream {
- private final OutputStream out;
- private final byte[] buf;
- private int count = 0;
-
- /**
- * Create a ChunkedOutputStream built on top of the specified
- * underlying output stream.
- *
- * @param out the underlying output stream
- * @param size the chunk size
- */
- public ChunkedOutputStream(OutputStream out, int size) {
- this.out = out;
- buf = new byte[size];
- }
-
- /**
- * Writes the specified byte to this output stream.
- *
- * @param b the byte to write
- * @exception IOException for I/O errors
- */
- @Override
- public void write(int b) throws IOException {
- buf[count++] = (byte)b;
- if (count >= buf.length)
- flush();
- }
-
- /**
- * Writes len bytes to this output stream starting at off.
- *
- * @param b bytes to write
- * @param off offset in array
- * @param len number of bytes to write
- * @exception IOException for I/O errors
- */
- @Override
- public void write(byte b[], int off, int len) throws IOException {
- while (len > 0) {
- int size = Math.min(buf.length - count, len);
- if (size == buf.length) {
- // avoid the copy
- bdat(b, off, size, false);
- } else {
- System.arraycopy(b, off, buf, count, size);
- count += size;
- }
- off += size;
- len -= size;
- if (count >= buf.length)
- flush();
- }
- }
-
- /**
- * Flush this output stream.
- *
- * @exception IOException for I/O errors
- */
- @Override
- public void flush() throws IOException {
- bdat(buf, 0, count, false);
- count = 0;
- }
-
- /**
- * Close this output stream.
- *
- * @exception IOException for I/O errors
- */
- @Override
- public void close() throws IOException {
- bdat(buf, 0, count, true);
- count = 0;
- }
-
- /**
- * Send the specified bytes using the BDAT command.
- */
- private void bdat(byte[] b, int off, int len, boolean last)
- throws IOException {
- if (len > 0 || last) {
- try {
- if (last)
- sendCommand("BDAT " + len + " LAST");
- else
- sendCommand("BDAT " + len);
- out.write(b, off, len);
- out.flush();
- int ret = readServerResponse();
- if (ret != 250)
- throw new IOException(lastServerResponse);
- } catch (MessagingException mex) {
- throw new IOException("BDAT write exception", mex);
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java b/app/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java
deleted file mode 100644
index 2c2a645ebd..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/SaslAuthenticator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/smtp/package.html b/app/src/main/java/com/sun/mail/smtp/package.html
deleted file mode 100644
index 197755c64c..0000000000
--- a/app/src/main/java/com/sun/mail/smtp/package.html
+++ /dev/null
@@ -1,835 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.smtp package
-
-
-
-An SMTP protocol provider for the Jakarta Mail 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 4616
-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.
-
-
-Support for OAuth 2.0 authentication via the
-
-XOAUTH2 authentication mechanism is provided either through the SASL
-support described above or as a built-in authentication mechanism in the
-SMTP provider.
-The OAuth 2.0 Access Token should be passed as the password for this mechanism.
-See
-OAuth2 Support for details.
-
-
-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 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 CHUNKING extension defined in
-RFC 3030 .
-Set the mail.smtp.chunksize property to the desired chunk
-size in bytes.
-If the server supports the CHUNKING extension, the BDAT command will be
-used to send the message in chunksize pieces. Note that no pipelining is
-done so this will be slower than sending the message in one piece.
-Note also that the BINARYMIME extension described in RFC 3030 is NOT supported.
-
-Properties
-
-The SMTP protocol provider supports the following properties,
-which may be set in the Jakarta Mail 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.*".
-
-
-SMTP properties
-
-Name
-Type
-Description
-
-
-
-mail.smtp.user
-String
-Default user name for SMTP.
-
-
-
-mail.smtp.host
-String
-The SMTP server to connect to.
-
-
-
-mail.smtp.port
-int
-The SMTP server port to connect to, if the connect() method doesn't
-explicitly specify one. Defaults to 25.
-
-
-
-mail.smtp.connectiontimeout
-int
-Socket connection timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.smtp.timeout
-int
-Socket read timeout value in milliseconds.
-This timeout is implemented by java.net.Socket.
-Default is infinite timeout.
-
-
-
-mail.smtp.writetimeout
-int
-Socket write timeout value in milliseconds.
-This timeout is implemented by using a
-java.util.concurrent.ScheduledExecutorService per connection
-that schedules a thread to close the socket if the timeout expires.
-Thus, the overhead of using this timeout is one thread per connection.
-Default is infinite timeout.
-
-
-
-mail.smtp.from
-String
-
-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.localhost
-String
-
-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.localaddress
-String
-
-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.localport
-int
-
-Local port number to bind to when creating the SMTP socket.
-Defaults to the port number picked by the Socket class.
-
-
-
-
-mail.smtp.ehlo
-boolean
-
-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.auth
-boolean
-If true, attempt to authenticate the user using the AUTH command.
-Defaults to false.
-
-
-
-mail.smtp.auth.mechanisms
-String
-
-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
-except XOAUTH2.
-
-
-
-
-mail.smtp.auth.login.disable
-boolean
-If true, prevents use of the AUTH LOGIN command.
-Default is false.
-
-
-
-mail.smtp.auth.plain.disable
-boolean
-If true, prevents use of the AUTH PLAIN command.
-Default is false.
-
-
-
-mail.smtp.auth.digest-md5.disable
-boolean
-If true, prevents use of the AUTH DIGEST-MD5 command.
-Default is false.
-
-
-
-mail.smtp.auth.ntlm.disable
-boolean
-If true, prevents use of the AUTH NTLM command.
-Default is false.
-
-
-
-mail.smtp.auth.ntlm.domain
-String
-
-The NTLM authentication domain.
-
-
-
-
-mail.smtp.auth.ntlm.flags
-int
-
-NTLM protocol-specific flags.
-See
-http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details.
-
-
-
-
-
-
-mail.smtp.auth.xoauth2.disable
-boolean
-If true, prevents use of the AUTHENTICATE XOAUTH2 command.
-Because the OAuth 2.0 protocol requires a special access token instead of
-a password, this mechanism is disabled by default. Enable it by explicitly
-setting this property to "false" or by setting the "mail.smtp.auth.mechanisms"
-property to "XOAUTH2".
-
-
-
-mail.smtp.submitter
-String
-The 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.notify
-String
-The NOTIFY option to the RCPT command. Either NEVER, or some
-combination of SUCCESS, FAILURE, and DELAY (separated by commas).
-
-
-
-mail.smtp.dsn.ret
-String
-The RET option to the MAIL command. Either FULL or HDRS.
-
-
-
-mail.smtp.allow8bitmime
-boolean
-
-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.sendpartial
-boolean
-
-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.enable
-boolean
-
-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.mechanisms
-String
-
-A space or comma separated list of SASL mechanism names to try
-to use.
-
-
-
-
-mail.smtp.sasl.authorizationid
-String
-
-The authorization ID to use in the SASL authentication.
-If not set, the authentication ID (user name) is used.
-
-
-
-
-mail.smtp.sasl.realm
-String
-The realm to use with DIGEST-MD5 authentication.
-
-
-
-mail.smtp.sasl.usecanonicalhostname
-boolean
-
-If set to true, the canonical host name returned by
-{@link java.net.InetAddress#getCanonicalHostName InetAddress.getCanonicalHostName}
-is passed to the SASL mechanism, instead of the host name used to connect.
-Defaults to false.
-
-
-
-
-mail.smtp.quitwait
-boolean
-
-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.quitonsessionreject
-boolean
-
-If set to false (the default), on session initiation rejection the QUIT
-command is not sent and the connection is immediately closed.
-If set to true, causes the transport to send the QUIT command prior to
-closing the connection.
-
-
-
-
-mail.smtp.reportsuccess
-boolean
-
-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.socketFactory
-SocketFactory
-
-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.class
-String
-
-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.fallback
-boolean
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.smtp.ssl.enable
-boolean
-
-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.checkserveridentity
-boolean
-
-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.trust
-String
-
-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.socketFactory
-SSLSocketFactory
-
-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.class
-String
-
-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.port
-int
-
-Specifies the port to connect to when using the specified socket
-factory.
-If not set, the default port will be used.
-
-
-
-
-mail.smtp.ssl.protocols
-string
-
-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.ciphersuites
-string
-
-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.enable
-boolean
-
-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.
-If the server does not support STARTTLS, the connection continues without
-the use of TLS; see the
-mail.smtp.starttls.required
-property to fail if STARTTLS isn't supported.
-Note that an appropriate trust store must configured so that the client
-will trust the server's certificate.
-Defaults to false.
-
-
-
-
-mail.smtp.starttls.required
-boolean
-
-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.proxy.host
-string
-
-Specifies the host name of an HTTP web proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.smtp.proxy.port
-string
-
-Specifies the port number for the HTTP web proxy server.
-Defaults to port 80.
-
-
-
-
-mail.smtp.proxy.user
-string
-
-Specifies the user name to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.smtp.proxy.password
-string
-
-Specifies the password to use to authenticate with the HTTP web proxy server.
-By default, no authentication is done.
-
-
-
-
-mail.smtp.socks.host
-string
-
-Specifies the host name of a SOCKS5 proxy server that will be used for
-connections to the mail server.
-
-
-
-
-mail.smtp.socks.port
-string
-
-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.mailextension
-String
-
-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.userset
-boolean
-
-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.strict
-boolean
-
-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:
-
-
-SMTP Loggers
-
-Logger Name
-Logging Level
-Purpose
-
-
-
-com.sun.mail.smtp
-CONFIG
-Configuration of the SMTPTransport
-
-
-
-com.sun.mail.smtp
-FINE
-General debugging output
-
-
-
-com.sun.mail.smtp.protocol
-FINEST
-Complete 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/app/src/main/java/com/sun/mail/util/ASCIIUtility.java b/app/src/main/java/com/sun/mail/util/ASCIIUtility.java
deleted file mode 100644
index 4ab7a5576d..0000000000
--- a/app/src/main/java/com/sun/mail/util/ASCIIUtility.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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()
- *
- * @param b the bytes
- * @param start the first byte offset
- * @param end the last byte offset
- * @param radix the radix
- * @return the integer value
- * @exception NumberFormatException for conversion errors
- */
- 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.
- *
- * @param b the bytes
- * @param start the first byte offset
- * @param end the last byte offset
- * @return the integer value
- * @exception NumberFormatException for conversion errors
- */
- 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()
- *
- * @param b the bytes
- * @param start the first byte offset
- * @param end the last byte offset
- * @param radix the radix
- * @return the long value
- * @exception NumberFormatException for conversion errors
- */
- 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.
- *
- * @param b the bytes
- * @param start the first byte offset
- * @param end the last byte offset
- * @return the long value
- * @exception NumberFormatException for conversion errors
- */
- 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.
- *
- * @param b the bytes
- * @param start the first byte offset
- * @param end the last byte offset
- * @return the String
- */
- 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.
- *
- * @param b the bytes
- * @return the 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/app/src/main/java/com/sun/mail/util/BASE64DecoderStream.java b/app/src/main/java/com/sun/mail/util/BASE64DecoderStream.java
deleted file mode 100644
index 2559c748a7..0000000000
--- a/app/src/main/java/com/sun/mail/util/BASE64DecoderStream.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- */
- @Override
- 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.
- */
- @Override
- public int read(byte[] buf, int off, int len) throws IOException {
- if (len == 0)
- return 0;
- // 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.
- */
- @Override
- 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
- */
- @Override
- 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
- */
- @Override
- 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.
- *
- * @param inbuf the byte array
- * @return the decoded byte array
- */
- 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/app/src/main/java/com/sun/mail/util/BASE64EncoderStream.java b/app/src/main/java/com/sun/mail/util/BASE64EncoderStream.java
deleted file mode 100644
index 036b534821..0000000000
--- a/app/src/main/java/com/sun/mail/util/BASE64EncoderStream.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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
- */
- @Override
- 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.
- *
- * @param inbuf the byte array
- * @return the encoded byte array
- */
- 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/app/src/main/java/com/sun/mail/util/BEncoderStream.java b/app/src/main/java/com/sun/mail/util/BEncoderStream.java
deleted file mode 100644
index 5a43dd688c..0000000000
--- a/app/src/main/java/com/sun/mail/util/BEncoderStream.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param b the byte array
- * @return the length
- */
- public static int encodedLength(byte[] b) {
- return ((b.length + 2)/3) * 4;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/CRLFOutputStream.java b/app/src/main/java/com/sun/mail/util/CRLFOutputStream.java
deleted file mode 100644
index 7cca3d7cc8..0000000000
--- a/app/src/main/java/com/sun/mail/util/CRLFOutputStream.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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);
- }
-
- @Override
- 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;
- }
-
- @Override
- public void write(byte b[]) throws IOException {
- write(b, 0, b.length);
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/util/DecodingException.java b/app/src/main/java/com/sun/mail/util/DecodingException.java
deleted file mode 100644
index fddd84f8fc..0000000000
--- a/app/src/main/java/com/sun/mail/util/DecodingException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param s the exception message
- */
- public DecodingException(String s) {
- super(s);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/DefaultProvider.java b/app/src/main/java/com/sun/mail/util/DefaultProvider.java
deleted file mode 100644
index c47e128782..0000000000
--- a/app/src/main/java/com/sun/mail/util/DefaultProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.lang.annotation.*;
-
-/**
- * Annotation to mark the default providers that are part of Jakarta Mail.
- * DO NOT use this on any provider made available independently.
- *
- * @author Bill Shannon
- * @since Jakarta Mail 1.6.4
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface DefaultProvider {
-}
diff --git a/app/src/main/java/com/sun/mail/util/FolderClosedIOException.java b/app/src/main/java/com/sun/mail/util/FolderClosedIOException.java
deleted file mode 100644
index 5a4ef10ae4..0000000000
--- a/app/src/main/java/com/sun/mail/util/FolderClosedIOException.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- *
- * @return the dead Folder
- */
- public Folder getFolder() {
- return folder;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/LineInputStream.java b/app/src/main/java/com/sun/mail/util/LineInputStream.java
deleted file mode 100644
index cb068f3ffb..0000000000
--- a/app/src/main/java/com/sun/mail/util/LineInputStream.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.io.*;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.CharacterCodingException;
-
-/**
- * LineInputStream supports 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 an IMAP/SMTP/etc. stream.
- *
- * This class also supports UTF-8 data by calling the appropriate
- * constructor. Or, if the System property mail.mime.allowutf8
- * is set to true, an attempt will be made to interpret the data as UTF-8,
- * falling back to treating it as an 8-bit charset if that fails.
- *
- * LineInputStream is implemented as a FilterInputStream, so one can just
- * wrap it around any input stream and read bytes from this filter.
- *
- * @author John Mani
- * @author Bill Shannon
- */
-
-public class LineInputStream extends FilterInputStream {
-
- private boolean allowutf8;
- private byte[] lineBuffer = null; // reusable byte buffer
- private CharsetDecoder decoder;
-
- private static boolean defaultutf8 =
- PropUtil.getBooleanSystemProperty("mail.mime.allowutf8", false);
- private static int MAX_INCR = 1024*1024; // 1MB
-
- public LineInputStream(InputStream in) {
- this(in, false);
- }
-
- /**
- * @param in the InputStream
- * @param allowutf8 allow UTF-8 characters?
- * @since JavaMail 1.6
- */
- public LineInputStream(InputStream in, boolean allowutf8) {
- super(in);
- this.allowutf8 = allowutf8;
- if (!allowutf8 && defaultutf8) {
- decoder = StandardCharsets.UTF_8.newDecoder();
- decoder.onMalformedInput(CodingErrorAction.REPORT);
- decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
- }
- }
-
- /**
- * 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()
- *
- * @return the line
- * @exception IOException for I/O errors
- */
- @SuppressWarnings("deprecation") // for old String constructor
- public String readLine() throws IOException {
- //InputStream in = this.in;
- byte[] buf = lineBuffer;
-
- if (buf == null)
- buf = lineBuffer = new byte[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 byte[buf.length * 2];
- else
- buf = new byte[buf.length + MAX_INCR];
- room = buf.length - offset - 1;
- System.arraycopy(lineBuffer, 0, buf, 0, offset);
- lineBuffer = buf;
- }
- buf[offset++] = (byte)c1;
- }
-
- if ((c1 == -1) && (offset == 0))
- return null;
-
- if (allowutf8)
- return new String(buf, 0, offset, StandardCharsets.UTF_8);
- else {
- if (defaultutf8) {
- // try to decode it as UTF-8
- try {
- return decoder.decode(ByteBuffer.wrap(buf, 0, offset)).
- toString();
- } catch (CharacterCodingException cex) {
- // looks like it's not valid UTF-8 data,
- // fall through and treat it as an 8-bit charset
- }
- }
- return new String(buf, 0, 0, offset);
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/LineOutputStream.java b/app/src/main/java/com/sun/mail/util/LineOutputStream.java
deleted file mode 100644
index d86f5ec22a..0000000000
--- a/app/src/main/java/com/sun/mail/util/LineOutputStream.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.io.*;
-import java.nio.charset.StandardCharsets;
-
-/**
- * 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
- * @author Bill Shannon
- */
-
-public class LineOutputStream extends FilterOutputStream {
- private boolean allowutf8;
-
- private static byte[] newline;
-
- static {
- newline = new byte[2];
- newline[0] = (byte)'\r';
- newline[1] = (byte)'\n';
- }
-
- public LineOutputStream(OutputStream out) {
- this(out, false);
- }
-
- /**
- * @param out the OutputStream
- * @param allowutf8 allow UTF-8 characters?
- * @since JavaMail 1.6
- */
- public LineOutputStream(OutputStream out, boolean allowutf8) {
- super(out);
- this.allowutf8 = allowutf8;
- }
-
- public void writeln(String s) throws IOException {
- byte[] bytes;
- if (allowutf8)
- bytes = s.getBytes(StandardCharsets.UTF_8);
- else
- bytes = ASCIIUtility.getBytes(s);
- out.write(bytes);
- out.write(newline);
- }
-
- public void writeln() throws IOException {
- out.write(newline);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/LogOutputStream.java b/app/src/main/java/com/sun/mail/util/LogOutputStream.java
deleted file mode 100644
index c1c31531f9..0000000000
--- a/app/src/main/java/com/sun/mail/util/LogOutputStream.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2008, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param logger the MailLogger
- */
- public LogOutputStream(MailLogger logger) {
- this.logger = logger;
- this.level = Level.FINEST;
- }
-
- @Override
- 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;
- }
-
- @Override
- public void write(byte b[]) throws IOException {
- write(b, 0, b.length);
- }
-
- @Override
- 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.
- *
- * @param msg the message to log
- */
- 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/app/src/main/java/com/sun/mail/util/MailConnectException.java b/app/src/main/java/com/sun/mail/util/MailConnectException.java
deleted file mode 100644
index 768e90f4f7..0000000000
--- a/app/src/main/java/com/sun/mail/util/MailConnectException.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/util/MailLogger.java b/app/src/main/java/com/sun/mail/util/MailLogger.java
deleted file mode 100644
index 686f5cab6f..0000000000
--- a/app/src/main/java/com/sun/mail/util/MailLogger.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.io.PrintStream;
-import java.text.MessageFormat;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.mail.Session;
-
-/**
- * A simplified logger used by Jakarta Mail 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 {
- /**
- * For log messages.
- */
- private final Logger logger;
- /**
- * For debug output.
- */
- private final String prefix;
- /**
- * Produce debug output?
- */
- private final boolean debug;
- /**
- * Stream for debug output.
- */
- private final PrintStream out;
-
- /**
- * 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
- */
- @Deprecated
- 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
- */
- @Deprecated
- 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
- * @return a MailLogger for the given name and prefix.
- */
- 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
- * @return a MailLogger for the given name and prefix.
- */
- 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
- * @return a MailLogger for the given name and prefix.
- */
- 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
- * @return a MailLogger for the given name and prefix.
- */
- 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.
- * @param level the log level.
- * @param msg the message.
- */
- public void log(Level level, String msg) {
- ifDebugOut(msg);
- if (logger.isLoggable(level)) {
- final StackTraceElement frame = inferCaller();
- logger.logp(level, frame.getClassName(), frame.getMethodName(), msg);
- }
- }
-
- /**
- * Log the message at the specified level.
- * @param level the log level.
- * @param msg the message.
- * @param param1 the additional parameter.
- */
- 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 StackTraceElement frame = inferCaller();
- logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, param1);
- }
- }
-
- /**
- * Log the message at the specified level.
- * @param level the log level.
- * @param msg the message.
- * @param params the message parameters.
- */
- public void log(Level level, String msg, Object... params) {
- if (debug) {
- msg = MessageFormat.format(msg, params);
- debugOut(msg);
- }
-
- if (logger.isLoggable(level)) {
- final StackTraceElement frame = inferCaller();
- logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, params);
- }
- }
-
- /**
- * Log the message at the specified level using a format string.
- * @param level the log level.
- * @param msg the message format string.
- * @param params the message parameters.
- *
- * @since JavaMail 1.5.4
- */
- 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.
- * @param level the log level.
- * @param msg the message.
- * @param thrown the throwable to log.
- */
- 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 StackTraceElement frame = inferCaller();
- logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, thrown);
- }
- }
-
- /**
- * Log a message at the CONFIG level.
- * @param msg the message.
- */
- public void config(String msg) {
- log(Level.CONFIG, msg);
- }
-
- /**
- * Log a message at the FINE level.
- * @param msg the message.
- */
- public void fine(String msg) {
- log(Level.FINE, msg);
- }
-
- /**
- * Log a message at the FINER level.
- * @param msg the message.
- */
- public void finer(String msg) {
- log(Level.FINER, msg);
- }
-
- /**
- * Log a message at the FINEST level.
- * @param msg the message.
- */
- 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.
- * @param level the log level.
- * @return true if loggable.
- */
- public boolean isLoggable(Level level) {
- return debug || logger.isLoggable(level);
- }
-
- /**
- * Common code to conditionally log debug statements.
- * @param msg the message to log.
- */
- private void ifDebugOut(String msg) {
- if (debug)
- debugOut(msg);
- }
-
- /**
- * Common formatting for debug output.
- * @param msg the message to log.
- */
- private 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()).
- * @param clazz the class source.
- * @return the package name or an empty string.
- */
- 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 Jakarta Mail
- * 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.
- * @return StackTraceElement that logged the message. Treat as read-only.
- */
- private StackTraceElement 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 frame;
- }
- 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 StackTraceElement(MailLogger.class.getName(), "log",
- MailLogger.class.getName(), -1);
- }
-
- /**
- * Frames to ignore as part of the MailLogger to JUL bridge.
- * @param cname the class name.
- * @return true if the class name is part of the MailLogger bridge.
- */
- private boolean isLoggerImplFrame(String cname) {
- return MailLogger.class.getName().equals(cname);
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java b/app/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java
deleted file mode 100644
index 2f6363244f..0000000000
--- a/app/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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 for security errors
- */
- 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
- * @throws GeneralSecurityException for security errors
- */
- 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.
- *
- * @throws KeyManagementException for key manager errors
- */
- private synchronized void newAdapteeFactory()
- throws KeyManagementException {
- sslcontext.init(keyManagers, trustManagers, secureRandom);
-
- // Get SocketFactory and save it in our instance var
- adapteeFactory = sslcontext.getSocketFactory();
- }
-
- /**
- * @return the keyManagers
- */
- public synchronized KeyManager[] getKeyManagers() {
- return keyManagers.clone();
- }
-
- /**
- * @param keyManagers the keyManagers to set
- * @throws GeneralSecurityException for security errors
- */
- public synchronized void setKeyManagers(KeyManager... keyManagers)
- throws GeneralSecurityException {
- this.keyManagers = keyManagers.clone();
- newAdapteeFactory();
- }
-
- /**
- * @return the secureRandom
- */
- public synchronized SecureRandom getSecureRandom() {
- return secureRandom;
- }
-
- /**
- * @param secureRandom the secureRandom to set
- * @throws GeneralSecurityException for security errors
- */
- 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
- * @throws GeneralSecurityException for security errors
- */
- 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() {
- if (trustedHosts == null)
- return null;
- else
- return trustedHosts.clone();
- }
-
- /**
- * @param trustedHosts the hosts to trust
- */
- public synchronized void setTrustedHosts(String... trustedHosts) {
- if (trustedHosts == null)
- this.trustedHosts = null;
- else
- this.trustedHosts = 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)
- */
- @Override
- 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)
- */
- @Override
- 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()
- */
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return adapteeTrustManager.getAcceptedIssuers();
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/MessageRemovedIOException.java b/app/src/main/java/com/sun/mail/util/MessageRemovedIOException.java
deleted file mode 100644
index 96840f773f..0000000000
--- a/app/src/main/java/com/sun/mail/util/MessageRemovedIOException.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/util/MimeUtil.java b/app/src/main/java/com/sun/mail/util/MimeUtil.java
deleted file mode 100644
index 6944249857..0000000000
--- a/app/src/main/java/com/sun/mail/util/MimeUtil.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param mp the MimePart
- * @param contentType the Content-Type value
- * @return the cleaned 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
- AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public ClassLoader run() {
- ClassLoader cl = null;
- try {
- cl = Thread.currentThread().getContextClassLoader();
- } catch (SecurityException ex) { }
- return cl;
- }
- });
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/PropUtil.java b/app/src/main/java/com/sun/mail/util/PropUtil.java
deleted file mode 100644
index 430b378692..0000000000
--- a/app/src/main/java/com/sun/mail/util/PropUtil.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param props the properties
- * @param name the property name
- * @param def default value if property not found
- * @return the property value
- */
- public static int getIntProperty(Properties props, String name, int def) {
- return getInt(getProp(props, name), def);
- }
-
- /**
- * Get a boolean valued property.
- *
- * @param props the properties
- * @param name the property name
- * @param def default value if property not found
- * @return the property value
- */
- public static boolean getBooleanProperty(Properties props,
- String name, boolean def) {
- return getBoolean(getProp(props, name), def);
- }
-
- /**
- * Get an integer valued property.
- *
- * @param session the Session
- * @param name the property name
- * @param def default value if property not found
- * @return the property value
- */
- @Deprecated
- public static int getIntSessionProperty(Session session,
- String name, int def) {
- return getInt(getProp(session.getProperties(), name), def);
- }
-
- /**
- * Get a boolean valued property.
- *
- * @param session the Session
- * @param name the property name
- * @param def default value if property not found
- * @return the property value
- */
- @Deprecated
- public static boolean getBooleanSessionProperty(Session session,
- String name, boolean def) {
- return getBoolean(getProp(session.getProperties(), name), def);
- }
-
- /**
- * Get a boolean valued System property.
- *
- * @param name the property name
- * @param def default value if property not found
- * @return the property value
- */
- 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 {
- String s = (String)value;
- if (s.startsWith("0x"))
- return Integer.parseInt(s.substring(2), 16);
- else
- return Integer.parseInt(s);
- } 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/app/src/main/java/com/sun/mail/util/QDecoderStream.java b/app/src/main/java/com/sun/mail/util/QDecoderStream.java
deleted file mode 100644
index 19ea0c6785..0000000000
--- a/app/src/main/java/com/sun/mail/util/QDecoderStream.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- 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/app/src/main/java/com/sun/mail/util/QEncoderStream.java b/app/src/main/java/com/sun/mail/util/QEncoderStream.java
deleted file mode 100644
index 659897aec3..0000000000
--- a/app/src/main/java/com/sun/mail/util/QEncoderStream.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- 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.
- *
- * @param b the byte array
- * @param encodingWord true if encoding words, false if encoding text
- * @return the length
- */
- 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/app/src/main/java/com/sun/mail/util/QPDecoderStream.java b/app/src/main/java/com/sun/mail/util/QPDecoderStream.java
deleted file mode 100644
index 284c6145ca..0000000000
--- a/app/src/main/java/com/sun/mail/util/QPDecoderStream.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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
- */
- @Override
- 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.
- */
- @Override
- 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/app/src/main/java/com/sun/mail/util/QPEncoderStream.java b/app/src/main/java/com/sun/mail/util/QPEncoderStream.java
deleted file mode 100644
index d39102dae6..0000000000
--- a/app/src/main/java/com/sun/mail/util/QPEncoderStream.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- 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.
- */
- @Override
- public void flush() throws IOException {
- if (gotSpace) {
- output(' ', true);
- gotSpace = false;
- }
- out.flush();
- }
-
- /**
- * Forces any buffered output bytes to be encoded out to the stream
- * and closes this output stream.
- *
- * @exception IOException for I/O errors
- */
- @Override
- public void close() throws IOException {
- flush();
- 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/app/src/main/java/com/sun/mail/util/ReadableMime.java b/app/src/main/java/com/sun/mail/util/ReadableMime.java
deleted file mode 100644
index ecba7e2a49..0000000000
--- a/app/src/main/java/com/sun/mail/util/ReadableMime.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- * @exception MessagingException for failures
- */
- public InputStream getMimeStream() throws MessagingException;
-}
diff --git a/app/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java b/app/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java
deleted file mode 100644
index 9fd617ea9b..0000000000
--- a/app/src/main/java/com/sun/mail/util/SharedByteArrayOutputStream.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/com/sun/mail/util/SocketConnectException.java b/app/src/main/java/com/sun/mail/util/SocketConnectException.java
deleted file mode 100644
index 6b3c16e082..0000000000
--- a/app/src/main/java/com/sun/mail/util/SocketConnectException.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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 {
- /**
- * The socket host name.
- */
- private String host;
- /**
- * The socket port.
- */
- private int port;
- /**
- * The connection timeout.
- */
- private int cto;
- /**
- * The generated serial id.
- */
- 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
- Throwable t = getCause();
- assert t == null || t instanceof Exception;
- return (Exception) t;
- }
-
- /**
- * 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/app/src/main/java/com/sun/mail/util/SocketFetcher.java b/app/src/main/java/com/sun/mail/util/SocketFetcher.java
deleted file mode 100644
index d6af220615..0000000000
--- a/app/src/main/java/com/sun/mail/util/SocketFetcher.java
+++ /dev/null
@@ -1,895 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.security.*;
-import java.net.*;
-import java.io.*;
-import java.nio.channels.SocketChannel;
-import java.nio.charset.StandardCharsets;
-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.*;
-
-/**
- * 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
- * prefix .usesocketchannels
- *
- * 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.
- *
- * If the usesocketchannels property is set, and we create the Socket
- * ourself, and the selection of other properties allows, create a
- * SocketChannel and get the Socket from it. This allows us to later
- * retrieve the SocketChannel from the Socket and use it with Select.
- *
- * @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
- * @return the Socket
- * @exception IOException for I/O errors
- */
- 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) {
- if (logger.isLoggable(Level.FINEST))
- logger.finest("set socket read timeout " + to);
- 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;
-
- if (logger.isLoggable(Level.FINEST))
- logger.finest("create socket: prefix " + prefix +
- ", localaddr " + localaddr + ", localport " + localport +
- ", host " + host + ", port " + port +
- ", connection timeout " + cto + ", timeout " + to +
- ", socket factory " + sf + ", useSSL " + useSSL);
-
- String proxyHost = props.getProperty(prefix + ".proxy.host", null);
- String proxyUser = props.getProperty(prefix + ".proxy.user", null);
- String proxyPassword = props.getProperty(prefix + ".proxy.password", null);
- int proxyPort = 80;
- String socksHost = null;
- int socksPort = 1080;
- String err = null;
-
- if (proxyHost != null) {
- int i = proxyHost.indexOf(':');
- if (i >= 0) {
- try {
- proxyPort = Integer.parseInt(proxyHost.substring(i + 1));
- } catch (NumberFormatException ex) {
- // ignore it
- }
- proxyHost = proxyHost.substring(0, i);
- }
- proxyPort = PropUtil.getIntProperty(props,
- prefix + ".proxy.port", proxyPort);
- err = "Using web proxy host, port: " + proxyHost + ", " + proxyPort;
- if (logger.isLoggable(Level.FINER)) {
- logger.finer("web proxy host " + proxyHost + ", port " + proxyPort);
- if (proxyUser != null)
- logger.finer("web proxy user " + proxyUser + ", password " +
- (proxyPassword == null ? "" : ""));
- }
- } else if ((socksHost =
- props.getProperty(prefix + ".socks.host", null)) != null) {
- int i = socksHost.indexOf(':');
- if (i >= 0) {
- try {
- socksPort = Integer.parseInt(socksHost.substring(i + 1));
- } catch (NumberFormatException ex) {
- // ignore it
- }
- socksHost = socksHost.substring(0, i);
- }
- 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 && !(sf instanceof SSLSocketFactory))
- 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 if (PropUtil.getBooleanProperty(props,
- prefix + ".usesocketchannels", false)) {
- logger.finer("using SocketChannels");
- socket = SocketChannel.open().socket();
- } else
- socket = new Socket();
- }
- if (to >= 0) {
- if (logger.isLoggable(Level.FINEST))
- logger.finest("set socket read timeout " + to);
- socket.setSoTimeout(to);
- }
- int writeTimeout = PropUtil.getIntProperty(props,
- prefix + ".writetimeout", -1);
- if (writeTimeout != -1) { // wrap original
- if (logger.isLoggable(Level.FINEST))
- logger.finest("set socket write timeout " + writeTimeout);
- socket = new WriteTimeoutSocket(socket, writeTimeout);
- }
- if (localaddr != null)
- socket.bind(new InetSocketAddress(localaddr, localport));
- try {
- logger.finest("connecting...");
- if (proxyHost != null)
- proxyConnect(socket, proxyHost, proxyPort,
- proxyUser, proxyPassword, host, port, cto);
- else if (cto >= 0)
- socket.connect(new InetSocketAddress(host, port), cto);
- else
- socket.connect(new InetSocketAddress(host, port));
- logger.finest("success!");
- } catch (IOException ex) {
- logger.log(Level.FINEST, "connection failed", 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 || sf instanceof SSLSocketFactory) &&
- !(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 if (sf instanceof SSLSocketFactory)
- ssf = (SSLSocketFactory)sf;
- 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.
- *
- * @param socket the existing socket
- * @return the wrapped Socket
- * @exception IOException for I/O errors
- * @deprecated
- */
- @Deprecated
- 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.
- *
- * @param socket the existing socket
- * @param props the properties
- * @param prefix the property prefix
- * @return the wrapped Socket
- * @exception IOException for I/O errors
- * @deprecated
- */
- @Deprecated
- 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.
- *
- * @param socket the existing socket
- * @param host the host the socket is connected to
- * @param props the properties
- * @param prefix the property prefix
- * @return the wrapped Socket
- * @exception IOException for I/O errors
- */
- 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 {
- /*
- * The UW IMAP server insists on at least the TLSv1
- * protocol for STARTTLS, and won't accept the old SSLv2
- * or SSLv3 protocols. Here we enable only the non-SSL
- * protocols. XXX - this should probably be parameterized.
- */
- String[] prots = sslsocket.getEnabledProtocols();
- if (logger.isLoggable(Level.FINER))
- logger.finer("SSL enabled protocols before " +
- Arrays.asList(prots));
- List eprots = new ArrayList<>();
- for (int i = 0; i < prots.length; i++) {
- if (prots[i] != null && !prots[i].startsWith("SSL"))
- eprots.add(prots[i]);
- }
- sslsocket.setEnabledProtocols(
- eprots.toArray(new String[eprots.size()]));
- }
- String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null);
- if (ciphers != null)
- sslsocket.setEnabledCipherSuites(stringArray(ciphers));
- if (logger.isLoggable(Level.FINER)) {
- logger.finer("SSL enabled protocols after " +
- Arrays.asList(sslsocket.getEnabledProtocols()));
- logger.finer("SSL enabled 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)) {
- throw cleanupAndThrow(sslsocket,
- new IOException("Server is not trusted: " + host));
- }
- }
- }
-
- private static IOException cleanupAndThrow(Socket socket, IOException ife) {
- try {
- socket.close();
- } catch (Throwable thr) {
- if (isRecoverable(thr)) {
- ife.addSuppressed(thr);
- } else {
- thr.addSuppressed(ife);
- if (thr instanceof Error) {
- throw (Error) thr;
- }
- if (thr instanceof RuntimeException) {
- throw (RuntimeException) thr;
- }
- throw new RuntimeException("unexpected exception", thr);
- }
- }
- return ife;
- }
-
- private static boolean isRecoverable(Throwable t) {
- return (t instanceof Exception) || (t instanceof LinkageError);
- }
-
- /**
- * 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
- * @exception IOException if we can't verify identity of server
- */
- 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[] { Byte.valueOf((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, "HostnameChecker 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 = 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);
- }
-
- /**
- * Use the HTTP CONNECT protocol to connect to a
- * site through an HTTP proxy server.
- *
- * Protocol is roughly:
- *
- * CONNECT : HTTP/1.1
- * Host: :
- *
- *
- * HTTP/1.1 200 Connect established
- *
- *
- *
- */
- private static void proxyConnect(Socket socket,
- String proxyHost, int proxyPort,
- String proxyUser, String proxyPassword,
- String host, int port, int cto)
- throws IOException {
- if (logger.isLoggable(Level.FINE))
- logger.fine("connecting through proxy " +
- proxyHost + ":" + proxyPort + " to " +
- host + ":" + port);
- if (cto >= 0)
- socket.connect(new InetSocketAddress(proxyHost, proxyPort), cto);
- else
- socket.connect(new InetSocketAddress(proxyHost, proxyPort));
- PrintStream os = new PrintStream(socket.getOutputStream(), false,
- StandardCharsets.UTF_8.name());
- StringBuilder requestBuilder = new StringBuilder();
- requestBuilder.append("CONNECT ").append(host).append(":").append(port).
- append(" HTTP/1.1\r\n");
- requestBuilder.append("Host: ").append(host).append(":").append(port).
- append("\r\n");
- if (proxyUser != null && proxyPassword != null) {
- byte[] upbytes = (proxyUser + ':' + proxyPassword).
- getBytes(StandardCharsets.UTF_8);
- String proxyHeaderValue = new String(
- BASE64EncoderStream.encode(upbytes),
- StandardCharsets.US_ASCII);
- requestBuilder.append("Proxy-Authorization: Basic ").
- append(proxyHeaderValue).append("\r\n");
- }
- requestBuilder.append("Proxy-Connection: keep-alive\r\n\r\n");
- os.print(requestBuilder.toString());
- os.flush();
- BufferedReader r = new BufferedReader(new InputStreamReader(
- socket.getInputStream(), StandardCharsets.UTF_8));
- String line;
- boolean first = true;
- while ((line = r.readLine()) != null) {
- if (line.length() == 0)
- break;
- logger.finest(line);
- if (first) {
- StringTokenizer st = new StringTokenizer(line);
- String http = st.nextToken();
- String code = st.nextToken();
- if (!code.equals("200")) {
- try {
- socket.close();
- } catch (IOException ioex) {
- // ignored
- }
- ConnectException ex = new ConnectException(
- "connection through proxy " +
- proxyHost + ":" + proxyPort + " to " +
- host + ":" + port + " failed: " + line);
- logger.log(Level.FINE, "connect failed", ex);
- throw ex;
- }
- first = false;
- }
- }
- }
-
- /**
- * 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 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
- AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public ClassLoader run() {
- ClassLoader cl = null;
- try {
- cl = Thread.currentThread().getContextClassLoader();
- } catch (SecurityException ex) { }
- return cl;
- }
- });
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/TraceInputStream.java b/app/src/main/java/com/sun/mail/util/TraceInputStream.java
deleted file mode 100644
index fceb7ac45a..0000000000
--- a/app/src/main/java/com/sun/mail/util/TraceInputStream.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- */
- @Override
- 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
- */
- @Override
- 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/app/src/main/java/com/sun/mail/util/TraceOutputStream.java b/app/src/main/java/com/sun/mail/util/TraceOutputStream.java
deleted file mode 100644
index 28f282015a..0000000000
--- a/app/src/main/java/com/sun/mail/util/TraceOutputStream.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @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;
- }
-
- /**
- * Writes the specified byte to this output stream.
- * Writes out the byte into the trace stream if the trace mode
- * is true
- *
- * @param b the byte to write
- * @exception IOException for I/O errors
- */
- @Override
- 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
- *
- * @param b bytes to write
- * @param off offset in array
- * @param len number of bytes to write
- * @exception IOException for I/O errors
- */
- @Override
- 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/app/src/main/java/com/sun/mail/util/UUDecoderStream.java b/app/src/main/java/com/sun/mail/util/UUDecoderStream.java
deleted file mode 100644
index 82f957cc0b..0000000000
--- a/app/src/main/java/com/sun/mail/util/UUDecoderStream.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- */
- @Override
- 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
- }
-
- @Override
- 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;
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/util/UUEncoderStream.java b/app/src/main/java/com/sun/mail/util/UUEncoderStream.java
deleted file mode 100644
index 183cc34737..0000000000
--- a/app/src/main/java/com/sun/mail/util/UUEncoderStream.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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;
- private boolean wroteSuffix = false;
-
- private String name; // name of file
- private 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", 0644);
- }
-
- /**
- * 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, 0644);
- }
-
- /**
- * 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
- *
- * @param name the buffer name
- * @param mode the permission mode
- */
- public void setNameMode(String name, int mode) {
- this.name = name;
- this.mode = mode;
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- for (int i = 0; i < len; i++)
- write(b[off + i]);
- }
-
- @Override
- public void write(byte[] data) throws IOException {
- write(data, 0, data.length);
- }
-
- @Override
- 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;
- }
- }
-
- @Override
- public void flush() throws IOException {
- if (bufsize > 0) { // If there's unencoded characters in the buffer
- writePrefix();
- encode(); // .. encode them
- bufsize = 0;
- }
- writeSuffix();
- out.flush();
- }
-
- @Override
- 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.format("begin %o %s%n", 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 {
- if (!wroteSuffix) {
- PrintStream ps = new PrintStream(out, false, "us-ascii");
- ps.println(" \nend");
- ps.flush();
- wroteSuffix = true;
- }
- }
-
- /**
- * 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/app/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java b/app/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java
deleted file mode 100644
index 1c84b52985..0000000000
--- a/app/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util;
-
-import java.io.*;
-import java.net.*;
-import java.util.concurrent.*;
-import java.util.Collections;
-import java.util.Set;
-import java.nio.channels.SocketChannel;
-import java.lang.reflect.*;
-
-/**
- * A special Socket that uses a ScheduledExecutorService to
- * implement timeouts for writes. The write timeout is specified
- * (in milliseconds) when the WriteTimeoutSocket is created.
- *
- * @author Bill Shannon
- */
-public class WriteTimeoutSocket extends Socket {
-
- // delegate all operations to this socket
- private final Socket socket;
- // to schedule task to cancel write after timeout
- private final ScheduledExecutorService ses;
- // the timeout, in milliseconds
- private final int timeout;
-
- public WriteTimeoutSocket(Socket socket, int timeout) throws IOException {
- this.socket = socket;
- // XXX - could share executor with all instances?
- this.ses = Executors.newScheduledThreadPool(1);
- this.timeout = timeout;
- }
-
- public WriteTimeoutSocket(int timeout) throws IOException {
- this(new Socket(), timeout);
- }
-
- public WriteTimeoutSocket(InetAddress address, int port, int timeout)
- throws IOException {
- this(timeout);
- socket.connect(new InetSocketAddress(address, port));
- }
-
- public WriteTimeoutSocket(InetAddress address, int port,
- InetAddress localAddress, int localPort, int timeout)
- throws IOException {
- this(timeout);
- socket.bind(new InetSocketAddress(localAddress, localPort));
- socket.connect(new InetSocketAddress(address, port));
- }
-
- public WriteTimeoutSocket(String host, int port, int timeout)
- throws IOException {
- this(timeout);
- socket.connect(new InetSocketAddress(host, port));
- }
-
- public WriteTimeoutSocket(String host, int port,
- InetAddress localAddress, int localPort, int timeout)
- throws IOException {
- this(timeout);
- socket.bind(new InetSocketAddress(localAddress, localPort));
- socket.connect(new InetSocketAddress(host, port));
- }
-
- // override all Socket methods and delegate to underlying Socket
-
- @Override
- public void connect(SocketAddress remote) throws IOException {
- socket.connect(remote, 0);
- }
-
- @Override
- public void connect(SocketAddress remote, int timeout) throws IOException {
- socket.connect(remote, timeout);
- }
-
- @Override
- public void bind(SocketAddress local) throws IOException {
- socket.bind(local);
- }
-
- @Override
- public SocketAddress getRemoteSocketAddress() {
- return socket.getRemoteSocketAddress();
- }
-
- @Override
- public SocketAddress getLocalSocketAddress() {
- return socket.getLocalSocketAddress();
- }
-
- @Override
- public void setPerformancePreferences(int connectionTime, int latency,
- int bandwidth) {
- socket.setPerformancePreferences(connectionTime, latency, bandwidth);
- }
-
- @Override
- public SocketChannel getChannel() {
- return socket.getChannel();
- }
-
- @Override
- public InetAddress getInetAddress() {
- return socket.getInetAddress();
- }
-
- @Override
- public InetAddress getLocalAddress() {
- return socket.getLocalAddress();
- }
-
- @Override
- public int getPort() {
- return socket.getPort();
- }
-
- @Override
- public int getLocalPort() {
- return socket.getLocalPort();
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return socket.getInputStream();
- }
-
- @Override
- public synchronized OutputStream getOutputStream() throws IOException {
- // wrap the returned stream to implement write timeout
- return new TimeoutOutputStream(socket.getOutputStream(), ses, timeout);
- }
-
- @Override
- public void setTcpNoDelay(boolean on) throws SocketException {
- socket.setTcpNoDelay(on);
- }
-
- @Override
- public boolean getTcpNoDelay() throws SocketException {
- return socket.getTcpNoDelay();
- }
-
- @Override
- public void setSoLinger(boolean on, int linger) throws SocketException {
- socket.setSoLinger(on, linger);
- }
-
- @Override
- public int getSoLinger() throws SocketException {
- return socket.getSoLinger();
- }
-
- @Override
- public void sendUrgentData(int data) throws IOException {
- socket.sendUrgentData(data);
- }
-
- @Override
- public void setOOBInline(boolean on) throws SocketException {
- socket.setOOBInline(on);
- }
-
- @Override
- public boolean getOOBInline() throws SocketException {
- return socket.getOOBInline();
- }
-
- @Override
- public void setSoTimeout(int timeout) throws SocketException {
- socket.setSoTimeout(timeout);
- }
-
- @Override
- public int getSoTimeout() throws SocketException {
- return socket.getSoTimeout();
- }
-
- @Override
- public void setSendBufferSize(int size) throws SocketException {
- socket.setSendBufferSize(size);
- }
-
- @Override
- public int getSendBufferSize() throws SocketException {
- return socket.getSendBufferSize();
- }
-
- @Override
- public void setReceiveBufferSize(int size) throws SocketException {
- socket.setReceiveBufferSize(size);
- }
-
- @Override
- public int getReceiveBufferSize() throws SocketException {
- return socket.getReceiveBufferSize();
- }
-
- @Override
- public void setKeepAlive(boolean on) throws SocketException {
- socket.setKeepAlive(on);
- }
-
- @Override
- public boolean getKeepAlive() throws SocketException {
- return socket.getKeepAlive();
- }
-
- @Override
- public void setTrafficClass(int tc) throws SocketException {
- socket.setTrafficClass(tc);
- }
-
- @Override
- public int getTrafficClass() throws SocketException {
- return socket.getTrafficClass();
- }
-
- @Override
- public void setReuseAddress(boolean on) throws SocketException {
- socket.setReuseAddress(on);
- }
-
- @Override
- public boolean getReuseAddress() throws SocketException {
- return socket.getReuseAddress();
- }
-
- @Override
- public void close() throws IOException {
- try {
- socket.close();
- } finally {
- ses.shutdownNow();
- }
- }
-
- @Override
- public void shutdownInput() throws IOException {
- socket.shutdownInput();
- }
-
- @Override
- public void shutdownOutput() throws IOException {
- socket.shutdownOutput();
- }
-
- @Override
- public String toString() {
- return socket.toString();
- }
-
- @Override
- public boolean isConnected() {
- return socket.isConnected();
- }
-
- @Override
- public boolean isBound() {
- return socket.isBound();
- }
-
- @Override
- public boolean isClosed() {
- return socket.isClosed();
- }
-
- @Override
- public boolean isInputShutdown() {
- return socket.isInputShutdown();
- }
-
- @Override
- public boolean isOutputShutdown() {
- return socket.isOutputShutdown();
- }
-
- /*
- * The following three methods were added to java.net.Socket in Java SE 9.
- * Since they're not supported on Android, and since we know that we
- * never use them in Jakarta Mail, we just stub them out here.
- */
- //@Override
- public Socket setOption(SocketOption so, T val) throws IOException {
- // socket.setOption(so, val);
- // return this;
- throw new UnsupportedOperationException("WriteTimeoutSocket.setOption");
- }
-
- //@Override
- public T getOption(SocketOption so) throws IOException {
- // return socket.getOption(so);
- throw new UnsupportedOperationException("WriteTimeoutSocket.getOption");
- }
-
- //@Override
- public Set> supportedOptions() {
- // return socket.supportedOptions();
- return Collections.emptySet();
- }
-
- /**
- * KLUDGE for Android, which has this illegal non-Java Compatible method.
- *
- * @return the FileDescriptor object
- */
- public FileDescriptor getFileDescriptor$() {
- try {
- Method m = Socket.class.getDeclaredMethod("getFileDescriptor$");
- return (FileDescriptor)m.invoke(socket);
- } catch (Exception ex) {
- return null;
- }
- }
-}
-
-
-/**
- * An OutputStream that wraps the Socket's OutputStream and uses
- * the ScheduledExecutorService to schedule a task to close the
- * socket (aborting the write) if the timeout expires.
- */
-class TimeoutOutputStream extends OutputStream {
- private final OutputStream os;
- private final ScheduledExecutorService ses;
- private final Callable timeoutTask;
- private final int timeout;
- private byte[] b1;
-
- public TimeoutOutputStream(OutputStream os0, ScheduledExecutorService ses,
- int timeout) throws IOException {
- this.os = os0;
- this.ses = ses;
- this.timeout = timeout;
- timeoutTask = new Callable() {
- @Override
- public Object call() throws Exception {
- os.close(); // close the stream to abort the write
- return null;
- }
- };
- }
-
- @Override
- public synchronized void write(int b) throws IOException {
- if (b1 == null)
- b1 = new byte[1];
- b1[0] = (byte)b;
- this.write(b1);
- }
-
- @Override
- public synchronized void write(byte[] bs, int off, int len)
- throws IOException {
- if ((off < 0) || (off > bs.length) || (len < 0) ||
- ((off + len) > bs.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
- return;
- }
-
- // Implement timeout with a scheduled task
- ScheduledFuture sf = null;
- try {
- try {
- if (timeout > 0)
- sf = ses.schedule(timeoutTask,
- timeout, TimeUnit.MILLISECONDS);
- } catch (RejectedExecutionException ex) {
- // ignore it; Executor was shut down by another thread,
- // the following write should fail with IOException
- }
- os.write(bs, off, len);
- } finally {
- if (sf != null)
- sf.cancel(true);
- }
- }
-
- @Override
- public void close() throws IOException {
- os.close();
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java b/app/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
deleted file mode 100644
index d2d889c215..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, 2019 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-package com.sun.mail.util.logging;
-
-import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.text.MessageFormat;
-import java.util.Comparator;
-import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-
-/**
- * A LogRecord formatter that takes a sequence of LogRecords and combines them
- * into a single summary result. Formating of the head, LogRecord, and tail are
- * delegated to the wrapped formatter.
- *
- *
- * By default each CollectorFormatter is initialized using the
- * following LogManager configuration properties where
- * <formatter-name> refers to the fully qualified class name
- * or the fully qualified derived class name of the formatter. If properties
- * are not defined, or contain invalid values, then the specified default values
- * are used.
- *
- * <formatter-name>.comparator name of a
- * {@linkplain java.util.Comparator} class used to choose the collected
- * LogRecord. If a comparator is specified then the max
- * LogRecord is chosen. If comparator is set to the string literal
- * null, then the last record is chosen. (defaults to
- * {@linkplain SeverityComparator})
- *
- * <formatter-name>.comparator.reverse a boolean
- * true to collect the min LogRecord or
- * false to collect the max LogRecord.
- * (defaults to false)
- *
- * <formatter-name>.format the
- * {@linkplain java.text.MessageFormat MessageFormat} string used to format the
- * collected summary statistics. The arguments are explained in detail in the
- * {@linkplain #getTail(java.util.logging.Handler) getTail} documentation.
- * (defaults to {0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer}
- * more}\n)
- *
- * <formatter-name>.formatter name of a Formatter class
- * used to format the collected LogRecord.
- * (defaults to {@linkplain CompactFormatter})
- *
- *
- *
- * @author Jason Mehrens
- * @since JavaMail 1.5.2
- */
-public class CollectorFormatter extends Formatter {
- /**
- * Avoid depending on JMX runtime bean to get the start time.
- */
- private static final long INIT_TIME = System.currentTimeMillis();
- /**
- * The message format string used as the formatted output.
- */
- private final String fmt;
- /**
- * The formatter used to format the chosen log record.
- */
- private final Formatter formatter;
- /**
- * The comparator used to pick the log record to format.
- */
- private final Comparator super LogRecord> comparator;
- /**
- * The last accepted record. Synchronized access is preferred over volatile
- * for this class.
- */
- private LogRecord last;
- /**
- * The number of log records that have been formatted.
- */
- private long count;
- /**
- * The number of log produced containing at least one log record.
- * Only incremented when this formatter is reset.
- */
- private long generation = 1L;
- /**
- * The number of log records that have been formatted with a thrown object.
- */
- private long thrown;
- /**
- * The eldest log record time or eldest time possible for this instance.
- */
- private long minMillis = INIT_TIME;
- /**
- * The newest log record time.
- */
- private long maxMillis = Long.MIN_VALUE;
-
- /**
- * Creates the formatter using the LogManager defaults.
- *
- * @throws SecurityException if a security manager exists and the caller
- * does not have LoggingPermission("control").
- * @throws UndeclaredThrowableException if there are problems loading from
- * the LogManager.
- */
- public CollectorFormatter() {
- final String p = getClass().getName();
- this.fmt = initFormat(p);
- this.formatter = initFormatter(p);
- this.comparator = initComparator(p);
- }
-
- /**
- * Creates the formatter using the given format.
- *
- * @param format the message format or null to use the LogManager default.
- * @throws SecurityException if a security manager exists and the caller
- * does not have LoggingPermission("control").
- * @throws UndeclaredThrowableException if there are problems loading from
- * the LogManager.
- */
- public CollectorFormatter(String format) {
- final String p = getClass().getName();
- this.fmt = format == null ? initFormat(p) : format;
- this.formatter = initFormatter(p);
- this.comparator = initComparator(p);
- }
-
- /**
- * Creates the formatter using the given values.
- *
- * @param format the format string or null to use the LogManager default.
- * @param f the formatter used on the collected log record or null to
- * specify no formatter.
- * @param c the comparator used to determine which log record to format or
- * null to specify no comparator.
- * @throws SecurityException if a security manager exists and the caller
- * does not have LoggingPermission("control").
- * @throws UndeclaredThrowableException if there are problems loading from
- * the LogManager.
- */
- public CollectorFormatter(String format, Formatter f,
- Comparator super LogRecord> c) {
- final String p = getClass().getName();
- this.fmt = format == null ? initFormat(p) : format;
- this.formatter = f;
- this.comparator = c;
- }
-
- /**
- * Accumulates log records which will be used to produce the final output.
- * The output is generated using the {@link #getTail} method which also
- * resets this formatter back to its original state.
- *
- * @param record the record to store.
- * @return an empty string.
- * @throws NullPointerException if the given record is null.
- */
- @Override
- public String format(final LogRecord record) {
- if (record == null) {
- throw new NullPointerException();
- }
-
- boolean accepted;
- do {
- final LogRecord peek = peek();
- //The self compare of the first record acts like a type check.
- LogRecord update = apply(peek != null ? peek : record, record);
- if (peek != update) { //Not identical.
- update.getSourceMethodName(); //Infer caller, null check.
- accepted = acceptAndUpdate(peek, update);
- } else {
- accepted = accept(peek, record);
- }
- } while (!accepted);
- return "";
- }
-
- /**
- * Formats the collected LogRecord and summary statistics. The collected
- * results are reset after calling this method. The
- * {@linkplain java.text.MessageFormat java.text} argument indexes are
- * assigned to the following properties:
- *
- *
- * {@code head} the
- * {@linkplain Formatter#getHead(java.util.logging.Handler) head} string
- * returned from the target formatter and
- * {@linkplain #finish(java.lang.String) finished} by this formatter.
- * {@code formatted} the current log record
- * {@linkplain Formatter#format(java.util.logging.LogRecord) formatted} by
- * the target formatter and {@linkplain #finish(java.lang.String) finished}
- * by this formatter. If the formatter is null then record is formatted by
- * this {@linkplain #formatMessage(java.util.logging.LogRecord) formatter}.
- * {@code tail} the
- * {@linkplain Formatter#getTail(java.util.logging.Handler) tail} string
- * returned from the target formatter and
- * {@linkplain #finish(java.lang.String) finished} by this formatter.
- * {@code count} the total number of log records
- * {@linkplain #format consumed} by this formatter.
- * {@code remaining} the count minus one.
- * {@code thrown} the total number of log records
- * {@linkplain #format consumed} by this formatter with an assigned
- * {@linkplain java.util.logging.LogRecord#getThrown throwable}.
- * {@code normal messages} the count minus the thrown.
- * {@code minMillis} the eldest log record
- * {@linkplain java.util.logging.LogRecord#getMillis event time}
- * {@linkplain #format consumed} by this formatter. If the count is zero
- * then this is set to the previous max or approximate start time if there
- * was no previous max. By default this parameter is defined as a number.
- * The format type and format style rules from the
- * {@linkplain java.text.MessageFormat} should be used to convert this from
- * milliseconds to a date or time.
- * {@code maxMillis} the most recent log record
- * {@linkplain java.util.logging.LogRecord#getMillis event time}
- * {@linkplain #format consumed} by this formatter. If the count is zero
- * then this is set to the {@linkplain System#currentTimeMillis() current time}.
- * By default this parameter is defined as a number. The format type and
- * format style rules from the {@linkplain java.text.MessageFormat} should
- * be used to convert this from milliseconds to a date or time.
- * {@code elapsed} the elapsed time in milliseconds between the
- * {@code maxMillis} and {@code minMillis}.
- * {@code startTime} the approximate start time in milliseconds. By
- * default this parameter is defined as a number. The format type and format
- * style rules from the {@linkplain java.text.MessageFormat} should be used
- * to convert this from milliseconds to a date or time.
- * {@code currentTime} the
- * {@linkplain System#currentTimeMillis() current time} in milliseconds. By
- * default this parameter is defined as a number. The format type and format
- * style rules from the {@linkplain java.text.MessageFormat} should be used
- * to convert this from milliseconds to a date or time.
- * {@code uptime} the elapsed time in milliseconds between the
- * {@code currentTime} and {@code startTime}.
- * {@code generation} the number times this method produced output with
- * at least one {@linkplain #format consumed} log record. This can be used
- * to track the number of complete reports this formatter has produced.
- *
- *
- *
- * Some example formats:
- *
- * {@code com.sun.mail.util.logging.CollectorFormatter.format={0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}\n}
- *
- * This prints the head ({@code {0}}), format ({@code {1}}), and tail
- * ({@code {2}}) from the target formatter followed by the number of
- * remaining ({@code {4}}) log records consumed by this formatter if there
- * are any remaining records.
- *
- * Encoding failed.|NullPointerException: null String.getBytes(:913)... 3 more
- *
- * {@code com.sun.mail.util.logging.CollectorFormatter.format=These {3} messages occurred between\n{7,date,EEE, MMM dd HH:mm:ss:S ZZZ yyyy} and {8,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy}\n}
- *
- * This prints the count ({@code {3}}) followed by the date and time of the
- * eldest log record ({@code {7}}) and the date and time of the most recent
- * log record ({@code {8}}).
- *
- * These 292 messages occurred between
- * Tue, Jul 21 14:11:42:449 -0500 2009 and Fri, Nov 20 07:29:24:0 -0600 2009
- *
- * {@code com.sun.mail.util.logging.CollectorFormatter.format=These {3} messages occurred between {9,choice,86400000#{7,date} {7,time} and {8,time}|86400000<{7,date} and {8,date}}\n}
- *
- * This prints the count ({@code {3}}) and then chooses the format based on
- * the elapsed time ({@code {9}}). If the elapsed time is less than one day
- * then the eldest log record ({@code {7}}) date and time is formatted
- * followed by just the time of the most recent log record ({@code {8}}.
- * Otherwise, the just the date of the eldest log record ({@code {7}}) and
- * just the date of most recent log record ({@code {8}} is formatted.
- *
- * These 73 messages occurred between Jul 21, 2009 2:11:42 PM and 2:13:32 PM
- *
- * These 116 messages occurred between Jul 21, 2009 and Aug 20, 2009
- *
- * {@code com.sun.mail.util.logging.CollectorFormatter.format={13} alert reports since {10,date}.\n}
- *
- * This prints the generation ({@code {13}}) followed by the start time
- * ({@code {10}}) formatted as a date.
- *
- * 4,320 alert reports since Jul 21, 2012.
- *
- *
- *
- * @param h the handler or null.
- * @return the output string.
- */
- @Override
- public String getTail(final Handler h) {
- super.getTail(h); //Be forward compatible with super.getHead.
- return formatRecord(h, true);
- }
-
- /**
- * Formats the collected LogRecord and summary statistics. The LogRecord and
- * summary statistics are not changed by calling this method.
- *
- * @return the current record formatted or the default toString.
- * @see #getTail(java.util.logging.Handler)
- */
- @Override
- public String toString() {
- String result;
- try {
- result = formatRecord((Handler) null, false);
- } catch (final RuntimeException ignore) {
- result = super.toString();
- }
- return result;
- }
-
- /**
- * Used to choose the collected LogRecord. This implementation returns the
- * greater of two LogRecords.
- *
- * @param t the current record.
- * @param u the record that could replace the current.
- * @return the greater of the given log records.
- * @throws NullPointerException may occur if either record is null.
- */
- protected LogRecord apply(final LogRecord t, final LogRecord u) {
- if (t == null || u == null) {
- throw new NullPointerException();
- }
-
- if (comparator != null) {
- return comparator.compare(t, u) >= 0 ? t : u;
- } else {
- return u;
- }
- }
-
- /**
- * Updates the summary statistics only if the expected record matches the
- * last record. The update record is not stored.
- *
- * @param e the LogRecord that is expected.
- * @param u the LogRecord used to collect statistics.
- * @return true if the last record was the expected record.
- * @throws NullPointerException if the update record is null.
- */
- private synchronized boolean accept(final LogRecord e, final LogRecord u) {
- /**
- * LogRecord methods must be called before the check of the last stored
- * record to guard against subclasses of LogRecord that might attempt to
- * reset the state by triggering a call to getTail.
- */
- final long millis = u.getMillis(); //Null check.
- final Throwable ex = u.getThrown();
- if (last == e) { //Only if the exact same reference.
- if (++count != 1L) {
- minMillis = Math.min(minMillis, millis);
- } else { //Show single records as instant and not a time period.
- minMillis = millis;
- }
- maxMillis = Math.max(maxMillis, millis);
-
- if (ex != null) {
- ++thrown;
- }
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Resets all of the collected summary statistics including the LogRecord.
- * @param min the current min milliseconds.
- */
- private synchronized void reset(final long min) {
- if (last != null) {
- last = null;
- ++generation;
- }
-
- count = 0L;
- thrown = 0L;
- minMillis = min;
- maxMillis = Long.MIN_VALUE;
- }
-
- /**
- * Formats the given record with the head and tail.
- *
- * @param h the Handler or null.
- * @param reset true if the summary statistics and LogRecord should be reset
- * back to initial values.
- * @return the formatted string.
- * @see #getTail(java.util.logging.Handler)
- */
- private String formatRecord(final Handler h, final boolean reset) {
- final LogRecord record;
- final long c;
- final long t;
- final long g;
- long msl;
- long msh;
- long now;
- synchronized (this) {
- record = last;
- c = count;
- g = generation;
- t = thrown;
- msl = minMillis;
- msh = maxMillis;
- now = System.currentTimeMillis();
- if (c == 0L) {
- msh = now;
- }
-
- if (reset) { //BUG ID 6351685
- reset(msh);
- }
- }
-
- final String head;
- final String msg;
- final String tail;
- final Formatter f = this.formatter;
- if (f != null) {
- synchronized (f) {
- head = f.getHead(h);
- msg = record != null ? f.format(record) : "";
- tail = f.getTail(h);
- }
- } else {
- head = "";
- msg = record != null ? formatMessage(record) : "";
- tail = "";
- }
-
- Locale l = null;
- if (record != null) {
- ResourceBundle rb = record.getResourceBundle();
- l = rb == null ? null : rb.getLocale();
- }
-
- final MessageFormat mf;
- if (l == null) { //BUG ID 8039165
- mf = new MessageFormat(fmt);
- } else {
- mf = new MessageFormat(fmt, l);
- }
-
- /**
- * These arguments are described in the getTail documentation.
- */
- return mf.format(new Object[]{finish(head), finish(msg), finish(tail),
- c, (c - 1L), t, (c - t), msl, msh, (msh - msl), INIT_TIME, now,
- (now - INIT_TIME), g});
- }
-
- /**
- * Applied to the head, format, and tail returned by the target formatter.
- * This implementation trims all input strings.
- *
- * @param s the string to transform.
- * @return the transformed string.
- * @throws NullPointerException if the given string is null.
- */
- protected String finish(String s) {
- return s.trim();
- }
-
- /**
- * Peek at the current log record.
- *
- * @return null or the current log record.
- */
- private synchronized LogRecord peek() {
- return this.last;
- }
-
- /**
- * Updates the summary statistics and stores given LogRecord if the expected
- * record matches the current record.
- *
- * @param e the expected record.
- * @param u the update record.
- * @return true if the update was performed.
- * @throws NullPointerException if the update record is null.
- */
- private synchronized boolean acceptAndUpdate(LogRecord e, LogRecord u) {
- if (accept(e, u)) {
- this.last = u;
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Gets the message format string from the LogManager or creates the default
- * message format string.
- *
- * @param p the class name prefix.
- * @return the format string.
- * @throws NullPointerException if the given argument is null.
- */
- private String initFormat(final String p) {
- String v = fromLogManager(p.concat(".format"));
- if (v == null || v.length() == 0) {
- v = "{0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}\n";
- }
- return v;
- }
-
- /**
- * Gets and creates the formatter from the LogManager or creates the default
- * formatter.
- *
- * @param p the class name prefix.
- * @return the formatter.
- * @throws NullPointerException if the given argument is null.
- * @throws UndeclaredThrowableException if the formatter can not be created.
- */
- private Formatter initFormatter(final String p) {
- Formatter f;
- String v = fromLogManager(p.concat(".formatter"));
- if (v != null && v.length() != 0) {
- if (!"null".equalsIgnoreCase(v)) {
- try {
- f = LogManagerProperties.newFormatter(v);
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception e) {
- throw new UndeclaredThrowableException(e);
- }
- } else {
- f = null;
- }
- } else {
- //Don't force the byte code verifier to load the formatter.
- f = Formatter.class.cast(new CompactFormatter());
- }
- return f;
- }
-
- /**
- * Gets and creates the comparator from the LogManager or returns the
- * default comparator.
- *
- * @param p the class name prefix.
- * @return the comparator or null.
- * @throws IllegalArgumentException if it was specified that the comparator
- * should be reversed but no initial comparator was specified.
- * @throws NullPointerException if the given argument is null.
- * @throws UndeclaredThrowableException if the comparator can not be
- * created.
- */
- @SuppressWarnings("unchecked")
- private Comparator super LogRecord> initComparator(final String p) {
- Comparator super LogRecord> c;
- final String name = fromLogManager(p.concat(".comparator"));
- final String reverse = fromLogManager(p.concat(".comparator.reverse"));
- try {
- if (name != null && name.length() != 0) {
- if (!"null".equalsIgnoreCase(name)) {
- c = LogManagerProperties.newComparator(name);
- if (Boolean.parseBoolean(reverse)) {
- assert c != null;
- c = LogManagerProperties.reverseOrder(c);
- }
- } else {
- if (reverse != null) {
- throw new IllegalArgumentException(
- "No comparator to reverse.");
- } else {
- c = null; //No ordering.
- }
- }
- } else {
- if (reverse != null) {
- throw new IllegalArgumentException(
- "No comparator to reverse.");
- } else {
- //Don't force the byte code verifier to load the comparator.
- c = Comparator.class.cast(SeverityComparator.getInstance());
- }
- }
- } catch (final RuntimeException re) {
- throw re; //Avoid catch all.
- } catch (final Exception e) {
- throw new UndeclaredThrowableException(e);
- }
- return c;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/logging/CompactFormatter.java b/app/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
deleted file mode 100644
index 7a1a2fb961..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
+++ /dev/null
@@ -1,865 +0,0 @@
-/*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, 2019 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-package com.sun.mail.util.logging;
-
-import java.util.*;
-import java.util.logging.LogRecord;
-
-/**
- * A plain text formatter that can produce fixed width output. By default this
- * formatter will produce output no greater than 160 characters wide plus the
- * separator and newline characters. Only specified fields support an
- * {@linkplain #toAlternate(java.lang.String) alternate} fixed width format.
- *
- * By default each CompactFormatter is initialized using the
- * following LogManager configuration properties where
- * <formatter-name> refers to the fully qualified class name
- * or the fully qualified derived class name of the formatter. If properties are
- * not defined, or contain invalid values, then the specified default values are
- * used.
- *
- * <formatter-name>.format - the {@linkplain java.util.Formatter
- * format} string used to transform the output. The format string can be
- * used to fix the output size. (defaults to %7$#.160s%n)
- *
- *
- * @author Jason Mehrens
- * @since JavaMail 1.5.2
- */
-public class CompactFormatter extends java.util.logging.Formatter {
-
- /**
- * Load any declared classes to workaround GLASSFISH-21258.
- */
- static {
- loadDeclaredClasses();
- }
-
- /**
- * Used to load declared classes encase class loader doesn't allow loading
- * during JVM termination. This method is used with unit testing.
- *
- * @return an array of classes never null.
- */
- private static Class>[] loadDeclaredClasses() {
- return new Class>[]{Alternate.class};
- }
-
- /**
- * Holds the java.util.Formatter pattern.
- */
- private final String fmt;
-
- /**
- * Creates an instance with a default format pattern.
- */
- public CompactFormatter() {
- String p = getClass().getName();
- this.fmt = initFormat(p);
- }
-
- /**
- * Creates an instance with the given format pattern.
- *
- * @param format the {@linkplain java.util.Formatter pattern} or null to use
- * the LogManager default. The arguments are described in the
- * {@linkplain #format(java.util.logging.LogRecord) format} method.
- */
- public CompactFormatter(final String format) {
- String p = getClass().getName();
- this.fmt = format == null ? initFormat(p) : format;
- }
-
- /**
- * Format the given log record and returns the formatted string. The
- * {@linkplain java.util.Formatter#format(java.lang.String, java.lang.Object...)
- * java.util} argument indexes are assigned to the following properties:
- *
- *
- * {@code format} - the {@linkplain java.util.Formatter
- * java.util.Formatter} format string specified in the
- * <formatter-name>.format property or the format that was given when
- * this formatter was created.
- * {@code date} - if the log record supports nanoseconds then a
- * ZonedDateTime object representing the event time of the log record in the
- * system time zone. Otherwise, a {@linkplain Date} object representing
- * {@linkplain LogRecord#getMillis event time} of the log record.
- * {@code source} - a string representing the caller, if available;
- * otherwise, the logger's name.
- * {@code logger} - the logger's
- * {@linkplain Class#getSimpleName() simple}
- * {@linkplain LogRecord#getLoggerName() name}.
- * {@code level} - the
- * {@linkplain java.util.logging.Level#getLocalizedName log level}.
- * {@code message} - the formatted log message returned from the
- * {@linkplain #formatMessage(LogRecord)} method.
- * {@code thrown} - a string representing the
- * {@linkplain LogRecord#getThrown throwable} associated with the log record
- * and a relevant stack trace element if available; otherwise, an empty
- * string is used.
- * {@code message|thrown} The message and the thrown properties joined
- * as one parameter. This parameter supports
- * {@linkplain #toAlternate(java.lang.String) alternate} form.
- * {@code thrown|message} The thrown and message properties joined as
- * one parameter. This parameter supports
- * {@linkplain #toAlternate(java.lang.String) alternate} form.
- * {@code sequence} the
- * {@linkplain LogRecord#getSequenceNumber() sequence number} if the given
- * log record.
- * {@code thread id} the {@linkplain LogRecord#getThreadID() thread id}
- * of the given log record. By default this is formatted as a {@code long}
- * by an unsigned conversion.
- * {@code error} the throwable
- * {@linkplain Class#getSimpleName() simple class name} and
- * {@linkplain #formatError(LogRecord) error message} without any stack
- * trace.
- * {@code message|error} The message and error properties joined as one
- * parameter. This parameter supports
- * {@linkplain #toAlternate(java.lang.String) alternate} form.
- * {@code error|message} The error and message properties joined as one
- * parameter. This parameter supports
- * {@linkplain #toAlternate(java.lang.String) alternate} form.
- * {@code backtrace} only the
- * {@linkplain #formatBackTrace(LogRecord) stack trace} of the given
- * throwable.
- * {@code bundlename} the resource bundle
- * {@linkplain LogRecord#getResourceBundleName() name} of the given log
- * record.
- * {@code key} the {@linkplain LogRecord#getMessage() raw message}
- * before localization or formatting.
- *
- *
- *
- * Some example formats:
- *
- * {@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n}
- *
- * This prints only 160 characters of the message|thrown ({@code 7$}) using
- * the {@linkplain #toAlternate(java.lang.String) alternate} form. The
- * separator is not included as part of the total width.
- *
- * Encoding failed.|NullPointerException: null String.getBytes(:913)
- *
- *
- * {@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n}
- *
- * This prints only 20 characters of the message|thrown ({@code 7$}) using
- * the {@linkplain #toAlternate(java.lang.String) alternate} form. This will
- * perform a weighted truncation of both the message and thrown properties
- * of the log record. The separator is not included as part of the total
- * width.
- *
- * Encoding|NullPointerE
- *
- *
- * {@code com.sun.mail.util.logging.CompactFormatter.format=%1$tc %2$s%n%4$s: %5$s%6$s%n}
- *
- * This prints the timestamp ({@code 1$}) and the source ({@code 2$}) on the
- * first line. The second line is the log level ({@code 4$}), log message
- * ({@code 5$}), and the throwable with a relevant stack trace element
- * ({@code 6$}) if one is available.
- *
- * Fri Nov 20 07:29:24 CST 2009 MyClass fatal
- * SEVERE: Encoding failed.NullPointerException: null String.getBytes(:913)
- *
- *
- * {@code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n}
- *
- * This prints the log level ({@code 4$}) and only 160 characters of the
- * message|error ({@code 12$}) using the alternate form.
- *
- * SEVERE: Unable to send notification.|SocketException: Permission denied: connect
- *
- *
- * {@code com.sun.mail.util.logging.CompactFormatter.format=[%9$d][%1$tT][%10$d][%2$s] %5$s%n%6$s%n}
- *
- * This prints the sequence ({@code 9$}), event time ({@code 1$}) as 24 hour
- * time, thread id ({@code 10$}), source ({@code 2$}), log message
- * ({@code 5$}), and the throwable with back trace ({@code 6$}).
- *
- * [125][14:11:42][38][MyClass fatal] Unable to send notification.
- * SocketException: Permission denied: connect SMTPTransport.openServer(:1949)
- *
- *
- *
- *
- * @param record the log record to format.
- * @return the formatted record.
- * @throws NullPointerException if the given record is null.
- */
- @Override
- public String format(final LogRecord record) {
- //LogRecord is mutable so define local vars.
- ResourceBundle rb = record.getResourceBundle();
- Locale l = rb == null ? null : rb.getLocale();
-
- String msg = formatMessage(record);
- String thrown = formatThrown(record);
- String err = formatError(record);
- Object[] params = {
- formatZonedDateTime(record),
- formatSource(record),
- formatLoggerName(record),
- formatLevel(record),
- msg,
- thrown,
- new Alternate(msg, thrown),
- new Alternate(thrown, msg),
- record.getSequenceNumber(),
- formatThreadID(record),
- err,
- new Alternate(msg, err),
- new Alternate(err, msg),
- formatBackTrace(record),
- record.getResourceBundleName(),
- record.getMessage()};
-
- if (l == null) { //BUG ID 6282094
- return String.format(fmt, params);
- } else {
- return String.format(l, fmt, params);
- }
- }
-
- /**
- * Formats message for the log record. This method removes any fully
- * qualified throwable class names from the message.
- *
- * @param record the log record.
- * @return the formatted message string.
- */
- @Override
- public String formatMessage(final LogRecord record) {
- String msg = super.formatMessage(record);
- msg = replaceClassName(msg, record.getThrown());
- msg = replaceClassName(msg, record.getParameters());
- return msg;
- }
-
- /**
- * Formats the message from the thrown property of the log record. This
- * method replaces fully qualified throwable class names from the message
- * cause chain with simple class names.
- *
- * @param t the throwable to format or null.
- * @return the empty string if null was given or the formatted message
- * string from the throwable which may be null.
- */
- public String formatMessage(final Throwable t) {
- String r;
- if (t != null) {
- final Throwable apply = apply(t);
- final String m = apply.getLocalizedMessage();
- final String s = apply.toString();
- final String sn = simpleClassName(apply.getClass());
- if (!isNullOrSpaces(m)) {
- if (s.contains(m)) {
- if (s.startsWith(apply.getClass().getName())
- || s.startsWith(sn)) {
- r = replaceClassName(m, t);
- } else {
- r = replaceClassName(simpleClassName(s), t);
- }
- } else {
- r = replaceClassName(simpleClassName(s) + ": " + m, t);
- }
- } else {
- r = replaceClassName(simpleClassName(s), t);
- }
-
- if (!r.contains(sn)) {
- r = sn + ": " + r;
- }
- } else {
- r = "";
- }
- return r;
- }
-
- /**
- * Formats the level property of the given log record.
- *
- * @param record the record.
- * @return the formatted logger name.
- * @throws NullPointerException if the given record is null.
- */
- public String formatLevel(final LogRecord record) {
- return record.getLevel().getLocalizedName();
- }
-
- /**
- * Formats the source from the given log record.
- *
- * @param record the record.
- * @return the formatted source of the log record.
- * @throws NullPointerException if the given record is null.
- */
- public String formatSource(final LogRecord record) {
- String source = record.getSourceClassName();
- if (source != null) {
- if (record.getSourceMethodName() != null) {
- source = simpleClassName(source) + " "
- + record.getSourceMethodName();
- } else {
- source = simpleClassName(source);
- }
- } else {
- source = simpleClassName(record.getLoggerName());
- }
- return source;
- }
-
- /**
- * Formats the logger name property of the given log record.
- *
- * @param record the record.
- * @return the formatted logger name.
- * @throws NullPointerException if the given record is null.
- */
- public String formatLoggerName(final LogRecord record) {
- return simpleClassName(record.getLoggerName());
- }
-
- /**
- * Formats the thread id property of the given log record. By default this
- * is formatted as a {@code long} by an unsigned conversion.
- *
- * @param record the record.
- * @return the formatted thread id as a number.
- * @throws NullPointerException if the given record is null.
- * @since JavaMail 1.5.4
- */
- public Number formatThreadID(final LogRecord record) {
- /**
- * Thread.getID is defined as long and LogRecord.getThreadID is defined
- * as int. Convert to unsigned as a means to better map the two types of
- * thread identifiers.
- */
- return (((long) record.getThreadID()) & 0xffffffffL);
- }
-
- /**
- * Formats the thrown property of a LogRecord. The returned string will
- * contain a throwable message with a back trace.
- *
- * @param record the record.
- * @return empty string if nothing was thrown or formatted string.
- * @throws NullPointerException if the given record is null.
- * @see #apply(java.lang.Throwable)
- * @see #formatBackTrace(java.util.logging.LogRecord)
- */
- public String formatThrown(final LogRecord record) {
- String msg;
- final Throwable t = record.getThrown();
- if (t != null) {
- String site = formatBackTrace(record);
- msg = formatMessage(t) + (isNullOrSpaces(site) ? "" : ' ' + site);
- } else {
- msg = "";
- }
- return msg;
- }
-
- /**
- * Formats the thrown property of a LogRecord as an error message. The
- * returned string will not contain a back trace.
- *
- * @param record the record.
- * @return empty string if nothing was thrown or formatted string.
- * @throws NullPointerException if the given record is null.
- * @see Throwable#toString()
- * @see #apply(java.lang.Throwable)
- * @see #formatMessage(java.lang.Throwable)
- * @since JavaMail 1.5.4
- */
- public String formatError(final LogRecord record) {
- return formatMessage(record.getThrown());
- }
-
- /**
- * Formats the back trace for the given log record.
- *
- * @param record the log record to format.
- * @return the formatted back trace.
- * @throws NullPointerException if the given record is null.
- * @see #apply(java.lang.Throwable)
- * @see #formatThrown(java.util.logging.LogRecord)
- * @see #ignore(java.lang.StackTraceElement)
- */
- public String formatBackTrace(final LogRecord record) {
- String site = "";
- final Throwable t = record.getThrown();
- if (t != null) {
- final Throwable root = apply(t);
- StackTraceElement[] trace = root.getStackTrace();
- site = findAndFormat(trace);
- if (isNullOrSpaces(site)) {
- int limit = 0;
- for (Throwable c = t; c != null; c = c.getCause()) {
- StackTraceElement[] ste = c.getStackTrace();
- site = findAndFormat(ste);
- if (!isNullOrSpaces(site)) {
- break;
- } else {
- if (trace.length == 0) {
- trace = ste;
- }
- }
-
- //Deal with excessive cause chains
- //and cyclic throwables.
- if (++limit == (1 << 16)) {
- break; //Give up.
- }
- }
-
- //Punt.
- if (isNullOrSpaces(site) && trace.length != 0) {
- site = formatStackTraceElement(trace[0]);
- }
- }
- }
- return site;
- }
-
- /**
- * Finds and formats the first stack frame of interest.
- *
- * @param trace the fill stack to examine.
- * @return a String that best describes the call site.
- * @throws NullPointerException if stack trace element array is null.
- */
- private String findAndFormat(final StackTraceElement[] trace) {
- String site = "";
- for (StackTraceElement s : trace) {
- if (!ignore(s)) {
- site = formatStackTraceElement(s);
- break;
- }
- }
-
- //Check if all code was compiled with no debugging info.
- if (isNullOrSpaces(site)) {
- for (StackTraceElement s : trace) {
- if (!defaultIgnore(s)) {
- site = formatStackTraceElement(s);
- break;
- }
- }
- }
- return site;
- }
-
- /**
- * Formats a stack trace element into a simple call site.
- *
- * @param s the stack trace element to format.
- * @return the formatted stack trace element.
- * @throws NullPointerException if stack trace element is null.
- * @see #formatThrown(java.util.logging.LogRecord)
- */
- private String formatStackTraceElement(final StackTraceElement s) {
- String v = simpleClassName(s.getClassName());
- String result;
- if (v != null) {
- result = s.toString().replace(s.getClassName(), v);
- } else {
- result = s.toString();
- }
-
- //If the class name contains the simple file name then remove file name.
- v = simpleFileName(s.getFileName());
- if (v != null && result.startsWith(v)) {
- result = result.replace(s.getFileName(), "");
- }
- return result;
- }
-
- /**
- * Chooses a single throwable from the cause chain that will be formatted.
- * This implementation chooses the throwable that best describes the chain.
- * Subclasses can override this method to choose an alternate throwable for
- * formatting.
- *
- * @param t the throwable from the log record.
- * @return the chosen throwable or null only if the given argument is null.
- * @see #formatThrown(java.util.logging.LogRecord)
- */
- protected Throwable apply(final Throwable t) {
- return SeverityComparator.getInstance().apply(t);
- }
-
- /**
- * Determines if a stack frame should be ignored as the cause of an error.
- *
- * @param s the stack trace element.
- * @return true if this frame should be ignored.
- * @see #formatThrown(java.util.logging.LogRecord)
- */
- protected boolean ignore(final StackTraceElement s) {
- return isUnknown(s) || defaultIgnore(s);
- }
-
- /**
- * Defines the alternate format. This implementation removes all control
- * characters from the given string.
- *
- * @param s any string or null.
- * @return null if the argument was null otherwise, an alternate string.
- */
- protected String toAlternate(final String s) {
- return s != null ? s.replaceAll("[\\x00-\\x1F\\x7F]+", "") : null;
- }
-
- /**
- * Gets the zoned date time from the given log record.
- *
- * @param record the current log record.
- * @return a zoned date time or a legacy date object.
- * @throws NullPointerException if the given record is null.
- * @since JavaMail 1.5.6
- */
- private Comparable> formatZonedDateTime(final LogRecord record) {
- Comparable> zdt = LogManagerProperties.getZonedDateTime(record);
- if (zdt == null) {
- zdt = new java.util.Date(record.getMillis());
- }
- return zdt;
- }
-
- /**
- * Determines if a stack frame should be ignored as the cause of an error.
- * This does not check for unknown line numbers because code can be compiled
- * without debugging info.
- *
- * @param s the stack trace element.
- * @return true if this frame should be ignored.
- */
- private boolean defaultIgnore(final StackTraceElement s) {
- return isSynthetic(s) || isStaticUtility(s) || isReflection(s);
- }
-
- /**
- * Determines if a stack frame is for a static utility class.
- *
- * @param s the stack trace element.
- * @return true if this frame should be ignored.
- */
- private boolean isStaticUtility(final StackTraceElement s) {
- try {
- return LogManagerProperties.isStaticUtilityClass(s.getClassName());
- } catch (RuntimeException ignore) {
- } catch (Exception | LinkageError ignore) {
- }
- final String cn = s.getClassName();
- return (cn.endsWith("s") && !cn.endsWith("es"))
- || cn.contains("Util") || cn.endsWith("Throwables");
- }
-
- /**
- * Determines if a stack trace element is for a synthetic method.
- *
- * @param s the stack trace element.
- * @return true if synthetic.
- * @throws NullPointerException if stack trace element is null.
- */
- private boolean isSynthetic(final StackTraceElement s) {
- return s.getMethodName().indexOf('$') > -1;
- }
-
- /**
- * Determines if a stack trace element has an unknown line number or a
- * native line number.
- *
- * @param s the stack trace element.
- * @return true if the line number is unknown.
- * @throws NullPointerException if stack trace element is null.
- */
- private boolean isUnknown(final StackTraceElement s) {
- return s.getLineNumber() < 0;
- }
-
- /**
- * Determines if a stack trace element represents a reflection frame.
- *
- * @param s the stack trace element.
- * @return true if the line number is unknown.
- * @throws NullPointerException if stack trace element is null.
- */
- private boolean isReflection(final StackTraceElement s) {
- try {
- return LogManagerProperties.isReflectionClass(s.getClassName());
- } catch (RuntimeException ignore) {
- } catch (Exception | LinkageError ignore) {
- }
- return s.getClassName().startsWith("java.lang.reflect.")
- || s.getClassName().startsWith("sun.reflect.");
- }
-
- /**
- * Creates the format pattern for this formatter.
- *
- * @param p the class name prefix.
- * @return the java.util.Formatter format string.
- * @throws NullPointerException if the given class name is null.
- */
- private String initFormat(final String p) {
- String v = LogManagerProperties.fromLogManager(p.concat(".format"));
- if (isNullOrSpaces(v)) {
- v = "%7$#.160s%n"; //160 chars split between message and thrown.
- }
- return v;
- }
-
- /**
- * Searches the given message for all instances fully qualified class name
- * with simple class name based off of the types contained in the given
- * parameter array.
- *
- * @param msg the message.
- * @param t the throwable cause chain to search or null.
- * @return the modified message string.
- */
- private static String replaceClassName(String msg, Throwable t) {
- if (!isNullOrSpaces(msg)) {
- int limit = 0;
- for (Throwable c = t; c != null; c = c.getCause()) {
- final Class> k = c.getClass();
- msg = msg.replace(k.getName(), simpleClassName(k));
-
- //Deal with excessive cause chains and cyclic throwables.
- if (++limit == (1 << 16)) {
- break; //Give up.
- }
- }
- }
- return msg;
- }
-
- /**
- * Searches the given message for all instances fully qualified class name
- * with simple class name based off of the types contained in the given
- * parameter array.
- *
- * @param msg the message or null.
- * @param p the parameter array or null.
- * @return the modified message string.
- */
- private static String replaceClassName(String msg, Object[] p) {
- if (!isNullOrSpaces(msg) && p != null) {
- for (Object o : p) {
- if (o != null) {
- final Class> k = o.getClass();
- msg = msg.replace(k.getName(), simpleClassName(k));
- }
- }
- }
- return msg;
- }
-
- /**
- * Gets the simple class name from the given class. This is a workaround for
- * BUG ID JDK-8057919.
- *
- * @param k the class object.
- * @return the simple class name or null.
- * @since JavaMail 1.5.3
- */
- private static String simpleClassName(final Class> k) {
- try {
- return k.getSimpleName();
- } catch (final InternalError JDK8057919) {
- }
- return simpleClassName(k.getName());
- }
-
- /**
- * Converts a fully qualified class name to a simple class name. If the
- * leading part of the given string is not a legal class name then the given
- * string is returned.
- *
- * @param name the fully qualified class name prefix or null.
- * @return the simple class name or given input.
- */
- private static String simpleClassName(String name) {
- if (name != null) {
- int cursor = 0;
- int sign = -1;
- int dot = -1;
- for (int c, prev = dot; cursor < name.length();
- cursor += Character.charCount(c)) {
- c = name.codePointAt(cursor);
- if (!Character.isJavaIdentifierPart(c)) {
- if (c == ((int) '.')) {
- if ((dot + 1) != cursor && (dot + 1) != sign) {
- prev = dot;
- dot = cursor;
- } else {
- return name;
- }
- } else {
- if ((dot + 1) == cursor) {
- dot = prev;
- }
- break;
- }
- } else {
- if (c == ((int) '$')) {
- sign = cursor;
- }
- }
- }
-
- if (dot > -1 && ++dot < cursor && ++sign < cursor) {
- name = name.substring(sign > dot ? sign : dot);
- }
- }
- return name;
- }
-
- /**
- * Converts a file name with an extension to a file name without an
- * extension.
- *
- * @param name the full file name or null.
- * @return the simple file name or null.
- */
- private static String simpleFileName(String name) {
- if (name != null) {
- final int index = name.lastIndexOf('.');
- name = index > -1 ? name.substring(0, index) : name;
- }
- return name;
- }
-
- /**
- * Determines is the given string is null or spaces.
- *
- * @param s the string or null.
- * @return true if null or spaces.
- */
- private static boolean isNullOrSpaces(final String s) {
- return s == null || s.trim().length() == 0;
- }
-
- /**
- * Used to format two arguments as fixed length message.
- */
- private class Alternate implements java.util.Formattable {
-
- /**
- * The left side of the output.
- */
- private final String left;
- /**
- * The right side of the output.
- */
- private final String right;
-
- /**
- * Creates an alternate output.
- *
- * @param left the left side or null.
- * @param right the right side or null.
- */
- Alternate(final String left, final String right) {
- this.left = String.valueOf(left);
- this.right = String.valueOf(right);
- }
-
- @SuppressWarnings("override") //JDK-6954234
- public void formatTo(java.util.Formatter formatter, int flags,
- int width, int precision) {
-
- String l = left;
- String r = right;
- if ((flags & java.util.FormattableFlags.UPPERCASE)
- == java.util.FormattableFlags.UPPERCASE) {
- l = l.toUpperCase(formatter.locale());
- r = r.toUpperCase(formatter.locale());
- }
-
- if ((flags & java.util.FormattableFlags.ALTERNATE)
- == java.util.FormattableFlags.ALTERNATE) {
- l = toAlternate(l);
- r = toAlternate(r);
- }
-
- if (precision <= 0) {
- precision = Integer.MAX_VALUE;
- }
-
- int fence = Math.min(l.length(), precision);
- if (fence > (precision >> 1)) {
- fence = Math.max(fence - r.length(), fence >> 1);
- }
-
- if (fence > 0) {
- if (fence > l.length()
- && Character.isHighSurrogate(l.charAt(fence - 1))) {
- --fence;
- }
- l = l.substring(0, fence);
- }
- r = r.substring(0, Math.min(precision - fence, r.length()));
-
- if (width > 0) {
- final int half = width >> 1;
- if (l.length() < half) {
- l = pad(flags, l, half);
- }
-
- if (r.length() < half) {
- r = pad(flags, r, half);
- }
- }
-
- Object[] empty = Collections.emptySet().toArray();
- formatter.format(l, empty);
- if (l.length() != 0 && r.length() != 0) {
- formatter.format("|", empty);
- }
- formatter.format(r, empty);
- }
-
- /**
- * Pad the given input string.
- *
- * @param flags the formatter flags.
- * @param s the string to pad.
- * @param length the final string length.
- * @return the padded string.
- */
- private String pad(int flags, String s, int length) {
- final int padding = length - s.length();
- final StringBuilder b = new StringBuilder(length);
- if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY)
- == java.util.FormattableFlags.LEFT_JUSTIFY) {
- for (int i = 0; i < padding; ++i) {
- b.append('\u0020');
- }
- b.append(s);
- } else {
- b.append(s);
- for (int i = 0; i < padding; ++i) {
- b.append('\u0020');
- }
- }
- return b.toString();
- }
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/logging/DurationFilter.java b/app/src/main/java/com/sun/mail/util/logging/DurationFilter.java
deleted file mode 100644
index b85101468c..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/DurationFilter.java
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, 2018 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-package com.sun.mail.util.logging;
-
-import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager;
-import java.util.logging.*;
-
-/**
- * A filter used to limit log records based on a maximum generation rate.
- *
- * The duration specified is used to compute the record rate and the amount of
- * time the filter will reject records once the rate has been exceeded. Once the
- * rate is exceeded records are not allowed until the duration has elapsed.
- *
- *
- * By default each {@code DurationFilter} is initialized using the following
- * LogManager configuration properties where {@code } 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.
- *
- *
- * {@literal }.records the max number of records per duration.
- * A numeric long integer or a multiplication expression can be used as the
- * value. (defaults to {@code 1000})
- *
- * {@literal }.duration the number of milliseconds to suppress
- * log records from being published. This is also used as duration to determine
- * the log record rate. A numeric long integer or a multiplication expression
- * can be used as the value. If the {@code java.time} package is available then
- * an ISO-8601 duration format of {@code PnDTnHnMn.nS} can be used as the value.
- * The suffixes of "D", "H", "M" and "S" are for days, hours, minutes and
- * seconds. The suffixes must occur in order. The seconds can be specified with
- * a fractional component to declare milliseconds. (defaults to {@code PT15M})
- *
- *
- *
- * For example, the settings to limit {@code MailHandler} with a default
- * capacity to only send a maximum of two email messages every six minutes would
- * be as follows:
- *
- * {@code
- * com.sun.mail.util.logging.MailHandler.filter = com.sun.mail.util.logging.DurationFilter
- * com.sun.mail.util.logging.MailHandler.capacity = 1000
- * com.sun.mail.util.logging.DurationFilter.records = 2L * 1000L
- * com.sun.mail.util.logging.DurationFilter.duration = PT6M
- * }
- *
- *
- *
- * @author Jason Mehrens
- * @since JavaMail 1.5.5
- */
-public class DurationFilter implements Filter {
-
- /**
- * The number of expected records per duration.
- */
- private final long records;
- /**
- * The duration in milliseconds used to determine the rate. The duration is
- * also used as the amount of time that the filter will not allow records
- * when saturated.
- */
- private final long duration;
- /**
- * The number of records seen for the current duration. This value negative
- * if saturated. Zero is considered saturated but is reserved for recording
- * the first duration.
- */
- private long count;
- /**
- * The most recent record time seen for the current duration.
- */
- private long peak;
- /**
- * The start time for the current duration.
- */
- private long start;
-
- /**
- * Creates the filter using the default properties.
- */
- public DurationFilter() {
- this.records = checkRecords(initLong(".records"));
- this.duration = checkDuration(initLong(".duration"));
- }
-
- /**
- * Creates the filter using the given properties. Default values are used if
- * any of the given values are outside the allowed range.
- *
- * @param records the number of records per duration.
- * @param duration the number of milliseconds to suppress log records from
- * being published.
- */
- public DurationFilter(final long records, final long duration) {
- this.records = checkRecords(records);
- this.duration = checkDuration(duration);
- }
-
- /**
- * Determines if this filter is equal to another filter.
- *
- * @param obj the given object.
- * @return true if equal otherwise false.
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) { //Avoid locks and deal with rapid state changes.
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- final DurationFilter other = (DurationFilter) obj;
- if (this.records != other.records) {
- return false;
- }
-
- if (this.duration != other.duration) {
- return false;
- }
-
- final long c;
- final long p;
- final long s;
- synchronized (this) {
- c = this.count;
- p = this.peak;
- s = this.start;
- }
-
- synchronized (other) {
- if (c != other.count || p != other.peak || s != other.start) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determines if this filter is able to accept the maximum number of log
- * records for this instant in time. The result is a best-effort estimate
- * and should be considered out of date as soon as it is produced. This
- * method is designed for use in monitoring the state of this filter.
- *
- * @return true if the filter is idle; false otherwise.
- */
- public boolean isIdle() {
- return test(0L, System.currentTimeMillis());
- }
-
- /**
- * Returns a hash code value for this filter.
- *
- * @return hash code for this filter.
- */
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 89 * hash + (int) (this.records ^ (this.records >>> 32));
- hash = 89 * hash + (int) (this.duration ^ (this.duration >>> 32));
- return hash;
- }
-
- /**
- * Check if the given log record should be published. This method will
- * modify the internal state of this filter.
- *
- * @param record the log record to check.
- * @return true if allowed; false otherwise.
- * @throws NullPointerException if given record is null.
- */
- @SuppressWarnings("override") //JDK-6954234
- public boolean isLoggable(final LogRecord record) {
- return accept(record.getMillis());
- }
-
- /**
- * Determines if this filter will accept log records for this instant in
- * time. The result is a best-effort estimate and should be considered out
- * of date as soon as it is produced. This method is designed for use in
- * monitoring the state of this filter.
- *
- * @return true if the filter is not saturated; false otherwise.
- */
- public boolean isLoggable() {
- return test(records, System.currentTimeMillis());
- }
-
- /**
- * Returns a string representation of this filter. The result is a
- * best-effort estimate and should be considered out of date as soon as it
- * is produced.
- *
- * @return a string representation of this filter.
- */
- @Override
- public String toString() {
- boolean idle;
- boolean loggable;
- synchronized (this) {
- final long millis = System.currentTimeMillis();
- idle = test(0L, millis);
- loggable = test(records, millis);
- }
-
- return getClass().getName() + "{records=" + records
- + ", duration=" + duration
- + ", idle=" + idle
- + ", loggable=" + loggable + '}';
- }
-
- /**
- * Creates a copy of this filter that retains the filter settings but does
- * not include the current filter state. The newly create clone acts as if
- * it has never seen any records.
- *
- * @return a copy of this filter.
- * @throws CloneNotSupportedException if this filter is not allowed to be
- * cloned.
- */
- @Override
- protected DurationFilter clone() throws CloneNotSupportedException {
- final DurationFilter clone = (DurationFilter) super.clone();
- clone.count = 0L; //Reset the filter state.
- clone.peak = 0L;
- clone.start = 0L;
- return clone;
- }
-
- /**
- * Checks if this filter is not saturated or bellow a maximum rate.
- *
- * @param limit the number of records allowed to be under the rate.
- * @param millis the current time in milliseconds.
- * @return true if not saturated or bellow the rate.
- */
- private boolean test(final long limit, final long millis) {
- assert limit >= 0L : limit;
- final long c;
- final long s;
- synchronized (this) {
- c = count;
- s = start;
- }
-
- if (c > 0L) { //If not saturated.
- if ((millis - s) >= duration || c < limit) {
- return true;
- }
- } else { //Subtraction is used to deal with numeric overflow.
- if ((millis - s) >= 0L || c == 0L) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Determines if the record is loggable by time.
- *
- * @param millis the log record milliseconds.
- * @return true if accepted false otherwise.
- */
- private synchronized boolean accept(final long millis) {
- //Subtraction is used to deal with numeric overflow of millis.
- boolean allow;
- if (count > 0L) { //If not saturated.
- if ((millis - peak) > 0L) {
- peak = millis; //Record the new peak.
- }
-
- //Under the rate if the count has not been reached.
- if (count != records) {
- ++count;
- allow = true;
- } else {
- if ((peak - start) >= duration) {
- count = 1L; //Start a new duration.
- start = peak;
- allow = true;
- } else {
- count = -1L; //Saturate for the duration.
- start = peak + duration;
- allow = false;
- }
- }
- } else {
- //If the saturation period has expired or this is the first record
- //then start a new duration and allow records.
- if ((millis - start) >= 0L || count == 0L) {
- count = 1L;
- start = millis;
- peak = millis;
- allow = true;
- } else {
- allow = false; //Remain in a saturated state.
- }
- }
- return allow;
- }
-
- /**
- * Reads a long value or multiplication expression from the LogManager. If
- * the value can not be parsed or is not defined then Long.MIN_VALUE is
- * returned.
- *
- * @param suffix a dot character followed by the key name.
- * @return a long value or Long.MIN_VALUE if unable to parse or undefined.
- * @throws NullPointerException if suffix is null.
- */
- private long initLong(final String suffix) {
- long result = 0L;
- final String p = getClass().getName();
- String value = fromLogManager(p.concat(suffix));
- if (value != null && value.length() != 0) {
- value = value.trim();
- if (isTimeEntry(suffix, value)) {
- try {
- result = LogManagerProperties.parseDurationToMillis(value);
- } catch (final RuntimeException ignore) {
- } catch (final Exception ignore) {
- } catch (final LinkageError ignore) {
- }
- }
-
- if (result == 0L) { //Zero is invalid.
- try {
- result = 1L;
- for (String s : tokenizeLongs(value)) {
- if (s.endsWith("L") || s.endsWith("l")) {
- s = s.substring(0, s.length() - 1);
- }
- result = multiplyExact(result, Long.parseLong(s));
- }
- } catch (final RuntimeException ignore) {
- result = Long.MIN_VALUE;
- }
- }
- } else {
- result = Long.MIN_VALUE;
- }
- return result;
- }
-
- /**
- * Determines if the given suffix can be a time unit and the value is
- * encoded as an ISO ISO-8601 duration format.
- *
- * @param suffix the suffix property.
- * @param value the value of the property.
- * @return true if the entry is a time entry.
- * @throws IndexOutOfBoundsException if value is empty.
- * @throws NullPointerException if either argument is null.
- */
- private boolean isTimeEntry(final String suffix, final String value) {
- return (value.charAt(0) == 'P' || value.charAt(0) == 'p')
- && suffix.equals(".duration");
- }
-
- /**
- * Parse any long value or multiplication expressions into tokens.
- *
- * @param value the expression or value.
- * @return an array of long tokens, never empty.
- * @throws NullPointerException if the given value is null.
- * @throws NumberFormatException if the expression is invalid.
- */
- private static String[] tokenizeLongs(final String value) {
- String[] e;
- final int i = value.indexOf('*');
- if (i > -1 && (e = value.split("\\s*\\*\\s*")).length != 0) {
- if (i == 0 || value.charAt(value.length() - 1) == '*') {
- throw new NumberFormatException(value);
- }
-
- if (e.length == 1) {
- throw new NumberFormatException(e[0]);
- }
- } else {
- e = new String[]{value};
- }
- return e;
- }
-
- /**
- * Multiply and check for overflow. This can be replaced with
- * {@code java.lang.Math.multiplyExact} when Jakarta Mail requires JDK 8.
- *
- * @param x the first value.
- * @param y the second value.
- * @return x times y.
- * @throws ArithmeticException if overflow is detected.
- */
- private static long multiplyExact(final long x, final long y) {
- long r = x * y;
- if (((Math.abs(x) | Math.abs(y)) >>> 31L != 0L)) {
- if (((y != 0L) && (r / y != x))
- || (x == Long.MIN_VALUE && y == -1L)) {
- throw new ArithmeticException();
- }
- }
- return r;
- }
-
- /**
- * Converts record count to a valid record count. If the value is out of
- * bounds then the default record count is used.
- *
- * @param records the record count.
- * @return a valid number of record count.
- */
- private static long checkRecords(final long records) {
- return records > 0L ? records : 1000L;
- }
-
- /**
- * Converts the duration to a valid duration. If the value is out of bounds
- * then the default duration is used.
- *
- * @param duration the duration to check.
- * @return a valid duration.
- */
- private static long checkDuration(final long duration) {
- return duration > 0L ? duration : 15L * 60L * 1000L;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java b/app/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
deleted file mode 100644
index e2ec6c575e..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- * Copyright (c) 2009, 2019 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2009, 2019 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-package com.sun.mail.util.logging;
-
-import java.io.*;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.*;
-import java.util.logging.*;
-import java.util.logging.Formatter;
-
-/**
- * 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 {
-
- /**
- * Generated serial id.
- */
- private static final long serialVersionUID = -2239983349056806252L;
- /**
- * Holds the method used to get the LogRecord instant if running on JDK 9 or
- * later.
- */
- private static final Method LR_GET_INSTANT;
-
- /**
- * Holds the method used to get the default time zone if running on JDK 9 or
- * later.
- */
- private static final Method ZI_SYSTEM_DEFAULT;
-
- /**
- * Holds the method used to convert and instant to a zoned date time if
- * running on JDK 9 later.
- */
- private static final Method ZDT_OF_INSTANT;
-
- static {
- Method lrgi = null;
- Method zisd = null;
- Method zdtoi = null;
- try {
- lrgi = LogRecord.class.getMethod("getInstant");
- assert Comparable.class
- .isAssignableFrom(lrgi.getReturnType()) : lrgi;
- zisd = findClass("java.time.ZoneId")
- .getMethod("systemDefault");
- if (!Modifier.isStatic(zisd.getModifiers())) {
- throw new NoSuchMethodException(zisd.toString());
- }
-
- zdtoi = findClass("java.time.ZonedDateTime")
- .getMethod("ofInstant", findClass("java.time.Instant"),
- findClass("java.time.ZoneId"));
- if (!Modifier.isStatic(zdtoi.getModifiers())) {
- throw new NoSuchMethodException(zdtoi.toString());
- }
-
- if (!Comparable.class.isAssignableFrom(zdtoi.getReturnType())) {
- throw new NoSuchMethodException(zdtoi.toString());
- }
- } catch (final RuntimeException ignore) {
- } catch (final Exception ignore) { //No need for specific catch.
- } catch (final LinkageError ignore) {
- } finally {
- if (lrgi == null || zisd == null || zdtoi == null) {
- lrgi = null; //If any are null then clear all.
- zisd = null;
- zdtoi = null;
- }
- }
-
- LR_GET_INSTANT = lrgi;
- ZI_SYSTEM_DEFAULT = zisd;
- ZDT_OF_INSTANT = zdtoi;
- }
- /**
- * Caches the read only reflection class names string array. Declared
- * volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY
- * warning is a false positive.
- */
- @SuppressWarnings("VolatileArrayField")
- private static volatile String[] REFLECT_NAMES;
- /**
- * Caches the LogManager or Properties so we only read the configuration
- * once.
- */
- private static final Object LOG_MANAGER = loadLogManager();
-
- /**
- * Get the LogManager or loads a Properties object to use as the LogManager.
- *
- * @return the LogManager or a loaded Properties object.
- * @since JavaMail 1.5.3
- */
- private static Object loadLogManager() {
- Object m;
- try {
- m = LogManager.getLogManager();
- } catch (final LinkageError restricted) {
- m = readConfiguration();
- } catch (final RuntimeException unexpected) {
- m = readConfiguration();
- }
- return m;
- }
-
- /**
- * Create a properties object from the default logging configuration file.
- * Since the LogManager is not available in restricted environments, only
- * the default configuration is applicable.
- *
- * @return a properties object loaded with the default configuration.
- * @since JavaMail 1.5.3
- */
- private static Properties readConfiguration() {
- /**
- * Load the properties file so the default settings are available when
- * user code creates a logging object. The class loader for the
- * restricted LogManager can't access these classes to attach them to a
- * logger or handler on startup. Creating logging objects at this point
- * is both useless and risky.
- */
- final Properties props = new Properties();
- try {
- String n = System.getProperty("java.util.logging.config.file");
- if (n != null) {
- final File f = new File(n).getCanonicalFile();
- final InputStream in = new FileInputStream(f);
- try {
- props.load(in);
- } finally {
- in.close();
- }
- }
- } catch (final RuntimeException permissionsOrMalformed) {
- } catch (final Exception ioe) {
- } catch (final LinkageError unexpected) {
- }
- return props;
- }
-
- /**
- * Gets LogManger property for the running JVM. If the LogManager doesn't
- * exist then the default LogManger properties are used.
- *
- * @param name the property name.
- * @return the LogManager.
- * @throws NullPointerException if the given name is null.
- * @since JavaMail 1.5.3
- */
- static String fromLogManager(final String name) {
- if (name == null) {
- throw new NullPointerException();
- }
-
- final Object m = LOG_MANAGER;
- try {
- if (m instanceof Properties) {
- return ((Properties) m).getProperty(name);
- }
- } catch (final RuntimeException unexpected) {
- }
-
- if (m != null) {
- try {
- if (m instanceof LogManager) {
- return ((LogManager) m).getProperty(name);
- }
- } catch (final LinkageError restricted) {
- } catch (final RuntimeException unexpected) {
- }
- }
- return null;
- }
-
- /**
- * Check that the current context is trusted to modify the logging
- * configuration. This requires LoggingPermission("control").
- * @throws SecurityException if a security manager exists and the caller
- * does not have {@code LoggingPermission("control")}.
- * @since JavaMail 1.5.3
- */
- static void checkLogManagerAccess() {
- boolean checked = false;
- final Object m = LOG_MANAGER;
- if (m != null) {
- try {
- if (m instanceof LogManager) {
- checked = true;
- ((LogManager) m).checkAccess();
- }
- } catch (final SecurityException notAllowed) {
- if (checked) {
- throw notAllowed;
- }
- } catch (final LinkageError restricted) {
- } catch (final RuntimeException unexpected) {
- }
- }
-
- if (!checked) {
- checkLoggingAccess();
- }
- }
-
- /**
- * Check that the current context is trusted to modify the logging
- * configuration when the LogManager is not present. This requires
- * LoggingPermission("control").
- * @throws SecurityException if a security manager exists and the caller
- * does not have {@code LoggingPermission("control")}.
- * @since JavaMail 1.5.3
- */
- private static void checkLoggingAccess() {
- /**
- * Some environments selectively enforce logging permissions by allowing
- * access to loggers but not allowing access to handlers. This is an
- * indirect way of checking for LoggingPermission when the LogManager is
- * not present. The root logger will lazy create handlers so the global
- * logger is used instead as it is a known named logger with well
- * defined behavior. If the global logger is a subclass then fallback to
- * using the SecurityManager.
- */
- boolean checked = false;
- final Logger global = Logger.getLogger("global");
- try {
- if (Logger.class == global.getClass()) {
- global.removeHandler((Handler) null);
- checked = true;
- }
- } catch (final NullPointerException unexpected) {
- }
-
- if (!checked) {
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new LoggingPermission("control", null));
- }
- }
- }
-
- /**
- * Determines if access to the {@code java.util.logging.LogManager} class is
- * restricted by the class loader.
- *
- * @return true if a LogManager is present.
- * @since JavaMail 1.5.3
- */
- static boolean hasLogManager() {
- final Object m = LOG_MANAGER;
- return m != null && !(m instanceof Properties);
- }
-
- /**
- * Gets the ZonedDateTime from the given log record.
- *
- * @param record used to generate the zoned date time.
- * @return null if LogRecord doesn't support nanoseconds otherwise a new
- * zoned date time is returned.
- * @throws NullPointerException if record is null.
- * @since JavaMail 1.5.6
- */
- @SuppressWarnings("UseSpecificCatch")
- static Comparable> getZonedDateTime(LogRecord record) {
- if (record == null) {
- throw new NullPointerException();
- }
- final Method m = ZDT_OF_INSTANT;
- if (m != null) {
- try {
- return (Comparable>) m.invoke((Object) null,
- LR_GET_INSTANT.invoke(record),
- ZI_SYSTEM_DEFAULT.invoke((Object) null));
- } catch (final RuntimeException ignore) {
- assert LR_GET_INSTANT != null
- && ZI_SYSTEM_DEFAULT != null : ignore;
- } catch (final InvocationTargetException ite) {
- final Throwable cause = ite.getCause();
- if (cause instanceof Error) {
- throw (Error) cause;
- } else if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- } else { //Should never happen.
- throw new UndeclaredThrowableException(ite);
- }
- } catch (final Exception ignore) {
- }
- }
- return null;
- }
-
- /**
- * Gets the local host name from the given service.
- *
- * @param s the service to examine.
- * @return the local host name or null.
- * @throws IllegalAccessException if the method is inaccessible.
- * @throws InvocationTargetException if the method throws an exception.
- * @throws LinkageError if the linkage fails.
- * @throws NullPointerException if the given service is null.
- * @throws ExceptionInInitializerError if the static initializer fails.
- * @throws Exception if there is a problem.
- * @throws NoSuchMethodException if the given service does not have a method
- * to get the local host name as a string.
- * @throws SecurityException if unable to inspect properties of object.
- * @since JavaMail 1.5.3
- */
- static String getLocalHost(final Object s) throws Exception {
- try {
- final Method m = s.getClass().getMethod("getLocalHost");
- if (!Modifier.isStatic(m.getModifiers())
- && m.getReturnType() == String.class) {
- return (String) m.invoke(s);
- } else {
- throw new NoSuchMethodException(m.toString());
- }
- } catch (final ExceptionInInitializerError EIIE) {
- throw wrapOrThrow(EIIE);
- } catch (final InvocationTargetException ite) {
- throw paramOrError(ite);
- }
- }
-
- /**
- * Used to parse an ISO-8601 duration format of {@code PnDTnHnMn.nS}.
- *
- * @param value an ISO-8601 duration character sequence.
- * @return the number of milliseconds parsed from the duration.
- * @throws ClassNotFoundException if the java.time classes are not present.
- * @throws IllegalAccessException if the method is inaccessible.
- * @throws InvocationTargetException if the method throws an exception.
- * @throws LinkageError if the linkage fails.
- * @throws NullPointerException if the given duration is null.
- * @throws ExceptionInInitializerError if the static initializer fails.
- * @throws Exception if there is a problem.
- * @throws NoSuchMethodException if the correct time methods are missing.
- * @throws SecurityException if reflective access to the java.time classes
- * are not allowed.
- * @since JavaMail 1.5.5
- */
- static long parseDurationToMillis(final CharSequence value) throws Exception {
- try {
- final Class> k = findClass("java.time.Duration");
- final Method parse = k.getMethod("parse", CharSequence.class);
- if (!k.isAssignableFrom(parse.getReturnType())
- || !Modifier.isStatic(parse.getModifiers())) {
- throw new NoSuchMethodException(parse.toString());
- }
-
- final Method toMillis = k.getMethod("toMillis");
- if (!Long.TYPE.isAssignableFrom(toMillis.getReturnType())
- || Modifier.isStatic(toMillis.getModifiers())) {
- throw new NoSuchMethodException(toMillis.toString());
- }
- return (Long) toMillis.invoke(parse.invoke(null, value));
- } catch (final ExceptionInInitializerError EIIE) {
- throw wrapOrThrow(EIIE);
- } catch (final InvocationTargetException ite) {
- throw paramOrError(ite);
- }
- }
-
- /**
- * 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 super LogRecord> 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", "ThrowableResultIgnored"})
- static Comparator reverseOrder(final Comparator c) {
- if (c == null) {
- throw new NullPointerException();
- }
-
- Comparator reverse = null;
- //Comparator in Java 1.8 has 'reversed' 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("reversed");
- if (!Modifier.isStatic(m.getModifiers())
- && 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 RuntimeException ignore) {
- } catch (final InvocationTargetException ite) {
- paramOrError(ite); //Ignore invocation bugs (returned values).
- }
-
- 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);
- }
-
- /**
- * Determines if the given class name identifies a utility class.
- *
- * @param name the fully qualified class name.
- * @return true if the given class name
- * @throws ClassNotFoundException if the class name was not found.
- * @throws IllegalAccessException if the constructor is inaccessible.
- * @throws LinkageError if the linkage fails.
- * @throws ExceptionInInitializerError if the static initializer fails.
- * @throws Exception to match the error method of the ErrorManager.
- * @throws SecurityException if unable to inspect properties of class.
- * @since JavaMail 1.5.2
- */
- static boolean isStaticUtilityClass(String name) throws Exception {
- final Class> c = findClass(name);
- final Class> obj = Object.class;
- Method[] methods;
- boolean util;
- if (c != obj && (methods = c.getMethods()).length != 0) {
- util = true;
- for (Method m : methods) {
- if (m.getDeclaringClass() != obj
- && !Modifier.isStatic(m.getModifiers())) {
- util = false;
- break;
- }
- }
- } else {
- util = false;
- }
- return util;
- }
-
- /**
- * Determines if the given class name is a reflection class name responsible
- * for invoking methods and or constructors.
- *
- * @param name the fully qualified class name.
- * @return true if the given class name
- * @throws ClassNotFoundException if the class name was not found.
- * @throws IllegalAccessException if the constructor is inaccessible.
- * @throws LinkageError if the linkage fails.
- * @throws ExceptionInInitializerError if the static initializer fails.
- * @throws Exception to match the error method of the ErrorManager.
- * @throws SecurityException if unable to inspect properties of class.
- * @since JavaMail 1.5.2
- */
- static boolean isReflectionClass(String name) throws Exception {
- String[] names = REFLECT_NAMES;
- if (names == null) { //Benign data race.
- REFLECT_NAMES = names = reflectionClassNames();
- }
-
- for (String rf : names) { //The set of names is small.
- if (name.equals(rf)) {
- return true;
- }
- }
-
- findClass(name); //Fail late instead of normal return.
- return false;
- }
-
- /**
- * Determines all of the reflection class names used to invoke methods.
- *
- * This method performs indirect and direct calls on a throwable to capture
- * the standard class names and the implementation class names.
- *
- * @return a string array containing the fully qualified class names.
- * @throws Exception if there is a problem.
- */
- private static String[] reflectionClassNames() throws Exception {
- final Class> thisClass = LogManagerProperties.class;
- assert Modifier.isFinal(thisClass.getModifiers()) : thisClass;
- try {
- final HashSet traces = new HashSet<>();
- Throwable t = Throwable.class.getConstructor().newInstance();
- for (StackTraceElement ste : t.getStackTrace()) {
- if (!thisClass.getName().equals(ste.getClassName())) {
- traces.add(ste.getClassName());
- } else {
- break;
- }
- }
-
- Throwable.class.getMethod("fillInStackTrace").invoke(t);
- for (StackTraceElement ste : t.getStackTrace()) {
- if (!thisClass.getName().equals(ste.getClassName())) {
- traces.add(ste.getClassName());
- } else {
- break;
- }
- }
- return traces.toArray(new String[traces.size()]);
- } catch (final InvocationTargetException ITE) {
- throw paramOrError(ITE);
- }
- }
-
- /**
- * Creates a new object from the given class name.
- *
- * @param The generic class type.
- * @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
- */
- 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 adapt 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) {
- //Bitwise inclusive OR produces tighter bytecode for instanceof
- //and matches with multicatch syntax.
- 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;
- }
-
- /**
- * Loads a class using the given loader or the class loader of this class.
- *
- * @param name the class name.
- * @param l any class loader or null.
- * @return the raw class.
- * @throws ClassNotFoundException if not found.
- */
- private static Class> tryLoad(String name, ClassLoader l) throws ClassNotFoundException {
- if (l != null) {
- return Class.forName(name, false, l);
- } else {
- return Class.forName(name);
- }
- }
-
- /**
- * Gets the class loaders using elevated privileges.
- *
- * @return any array of class loaders. Indexes may be null.
- */
- private static ClassLoader[] getClassLoaders() {
- return AccessController.doPrivileged(new PrivilegedAction() {
-
- @SuppressWarnings("override") //JDK-6954234
- 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);
- if (parent == null || prefix == null) {
- throw new NullPointerException();
- }
- this.prefix = prefix;
- }
-
- /**
- * 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
- @SuppressWarnings("CloneDoesntCallSuperClone")
- public synchronized Object clone() {
- return exportCopy(defaults);
- }
-
- /**
- * Searches defaults, then searches the log manager if available or the
- * system properties 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) {
- if (key.length() > 0) {
- value = fromLogManager(prefix + '.' + key);
- }
-
- if (value == null) {
- value = fromLogManager(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 get operation on the defaults
- * followed by the normal hash table get.
- *
- * @param key any key.
- * @return the value for the key or null.
- * @since JavaMail 1.4.5
- */
- @Override
- public synchronized Object get(final Object key) {
- Object value;
- if (key instanceof String) {
- value = getProperty((String) key);
- } else {
- value = null;
- }
-
- //Search for non-string value.
- if (value == null) {
- value = defaults.get(key);
- if (value == null && !defaults.containsKey(key)) {
- value = super.get(key);
- }
- }
- return value;
- }
-
- /**
- * 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) {
- if (key instanceof String && value instanceof String) {
- final Object def = preWrite(key);
- final Object man = super.put(key, value);
- return man == null ? def : man;
- } else {
- return super.put(key, value);
- }
- }
-
- /**
- * 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 synchronized boolean containsKey(final Object key) {
- boolean found = key instanceof String
- && getProperty((String) key) != null;
- if (!found) {
- found = defaults.containsKey(key) || super.containsKey(key);
- }
- return found;
- }
-
- /**
- * 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);
- return get(key);
- }
-
- /**
- * 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/app/src/main/java/com/sun/mail/util/logging/MailHandler.java b/app/src/main/java/com/sun/mail/util/logging/MailHandler.java
deleted file mode 100644
index ecfe0684a4..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/MailHandler.java
+++ /dev/null
@@ -1,4416 +0,0 @@
-/*
- * Copyright (c) 2009, 2020 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2009, 2020 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package com.sun.mail.util.logging;
-
-import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager;
-import java.io.*;
-import java.lang.reflect.InvocationTargetException;
-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.*;
-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 <handler-name> 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. All
- * control characters are removed from the attachment names.
- * (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. All control
- * characters are removed from the subject line. (defaults to {@linkplain
- * com.sun.mail.util.logging.CollectorFormatter CollectorFormatter}.)
- *
- * <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 login, the Handler
- * will verify all local settings and try to establish a connection with
- * the email server.
- * If set to a value of remote, the Handler
- * will verify all local settings, try to establish a connection with the
- * email server, and try to verify the envelope of the email message.
- *
- * 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 message.
- * The presence of an attachment formatter will change the content type of the
- * email message to a multi-part message. 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 set the context class loader for use with the Jakarta Mail API.
- * Load and pin this before it is loaded in the close method. The field is
- * declared as java.security.PrivilegedAction so
- * WebappClassLoader.clearReferencesStaticFinal() method will ignore this
- * field.
- */
- private static final PrivilegedAction MAILHANDLER_LOADER
- = new GetAndSetContext(MailHandler.class);
- /**
- * A thread local mutex used to prevent logging loops. This code has to be
- * prepared to deal with unexpected null values since the
- * WebappClassLoader.clearReferencesThreadLocals() and
- * InnocuousThread.eraseThreadLocals() can remove thread local values.
- * The MUTEX has 5 states:
- * 1. A null value meaning default state of not publishing.
- * 2. MUTEX_PUBLISH on first entry of a push or publish.
- * 3. The index of the first filter to accept a log record.
- * 4. MUTEX_REPORT when cycle of records is detected.
- * 5. MUTEXT_LINKAGE when a linkage error is reported.
- */
- private static final ThreadLocal MUTEX = new ThreadLocal<>();
- /**
- * The marker object used to report a publishing state.
- * This must be less than the body filter index (-1).
- */
- private static final Integer MUTEX_PUBLISH = -2;
- /**
- * The used for the error reporting state.
- * This must be less than the PUBLISH state.
- */
- private static final Integer MUTEX_REPORT = -4;
- /**
- * The used for linkage error reporting.
- * This must be less than the REPORT state.
- */
- private static final Integer MUTEX_LINKAGE = -8;
- /**
- * 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 JDK-6228391 and K 6278.
- */
- private Session session;
- /**
- * A mapping of log record to matching filter index. Negative one is used
- * to track the body filter. Zero and greater is used to track the
- * attachment parts. All indexes less than or equal to the matched value
- * have already seen the given log record.
- */
- private int[] matched;
- /**
- * 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 super LogRecord> 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 entry and body filter for this handler.
- * There is no way to un-seal the super handler.
- */
- private volatile Filter filter;
- /**
- * Holds the level for this handler.
- * There is no way to un-seal the super handler.
- */
- private volatile Level logLevel = Level.ALL;
- /**
- * Holds the filters for each attachment. Filters are optional for
- * each attachment. This is declared volatile because this is treated as
- * copy-on-write. The VO_VOLATILE_REFERENCE_TO_ARRAY warning is a false
- * positive.
- */
- @SuppressWarnings("VolatileArrayField")
- private volatile Filter[] attachmentFilters;
- /**
- * Holds the encoding name for this handler.
- * There is no way to un-seal the super handler.
- */
- private String encoding;
- /**
- * Holds the entry and body filter for this handler.
- * There is no way to un-seal the super handler.
- */
- private Formatter formatter;
- /**
- * 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;
- /**
- * Holds the error manager for this handler.
- * There is no way to un-seal the super handler.
- */
- private volatile ErrorManager errorManager = defaultErrorManager();
-
- /**
- * 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;
- checkAccess();
- }
-
- /**
- * 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 or null.
- * @return true if the LogRecord would be logged.
- */
- @Override
- public boolean isLoggable(final LogRecord record) {
- if (record == null) { //JDK-8233979
- return false;
- }
-
- int levelValue = getLevel().intValue();
- if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
- return false;
- }
-
- Filter body = getFilter();
- if (body == null || body.isLoggable(record)) {
- setMatchedPart(-1);
- 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 or null.
- */
- @Override
- 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)) {
- if (record != null) {
- record.getSourceMethodName(); //Infer caller.
- publish0(record);
- } else { //Override of isLoggable is broken.
- reportNullError(ErrorManager.WRITE_FAILURE);
- }
- }
- } catch (final LinkageError JDK8152515) {
- reportLinkageError(JDK8152515, ErrorManager.WRITE_FAILURE);
- } finally {
- releaseMutex();
- }
- } else {
- reportUnPublishedError(record);
- }
- }
-
- /**
- * Performs the publish after the record has been filtered.
- * @param record the record which must not be null.
- * @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) {
- //assert data.length == matched.length;
- matched[size] = getMatchedPart();
- 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) {
- final Integer idx = MUTEX.get();
- if (idx == null || idx > MUTEX_REPORT) {
- MUTEX.set(MUTEX_REPORT);
- try {
- final String msg;
- if (record != null) {
- final Formatter f = createSimpleFormatter();
- 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 {
- if (idx != null) {
- MUTEX.set(idx);
- } else {
- MUTEX.remove();
- }
- }
- }
- }
-
- /**
- * 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();
- }
-
- /**
- * This is used to get the filter index from when {@code isLoggable} and
- * {@code isAttachmentLoggable} was invoked by {@code publish} method.
- *
- * @return the filter index or MUTEX_PUBLISH if unknown.
- * @since JavaMail 1.5.5
- * @throws NullPointerException if tryMutex was not called.
- */
- private int getMatchedPart() {
- //assert Thread.holdsLock(this);
- Integer idx = MUTEX.get();
- if (idx == null || idx >= readOnlyAttachmentFilters().length) {
- idx = MUTEX_PUBLISH;
- }
- return idx;
- }
-
- /**
- * This is used to record the filter index when {@code isLoggable} and
- * {@code isAttachmentLoggable} was invoked by {@code publish} method.
- *
- * @param index the filter index.
- * @since JavaMail 1.5.5
- */
- private void setMatchedPart(int index) {
- if (MUTEX_PUBLISH.equals(MUTEX.get())) {
- MUTEX.set(index);
- }
- }
-
- /**
- * Clear previous matches when the filters are modified and there are
- * existing log records that were matched.
- * @param index the lowest filter index to clear.
- * @since JavaMail 1.5.5
- */
- private void clearMatches(int index) {
- assert Thread.holdsLock(this);
- for (int r = 0; r < size; ++r) {
- if (matched[r] >= index) {
- matched[r] = MUTEX_PUBLISH;
- }
- }
- }
-
- /**
- * A callback method for when this object is about to be placed into
- * commission. This contract is defined by the
- * {@code org.glassfish.hk2.api.PostConstruct} interface. If this class is
- * loaded via a lifecycle managed environment other than HK2 then it is
- * recommended that this method is called either directly or through
- * extending this class to signal that this object is ready for use.
- *
- * @since JavaMail 1.5.3
- */
- //@javax.annotation.PostConstruct
- public void postConstruct() {
- }
-
- /**
- * A callback method for when this object is about to be decommissioned.
- * This contract is defined by the {@code org.glassfish.hk2.api.PreDestory}
- * interface. If this class is loaded via a lifecycle managed environment
- * other than HK2 then it is recommended that this method is called either
- * directly or through extending this class to signal that this object will
- * be destroyed.
- *
- * @since JavaMail 1.5.3
- */
- //@javax.annotation.PreDestroy
- public void preDestroy() {
- /**
- * Close can require permissions so just trigger a push.
- */
- push(false, ErrorManager.CLOSE_FAILURE);
- }
-
- /**
- * 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()
- */
- @Override
- 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()
- */
- @Override
- public void close() {
- try {
- checkAccess(); //Ensure setLevel works before clearing the buffer.
- Message msg = null;
- synchronized (this) {
- try {
- msg = writeLogRecords(ErrorManager.CLOSE_FAILURE);
- } finally { //Change level after formatting.
- this.logLevel = Level.OFF;
- /**
- * 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];
- this.matched = new int[this.data.length];
- }
- }
- }
-
- if (msg != null) {
- send(msg, false, ErrorManager.CLOSE_FAILURE);
- }
- } catch (final LinkageError JDK8152515) {
- reportLinkageError(JDK8152515, ErrorManager.CLOSE_FAILURE);
- }
- }
-
- /**
- * 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 void setLevel(final Level newLevel) {
- if (newLevel == null) {
- throw new NullPointerException();
- }
- checkAccess();
-
- //Don't allow a closed handler to be opened (half way).
- synchronized (this) { //Wait for writeLogRecords.
- if (this.capacity > 0) {
- this.logLevel = newLevel;
- }
- }
- }
-
- /**
- * Get the log level specifying which messages will be logged by this
- * Handler. Message levels lower than this level will be
- * discarded.
- *
- * @return the level of messages being logged.
- */
- @Override
- public Level getLevel() {
- return logLevel; //Volatile access.
- }
-
- /**
- * Retrieves the ErrorManager for this Handler.
- *
- * @return the ErrorManager for this Handler
- * @throws SecurityException if a security manager exists and if the caller
- * does not have LoggingPermission("control").
- */
- @Override
- public ErrorManager getErrorManager() {
- checkAccess();
- return this.errorManager; //Volatile access.
- }
-
- /**
- * Define an ErrorManager for this Handler.
- *
- * The ErrorManager's "error" method will be invoked if any errors occur
- * while using this Handler.
- *
- * @param em the new ErrorManager
- * @throws SecurityException if a security manager exists and if the
- * caller does not have LoggingPermission("control").
- * @throws NullPointerException if the given error manager is null.
- */
- @Override
- public void setErrorManager(final ErrorManager em) {
- checkAccess();
- setErrorManager0(em);
- }
-
- /**
- * Sets the error manager on this handler and the super handler. In secure
- * environments the super call may not be allowed which is not a failure
- * condition as it is an attempt to free the unused handler error manager.
- *
- * @param em a non null error manager.
- * @throws NullPointerException if the given error manager is null.
- * @since JavaMail 1.5.6
- */
- private void setErrorManager0(final ErrorManager em) {
- if (em == null) {
- throw new NullPointerException();
- }
- try {
- synchronized (this) { //Wait for writeLogRecords.
- this.errorManager = em;
- super.setErrorManager(em); //Try to free super error manager.
- }
- } catch (RuntimeException | LinkageError ignore) {
- }
- }
-
- /**
- * Get the current Filter for this Handler.
- *
- * @return a Filter object (may be null)
- */
- @Override
- public Filter getFilter() {
- return this.filter; //Volatile access.
- }
-
- /**
- * Set a Filter to control output on this Handler.
- *
- * For each call of publish the Handler will call
- * this Filter (if it is non-null) to check if the
- * LogRecord should be published or discarded.
- *
- * @param newFilter a Filter object (may be null)
- * @throws SecurityException if a security manager exists and if the caller
- * does not have LoggingPermission("control").
- */
- @Override
- public void setFilter(final Filter newFilter) {
- checkAccess();
- synchronized (this) { //Wait for writeLogRecords.
- if (newFilter != filter) {
- clearMatches(-1);
- }
- this.filter = newFilter; //Volatile access.
- }
- }
-
- /**
- * Return the character encoding for this Handler.
- *
- * @return The encoding name. May be null, which indicates the default
- * encoding should be used.
- */
- @Override
- public synchronized String getEncoding() {
- return this.encoding;
- }
-
- /**
- * Set the character encoding used by this Handler.
- *
- * The encoding should be set before any LogRecords are written
- * to the Handler.
- *
- * @param encoding The name of a supported character encoding. May be
- * null, to indicate the default platform encoding.
- * @throws SecurityException if a security manager exists and if the caller
- * does not have LoggingPermission("control").
- * @throws UnsupportedEncodingException if the named encoding is not
- * supported.
- */
- @Override
- public void setEncoding(String encoding) throws UnsupportedEncodingException {
- checkAccess();
- setEncoding0(encoding);
- }
-
- /**
- * Set the character encoding used by this handler. This method does not
- * check permissions of the caller.
- *
- * @param e any encoding name or null for the default.
- * @throws UnsupportedEncodingException if the given encoding is not supported.
- */
- private void setEncoding0(String e) throws UnsupportedEncodingException {
- if (e != null) {
- try {
- if (!java.nio.charset.Charset.isSupported(e)) {
- throw new UnsupportedEncodingException(e);
- }
- } catch (java.nio.charset.IllegalCharsetNameException icne) {
- throw new UnsupportedEncodingException(e);
- }
- }
-
- synchronized (this) { //Wait for writeLogRecords.
- this.encoding = e;
- }
- }
-
- /**
- * Return the Formatter for this Handler.
- *
- * @return the Formatter (may be null).
- */
- @Override
- public synchronized Formatter getFormatter() {
- return this.formatter;
- }
-
- /**
- * Set a Formatter. This Formatter will be used
- * to format LogRecords for this Handler.
- *
- * Some Handlers may not use Formatters, in which
- * case the Formatter will be remembered, but not used.
- *
- * @param newFormatter the Formatter to use (may not be null)
- * @throws SecurityException if a security manager exists and if the caller
- * does not have LoggingPermission("control").
- * @throws NullPointerException if the given formatter is null.
- */
- @Override
- public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
- checkAccess();
- if (newFormatter == null) {
- throw new NullPointerException();
- }
- this.formatter = newFormatter;
- }
-
- /**
- * 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 super LogRecord> 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 super LogRecord> 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, empty array can be used to only supply a
- * user name set by mail.user property, or null if no
- * credentials are 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(DefaultAuthenticator.of(new String(password)));
- }
- }
-
- /**
- * A private hook to handle possible future overrides. See public method.
- * @param auth see public method.
- * @throws SecurityException if a security manager exists and the
- * caller does not have LoggingPermission("control").
- * @throws IllegalStateException if called from inside a push.
- */
- private void setAuthenticator0(final Authenticator auth) {
- checkAccess();
-
- Session settings;
- synchronized (this) {
- if (isWriting) {
- throw new IllegalStateException();
- }
- this.auth = auth;
- settings = updateSession();
- }
- 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);
- }
-
- /**
- * A private hook to handle overrides when the public method is declared
- * non final. See public method for details.
- * @param props see public method.
- */
- 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 = updateSession();
- }
- 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();
- if (filters.length == 0) {
- filters = emptyFilterArray();
- } else {
- filters = Arrays.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();
- }
-
- if (size != 0) {
- for (int i = 0; i < filters.length; ++i) {
- if (filters[i] != attachmentFilters[i]) {
- clearMatches(i);
- break;
- }
- }
- }
- 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 = Arrays.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.alignAttachmentFilters();
- this.alignAttachmentNames();
- }
- }
-
- /**
- * 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. All control
- * characters are removed from the attachment names.
- * 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] = TailNameFormatter.of(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. All
- * control characters will be removed from the output of the formatter. 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();
-
- if (formatters.length == 0) {
- formatters = emptyFormatterArray();
- } else {
- formatters = Arrays.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. All control characters are
- * removed from the subject line.
- * @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(TailNameFormatter.of(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. All control characters
- * will be removed from the formatter output. 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) {
- try {
- if (msg != null) {
- errorManager.error(Level.SEVERE.getName()
- .concat(": ").concat(msg), ex, code);
- } else {
- errorManager.error(null, ex, code);
- }
- } catch (RuntimeException | LinkageError GLASSFISH_21258) {
- reportLinkageError(GLASSFISH_21258, code);
- }
- }
-
- /**
- * Calls log manager checkAccess if this is sealed.
- */
- private void checkAccess() {
- if (sealed) {
- LogManagerProperties.checkLogManagerAccess();
- }
- }
-
- /**
- * 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 chunk any char sequence or null.
- * @return return the mime type or null for text/plain.
- */
- final String contentTypeOf(CharSequence chunk) {
- if (!isEmpty(chunk)) {
- final int MAX_CHARS = 25;
- if (chunk.length() > MAX_CHARS) {
- chunk = chunk.subSequence(0, MAX_CHARS);
- }
- try {
- final String charset = getEncodingName();
- final byte[] b = chunk.toString().getBytes(charset);
- final ByteArrayInputStream in = new ByteArrayInputStream(b);
- 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 the mimeType of a formatter by the class name. This method
- * avoids calling getHead and getTail of content formatters during verify
- * because they might trigger side effects or excessive work. The name
- * formatters and subject are usually safe to call.
- * Package-private for unit testing.
- *
- * @param f the formatter or null.
- * @return return the mime type or null, meaning text/plain.
- * @since JavaMail 1.5.6
- */
- final String contentTypeOf(final Formatter f) {
- assert Thread.holdsLock(this);
- if (f != null) {
- String type = getContentType(f.getClass().getName());
- if (type != null) {
- return type;
- }
-
- for (Class> k = f.getClass(); k != Formatter.class;
- k = k.getSuperclass()) {
- String name;
- try {
- name = k.getSimpleName();
- } catch (final InternalError JDK8057919) {
- name = k.getName();
- }
- name = name.toLowerCase(Locale.ENGLISH);
- for (int idx = name.indexOf('$') + 1;
- (idx = name.indexOf("ml", idx)) > -1; idx += 2) {
- if (idx > 0) {
- if (name.charAt(idx - 1) == 'x') {
- return "application/xml";
- }
- if (idx > 1 && name.charAt(idx - 2) == 'h'
- && name.charAt(idx - 1) == 't') {
- return "text/html";
- }
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Determines if the given throwable is a no content exception. It is
- * assumed Transport.sendMessage will call Message.writeTo so we need to
- * ignore any exceptions that could be layered on top of that call chain to
- * infer that sendMessage is failing because of writeTo. Package-private
- * for unit testing.
- * @param msg the message without content.
- * @param t the throwable chain 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
- */
- @SuppressWarnings({"UseSpecificCatch", "ThrowableResultIgnored"})
- final boolean isMissingContent(Message msg, Throwable t) {
- final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- 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)) {
- int limit = 0;
- while (t != null) {
- if (noContent.getClass() == t.getClass()
- && txt.equals(t.getMessage())) {
- return true;
- }
-
- //Not all Jakarta Mail implementations support JDK 1.4
- //exception chaining.
- final Throwable cause = t.getCause();
- if (cause == null && t instanceof MessagingException) {
- t = ((MessagingException) t).getNextException();
- } else {
- t = cause;
- }
-
- //Deal with excessive cause chains and cyclic throwables.
- if (++limit == (1 << 16)) {
- break; //Give up.
- }
- }
- }
- } finally {
- getAndSetContextClassLoader(ccl);
- }
- 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
- */
- @SuppressWarnings("UseSpecificCatch")
- private void reportError(Message msg, Exception ex, int code) {
- try {
- try { //Use direct call so we do not prefix raw email.
- errorManager.error(toRawString(msg), ex, code);
- } catch (final RuntimeException re) {
- reportError(toMsgString(re), ex, code);
- } catch (final Exception e) {
- reportError(toMsgString(e), ex, code);
- }
- } catch (final LinkageError GLASSFISH_21258) {
- reportLinkageError(GLASSFISH_21258, code);
- }
- }
-
- /**
- * Reports the given linkage error or runtime exception.
- *
- * The current LogManager code will stop closing all remaining handlers if
- * an error is thrown during resetLogger. This is a workaround for
- * GLASSFISH-21258 and JDK-8152515.
- * @param le the linkage error or a RuntimeException.
- * @param code the ErrorManager code.
- * @throws NullPointerException if error is null.
- * @since JavaMail 1.5.3
- */
- private void reportLinkageError(final Throwable le, final int code) {
- if (le == null) {
- throw new NullPointerException(String.valueOf(code));
- }
-
- final Integer idx = MUTEX.get();
- if (idx == null || idx > MUTEX_LINKAGE) {
- MUTEX.set(MUTEX_LINKAGE);
- try {
- Thread.currentThread().getUncaughtExceptionHandler()
- .uncaughtException(Thread.currentThread(), le);
- } catch (RuntimeException | LinkageError ignore) {
- } finally {
- if (idx != null) {
- MUTEX.set(idx);
- } else {
- MUTEX.remove();
- }
- }
- }
- }
-
- /**
- * 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 charset = getEncoding();
- if (charset == null) {
- charset = MimeUtility.getDefaultJavaCharset();
- }
- return charset;
- }
-
- /**
- * 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 or null, meaning text/plain.
- * @throws MessagingException if there is a problem.
- */
- private void setContent(MimePart part, CharSequence buf, String type) throws MessagingException {
- final String charset = getEncodingName();
- if (type != null && !"text/plain".equalsIgnoreCase(type)) {
- type = contentWithEncoding(type, charset);
- 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(), charset);
- }
- } else {
- part.setText(buf.toString(), MimeUtility.mimeCharset(charset));
- }
- }
-
- /**
- * 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)) { //Support pre K5687.
- 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.
- * E.G. do nothing, flush now, truncate now, push now and resize.
- * @param newCapacity the max number of records.
- * @throws SecurityException if a security manager exists and the
- * caller does not have LoggingPermission("control").
- * @throws IllegalStateException if called from inside a push.
- */
- private synchronized void setCapacity0(final int newCapacity) {
- checkAccess();
- 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 using a happens-before relationship between
- * this method and setAttachmentFilters. 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 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 with the attachment
- * formatters.
- * @return true if size was changed.
- */
- private boolean alignAttachmentNames() {
- assert Thread.holdsLock(this);
- boolean fixed = false;
- final int expect = this.attachmentFormatters.length;
- final int current = this.attachmentNames.length;
- if (current != expect) {
- this.attachmentNames = Arrays.copyOf(attachmentNames, expect,
- Formatter[].class);
- 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] = TailNameFormatter.of(
- toString(this.attachmentFormatters[i]));
- }
- }
- }
- return fixed;
- }
-
- /**
- * Expand or shrink the attachment filters with the attachment formatters.
- * @return true if the size was changed.
- */
- private boolean alignAttachmentFilters() {
- assert Thread.holdsLock(this);
-
- boolean fixed = false;
- final int expect = this.attachmentFormatters.length;
- final int current = this.attachmentFilters.length;
- if (current != expect) {
- this.attachmentFilters = Arrays.copyOf(attachmentFilters, expect,
- Filter[].class);
- clearMatches(current);
- 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 = this.filter;
- 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;
- }
-
- /**
- * 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 = Arrays.copyOf(data, newCapacity, LogRecord[].class);
- this.matched = Arrays.copyOf(matched, 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) {
- assert this.errorManager != null;
- final String p = getClass().getName();
- this.mailProps = new Properties(); //See method param comments.
- final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- try {
- this.contentTypes = FileTypeMap.getDefaultFileTypeMap();
- } finally {
- getAndSetContextClassLoader(ccl);
- }
-
- //Assign any custom error manager first so it can detect all failures.
- initErrorManager(p);
-
- initLevel(p);
- initFilter(p);
- initCapacity(p);
- initAuthenticator(p);
-
- initEncoding(p);
- initFormatter(p);
- initComparator(p);
- initPushLevel(p);
- initPushFilter(p);
-
- initSubject(p);
-
- initAttachmentFormaters(p);
- initAttachmentFilters(p);
- initAttachmentNames(p);
-
- if (props == null && fromLogManager(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, this.errorManager);
- } catch (final SecurityException se) {
- reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
- }
-
- try {
- canidate = this.filter;
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Filter) {
- this.filter = (Filter) result;
- }
-
- canidate = this.formatter;
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Formatter) {
- this.formatter = (Formatter) result;
- }
- } catch (final SecurityException se) {
- reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
- }
-
- canidate = this.subjectFormatter;
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Formatter) {
- this.subjectFormatter = (Formatter) result;
- }
-
- canidate = this.pushFilter;
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Filter) {
- this.pushFilter = (Filter) result;
- }
-
- for (int i = 0; i < attachmentFormatters.length; ++i) {
- canidate = attachmentFormatters[i];
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Formatter) {
- attachmentFormatters[i] = (Formatter) result;
- }
-
- canidate = attachmentFilters[i];
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Filter) {
- attachmentFilters[i] = (Filter) result;
- }
-
- canidate = attachmentNames[i];
- result = intern(seen, canidate);
- if (result != canidate && result instanceof Formatter) {
- attachmentNames[i] = (Formatter) result;
- }
- }
- } catch (final Exception skip) {
- reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE);
- } catch (final LinkageError skip) {
- reportError(skip.getMessage(), new InvocationTargetException(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;
- }
-
- /**
- * Factory method used to create a java.util.logging.SimpleFormatter.
- * @return a new SimpleFormatter.
- * @since JavaMail 1.5.6
- */
- private static Formatter createSimpleFormatter() {
- //Don't force the byte code verifier to load the formatter.
- return Formatter.class.cast(new SimpleFormatter());
- }
-
- /**
- * Checks a char sequence value for null or empty.
- * @param s the char sequence.
- * @return true if the given string is null or zero length.
- */
- private static boolean isEmpty(final CharSequence 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);
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initAttachmentFilters(final String p) {
- assert Thread.holdsLock(this);
- assert this.attachmentFormatters != null;
- final String list = fromLogManager(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 (alignAttachmentFilters()) {
- reportError("Attachment filters.",
- attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE);
- }
- } else {
- this.attachmentFilters = emptyFilterArray();
- alignAttachmentFilters();
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initAttachmentFormaters(final String p) {
- assert Thread.holdsLock(this);
- final String list = fromLogManager(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] = createSimpleFormatter();
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- a[i] = createSimpleFormatter();
- }
- } else {
- final Exception NPE = new NullPointerException(atIndexMsg(i));
- reportError("Attachment formatter.", NPE, ErrorManager.OPEN_FAILURE);
- a[i] = createSimpleFormatter();
- }
- }
-
- this.attachmentFormatters = a;
- } else {
- this.attachmentFormatters = emptyFormatterArray();
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initAttachmentNames(final String p) {
- assert Thread.holdsLock(this);
- assert this.attachmentFormatters != null;
-
- final String list = fromLogManager(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 (ClassNotFoundException
- | ClassCastException literal) {
- a[i] = TailNameFormatter.of(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 (alignAttachmentNames()) { //Any null indexes are repaired.
- reportError("Attachment names.",
- attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE);
- }
- } else {
- this.attachmentNames = emptyFormatterArray();
- alignAttachmentNames();
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initAuthenticator(final String p) {
- assert Thread.holdsLock(this);
- String name = fromLogManager(p.concat(".authenticator"));
- if (name != null && !"null".equalsIgnoreCase(name)) {
- if (name.length() != 0) {
- try {
- this.auth = LogManagerProperties
- .newObjectFrom(name, Authenticator.class);
- } catch (final SecurityException SE) {
- throw SE;
- } catch (final ClassNotFoundException
- | ClassCastException literalAuth) {
- this.auth = DefaultAuthenticator.of(name);
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- }
- } else { //Authenticator is installed to provide the user name.
- this.auth = DefaultAuthenticator.of(name);
- }
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initLevel(final String p) {
- assert Thread.holdsLock(this);
- try {
- final String val = fromLogManager(p.concat(".level"));
- if (val != null) {
- logLevel = Level.parse(val);
- } else {
- logLevel = Level.WARNING;
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final RuntimeException RE) {
- reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
- logLevel = Level.WARNING;
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initFilter(final String p) {
- assert Thread.holdsLock(this);
- try {
- String name = fromLogManager(p.concat(".filter"));
- if (hasValue(name)) {
- filter = LogManagerProperties.newFilter(name);
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initCapacity(final String p) {
- assert Thread.holdsLock(this);
- final int DEFAULT_CAPACITY = 1000;
- try {
- final String value = fromLogManager(p.concat(".capacity"));
- if (value != null) {
- this.setCapacity0(Integer.parseInt(value));
- } else {
- this.setCapacity0(DEFAULT_CAPACITY);
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final RuntimeException RE) {
- reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
- }
-
- if (capacity <= 0) {
- capacity = DEFAULT_CAPACITY;
- }
-
- this.data = new LogRecord[1];
- this.matched = new int[this.data.length];
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initEncoding(final String p) {
- assert Thread.holdsLock(this);
- try {
- String e = fromLogManager(p.concat(".encoding"));
- if (e != null) {
- setEncoding0(e);
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (UnsupportedEncodingException | RuntimeException UEE) {
- reportError(UEE.getMessage(), UEE, ErrorManager.OPEN_FAILURE);
- }
- }
-
- /**
- * Used to get or create the default ErrorManager used before init.
- * @return the super error manager or a new ErrorManager.
- * @since JavaMail 1.5.3
- */
- private ErrorManager defaultErrorManager() {
- ErrorManager em;
- try { //Try to share the super error manager.
- em = super.getErrorManager();
- } catch (RuntimeException | LinkageError ignore) {
- em = null;
- }
-
- //Don't assume that the super call is not null.
- if (em == null) {
- em = new ErrorManager();
- }
- return em;
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initErrorManager(final String p) {
- assert Thread.holdsLock(this);
- try {
- String name = fromLogManager(p.concat(".errorManager"));
- if (name != null) {
- setErrorManager0(LogManagerProperties.newErrorManager(name));
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initFormatter(final String p) {
- assert Thread.holdsLock(this);
- try {
- String name = fromLogManager(p.concat(".formatter"));
- if (hasValue(name)) {
- final Formatter f
- = LogManagerProperties.newFormatter(name);
- assert f != null;
- if (f instanceof TailNameFormatter == false) {
- formatter = f;
- } else {
- formatter = createSimpleFormatter();
- }
- } else {
- formatter = createSimpleFormatter();
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- formatter = createSimpleFormatter();
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initComparator(final String p) {
- assert Thread.holdsLock(this);
- try {
- String name = fromLogManager(p.concat(".comparator"));
- String reverse = fromLogManager(p.concat(".comparator.reverse"));
- 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);
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initPushLevel(final String p) {
- assert Thread.holdsLock(this);
- try {
- final String val = fromLogManager(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;
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initPushFilter(final String p) {
- assert Thread.holdsLock(this);
- try {
- String name = fromLogManager(p.concat(".pushFilter"));
- if (hasValue(name)) {
- 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);
- }
- }
-
- /**
- * Parses LogManager string values into objects used by this handler.
- * @param p the handler class name used as the prefix.
- * @throws NullPointerException if the given argument is null.
- * @throws SecurityException if not allowed.
- */
- private void initSubject(final String p) {
- assert Thread.holdsLock(this);
- String name = fromLogManager(p.concat(".subject"));
- if (name == null) { //Soft dependency on CollectorFormatter.
- name = "com.sun.mail.util.logging.CollectorFormatter";
- }
-
- if (hasValue(name)) {
- try {
- this.subjectFormatter = LogManagerProperties.newFormatter(name);
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (ClassNotFoundException
- | ClassCastException literalSubject) {
- this.subjectFormatter = TailNameFormatter.of(name);
- } catch (final Exception E) {
- this.subjectFormatter = TailNameFormatter.of(name);
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- }
- } else { //User has forced empty or literal null.
- this.subjectFormatter = TailNameFormatter.of(name);
- }
- }
-
- /**
- * 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)) {
- setMatchedPart(i);
- 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.
- * @throws NullPointerException if tryMutex was not called.
- */
- 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 push = getPushFilter();
- if (push == null) {
- return true;
- }
-
- final int match = getMatchedPart();
- if ((match == -1 && getFilter() == push)
- || (match >= 0 && attachmentFilters[match] == push)) {
- return true;
- } else {
- return push.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);
- }
- } catch (final LinkageError JDK8152515) {
- reportLinkageError(JDK8152515, 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);
- final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- try { //JDK-8025251
- Transport.send(msg); //Calls save changes.
- } finally {
- getAndSetContextClassLoader(ccl);
- }
- } catch (final RuntimeException re) {
- reportError(msg, re, code);
- } 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 {
- if (comparator.compare(data[0], data[0]) != 0) {
- throw new IllegalArgumentException(
- comparator.getClass().getName());
- }
- }
- } 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 under most conditions will catch, report, and continue when
- * exceptions occur. 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 Message writeLogRecords(final int code) {
- try {
- synchronized (this) {
- if (size > 0 && !isWriting) {
- isWriting = true;
- try {
- return writeLogRecords0();
- } finally {
- isWriting = false;
- if (size > 0) {
- reset();
- }
- }
- }
- }
- } catch (final RuntimeException re) {
- reportError(re.getMessage(), re, code);
- } catch (final Exception e) {
- reportError(e.getMessage(), e, code);
- }
- return null;
- }
-
- /**
- * Formats all records in the buffer and places the output in a Message.
- * This method under most conditions will catch, report, and continue when
- * exceptions occur.
- *
- * @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.
- * @throws MessagingException if there is a problem.
- * @throws IOException if there is a problem.
- * @throws RuntimeException if there is an unexpected problem.
- * @since JavaMail 1.5.3
- */
- private Message writeLogRecords0() throws Exception {
- assert Thread.holdsLock(this);
- sort();
- if (session == null) {
- initSession();
- }
- MimeMessage msg = new MimeMessage(session);
-
- /**
- * 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];
- StringBuilder buf = null;
- final MimePart body;
- if (parts.length == 0) {
- msg.setDescription(descriptionFrom(
- getFormatter(), getFilter(), subjectFormatter));
- body = msg;
- } else {
- msg.setDescription(descriptionFrom(
- comparator, pushLevel, pushFilter));
- body = createBodyPart();
- }
-
- appendSubject(msg, head(subjectFormatter));
- final Formatter bodyFormat = getFormatter();
- final Filter bodyFilter = getFilter();
-
- Locale lastLocale = null;
- for (int ix = 0; ix < size; ++ix) {
- boolean formatted = false;
- final int match = matched[ix];
- final LogRecord r = data[ix];
- data[ix] = null; //Clear while formatting.
-
- final Locale locale = localeFor(r);
- appendSubject(msg, format(subjectFormatter, r));
- Filter lmf = null; //Identity of last matched filter.
- if (bodyFilter == null || match == -1 || parts.length == 0
- || (match < -1 && bodyFilter.isLoggable(r))) {
- lmf = bodyFilter;
- if (buf == null) {
- buf = new StringBuilder();
- buf.append(head(bodyFormat));
- }
- formatted = true;
- buf.append(format(bodyFormat, r));
- if (locale != null && !locale.equals(lastLocale)) {
- appendContentLang(body, locale);
- }
- }
-
- for (int i = 0; i < parts.length; ++i) {
- //A match index less than the attachment index means that
- //the filter has not seen this record.
- final Filter af = attachmentFilters[i];
- if (af == null || lmf == af || match == i
- || (match < i && af.isLoggable(r))) {
- if (lmf == null && af != null) {
- lmf = af;
- }
- 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 (body != msg && 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, ""));
-
- String contentType = contentTypeOf(buf);
- String altType = contentTypeOf(bodyFormat);
- setContent(body, buf, altType == null ? contentType : altType);
- if (body != msg) {
- final MimeMultipart multipart = new MimeMultipart();
- //assert body instanceof BodyPart : body;
- multipart.addBodyPart((BodyPart) body);
-
- for (int i = 0; i < parts.length; ++i) {
- if (parts[i] != null) {
- multipart.addBodyPart(parts[i]);
- }
- }
- msg.setContent(multipart);
- }
-
- return msg;
- }
-
- /**
- * 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) {
- try {
- 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) { //Pass some invalid string.
- verifySettings0(session, check.getClass().toString());
- }
- }
- }
- } catch (final LinkageError JDK8152515) {
- reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE);
- }
- }
-
- /**
- * 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.
- * @param verify the type of verify to perform.
- * @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)
- && !"login".equals(verify)) {
- reportError("Verify must be 'limited', local', "
- + "'resolve', 'login', 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.
- String[] atn;
- synchronized (this) { //Create the subject.
- appendSubject(abort, head(subjectFormatter));
- appendSubject(abort, tail(subjectFormatter, ""));
- atn = new String[attachmentNames.length];
- for (int i = 0; i < atn.length; ++i) {
- atn[i] = head(attachmentNames[i]);
- if (atn[i].length() == 0) {
- atn[i] = tail(attachmentNames[i], "");
- } else {
- atn[i] = atn[i].concat(tail(attachmentNames[i], ""));
- }
- }
- }
-
- setIncompleteCopy(abort); //Original body part is never added.
- envelopeFor(abort, true);
- saveChangesNoContent(abort, msg);
- 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) {
- //Switching the CCL emulates the current send behavior.
- Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- try {
- t = session.getTransport();
- } catch (final MessagingException fail) {
- throw attach(protocol, fail);
- } finally {
- getAndSetContextClassLoader(ccl);
- }
- }
-
- String local = null;
- if ("remote".equals(verify) || "login".equals(verify)) {
- MessagingException closed = null;
- t.connect();
- try {
- try {
- //Capture localhost while connection is open.
- local = getLocalHost(t);
-
- //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.
- if ("remote".equals(verify)) {
- t.sendMessage(abort, all);
- }
- } finally {
- try {
- t.close();
- } catch (final MessagingException ME) {
- closed = ME;
- }
- }
- //Close the transport before reporting errors.
- if ("remote".equals(verify)) {
- reportUnexpectedSend(abort, verify, null);
- } else {
- final String protocol = t.getURLName().getProtocol();
- verifyProperties(session, protocol);
- }
- } catch (final SendFailedException sfe) {
- Address[] recip = sfe.getInvalidAddresses();
- if (recip != null && recip.length != 0) {
- setErrorContent(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)) {
- setErrorContent(abort, verify, ME);
- reportError(abort, ME, ErrorManager.OPEN_FAILURE);
- }
- }
-
- if (closed != null) {
- setErrorContent(abort, verify, closed);
- reportError(abort, closed, ErrorManager.CLOSE_FAILURE);
- }
- } else {
- //Force a property copy, JDK-7092981.
- final String protocol = t.getURLName().getProtocol();
- verifyProperties(session, protocol);
- String mailHost = session.getProperty("mail."
- + protocol + ".host");
- if (isEmpty(mailHost)) {
- mailHost = session.getProperty("mail.host");
- } else {
- session.getProperty("mail.host");
- }
-
- local = session.getProperty("mail." + protocol + ".localhost");
- if (isEmpty(local)) {
- local = session.getProperty("mail."
- + protocol + ".localaddress");
- } else {
- session.getProperty("mail." + protocol + ".localaddress");
- }
-
- if ("resolve".equals(verify)) {
- try { //Resolve the remote host name.
- String transportHost = t.getURLName().getHost();
- if (!isEmpty(transportHost)) {
- verifyHost(transportHost);
- if (!transportHost.equalsIgnoreCase(mailHost)) {
- verifyHost(mailHost);
- }
- } else {
- verifyHost(mailHost);
- }
- } catch (final RuntimeException | IOException IOE) {
- MessagingException ME =
- new MessagingException(msg, IOE);
- setErrorContent(abort, verify, ME);
- reportError(abort, ME, ErrorManager.OPEN_FAILURE);
- }
- }
- }
-
- if (!"limited".equals(verify)) {
- try { //Verify host name and hit the host name cache.
- if (!"remote".equals(verify) && !"login".equals(verify)) {
- local = getLocalHost(t);
- }
- verifyHost(local);
- } catch (final RuntimeException | IOException IOE) {
- MessagingException ME = new MessagingException(msg, IOE);
- setErrorContent(abort, verify, ME);
- reportError(abort, ME, ErrorManager.OPEN_FAILURE);
- }
-
- try { //Verify that the DataHandler can be loaded.
- Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- try {
- //Always load the multipart classes.
- MimeMultipart multipart = new MimeMultipart();
- MimeBodyPart[] ambp = new MimeBodyPart[atn.length];
- final MimeBodyPart body;
- final String bodyContentType;
- synchronized (this) {
- bodyContentType = contentTypeOf(getFormatter());
- body = createBodyPart();
- for (int i = 0; i < atn.length; ++i) {
- ambp[i] = createBodyPart(i);
- ambp[i].setFileName(atn[i]);
- //Convert names to mime type under lock.
- atn[i] = getContentType(atn[i]);
- }
- }
-
- body.setDescription(verify);
- setContent(body, "", bodyContentType);
- multipart.addBodyPart(body);
- for (int i = 0; i < ambp.length; ++i) {
- ambp[i].setDescription(verify);
- setContent(ambp[i], "", atn[i]);
- }
-
- abort.setContent(multipart);
- abort.saveChanges();
- abort.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE));
- } finally {
- getAndSetContextClassLoader(ccl);
- }
- } catch (final IOException IOE) {
- MessagingException ME = new MessagingException(msg, IOE);
- setErrorContent(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 RuntimeException RE) {
- setErrorContent(abort, verify, RE);
- reportError(abort, RE, ErrorManager.OPEN_FAILURE);
- } catch (final Exception ME) {
- setErrorContent(abort, verify, ME);
- reportError(abort, ME, ErrorManager.OPEN_FAILURE);
- }
- }
-
- /**
- * Handles all exceptions thrown when save changes is called on a message
- * that doesn't have any content.
- *
- * @param abort the message requiring save changes.
- * @param msg the error description.
- * @since JavaMail 1.6.0
- */
- private void saveChangesNoContent(final Message abort, final String msg) {
- if (abort != null) {
- try {
- try {
- abort.saveChanges();
- } catch (final NullPointerException xferEncoding) {
- //Workaround GNU JavaMail bug in MimeUtility.getEncoding
- //when the mime message has no content.
- try {
- String cte = "Content-Transfer-Encoding";
- if (abort.getHeader(cte) == null) {
- abort.setHeader(cte, "base64");
- abort.saveChanges();
- } else {
- throw xferEncoding;
- }
- } catch (RuntimeException | MessagingException e) {
- if (e != xferEncoding) {
- e.addSuppressed(xferEncoding);
- }
- throw e;
- }
- }
- } catch (RuntimeException | MessagingException ME) {
- reportError(msg, ME, ErrorManager.FORMAT_FAILURE);
- }
- }
- }
-
- /**
- * Cache common session properties into the LogManagerProperties. This is
- * a workaround for JDK-7092981.
- *
- * @param session the session.
- * @param protocol the mail protocol.
- * @throws NullPointerException if session is null.
- * @since JavaMail 1.6.0
- */
- private static void verifyProperties(Session session, String protocol) {
- session.getProperty("mail.from");
- session.getProperty("mail." + protocol + ".from");
- session.getProperty("mail.dsn.ret");
- session.getProperty("mail." + protocol + ".dsn.ret");
- session.getProperty("mail.dsn.notify");
- session.getProperty("mail." + protocol + ".dsn.notify");
- session.getProperty("mail." + protocol + ".port");
- session.getProperty("mail.user");
- session.getProperty("mail." + protocol + ".user");
- session.getProperty("mail." + protocol + ".localport");
- }
-
- /**
- * 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.
- * @throws SecurityException if security manager is present and doesn't
- * allow access to check connect permission.
- * @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);
- setErrorContent(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 setErrorContent(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 (MessagingException | RuntimeException ME) {
- reportError("Unable to create body.", ME, 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 updateSession() {
- assert Thread.holdsLock(this);
- final Session settings;
- if (mailProps.getProperty("verify") != null) {
- settings = initSession();
- assert settings == session : session;
- } else {
- session = null; //Remove old session.
- settings = null;
- }
- return settings;
- }
-
- /**
- * Creates a session using a proxy properties object.
- * @return the session that was created and assigned.
- */
- 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);
- }
- }
-
- /**
- * Factory to create the in-line body part.
- * @return a body part with default headers set.
- * @throws MessagingException if there is a problem.
- */
- 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;
- }
-
- /**
- * Factory to create the attachment body part.
- * @param index the attachment index.
- * @return a body part with default headers set.
- * @throws MessagingException if there is a problem.
- * @throws IndexOutOfBoundsException if the given index is not an valid
- * attachment index.
- */
- 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()) + '.';
- }
-
- /**
- * Creates a description for a body part.
- * @param f the content formatter.
- * @param filter the content filter.
- * @param name the naming formatter.
- * @return the description for the body part.
- */
- 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, String chunk) {
- try {
- //Remove all control character groups.
- chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", "");
- 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, String chunk) {
- try {
- //Remove all control character groups.
- chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", "");
- final String charset = getEncodingName();
- final String old = msg.getSubject();
- assert msg instanceof MimeMessage : msg;
- ((MimeMessage) msg).setSubject(old != null ? old.concat(chunk)
- : chunk, MimeUtility.mimeCharset(charset));
- } 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 Formatter f = createSimpleFormatter();
- 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);
- }
-
- /**
- * Used to outline the bytes to report a null pointer exception.
- * See BUD ID 6533165.
- * @param code the ErrorManager code.
- */
- private void reportNullError(final int code) {
- reportError("null", new NullPointerException(), code);
- }
-
- /**
- * Creates the head or reports a formatting error.
- * @param f the formatter.
- * @return the head string or an empty string.
- */
- private String head(final Formatter f) {
- try {
- return f.getHead(this);
- } catch (final RuntimeException RE) {
- reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
- return "";
- }
- }
-
- /**
- * Creates the formatted log record or reports a formatting error.
- * @param f the formatter.
- * @param r the log record.
- * @return the formatted string or an empty string.
- */
- 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 "";
- }
- }
-
- /**
- * Creates the tail or reports a formatting error.
- * @param f the formatter.
- * @param def the default string to use when there is an error.
- * @return the tail string or the given default string.
- */
- 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;
- }
- }
-
- /**
- * Sets the x-mailer header.
- * @param msg the target message.
- */
- 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);
- }
- }
-
- /**
- * Sets the priority and importance headers.
- * @param msg the target message.
- */
- 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) {
- if (allowRestrictedHeaders()) {
- try { //RFC 3834 (5.2)
- msg.setHeader("auto-submitted", "auto-generated");
- } catch (final MessagingException ME) {
- reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
- }
- }
- }
-
- /**
- * Sets from address header.
- * @param msg the target message.
- */
- private void setFrom(final Message msg) {
- final String from = getSession(msg).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);
- }
- }
-
- /**
- * Sets the from header to the local address.
- * @param msg the target message.
- */
- 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(getSession(msg));
- if (a != null) {
- msg.setRecipient(type, a);
- } else {
- final MimeMessage m = new MimeMessage(getSession(msg));
- 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 (MessagingException | RuntimeException ME) {
- reportError("Unable to compute a default recipient.",
- ME, ErrorManager.FORMAT_FAILURE);
- }
- }
-
- /**
- * Sets reply-to address header.
- * @param msg the target message.
- */
- private void setReplyTo(final Message msg) {
- final String reply = getSession(msg).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);
- }
- }
- }
-
- /**
- * Sets sender address header.
- * @param msg the target message.
- */
- private void setSender(final Message msg) {
- assert msg instanceof MimeMessage : msg;
- final String sender = getSession(msg).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);
- }
- }
- }
-
- /**
- * A common factory used to create the too many addresses exception.
- * @param address the addresses, never null.
- * @param offset the starting address to display.
- * @return the too many addresses exception.
- */
- 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 = getSession(msg).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) {
- Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
- try { //JDK-8025251
- int nbytes = Math.max(msg.getSize() + MIN_HEADER_SIZE, MIN_HEADER_SIZE);
- ByteArrayOutputStream out = new ByteArrayOutputStream(nbytes);
- msg.writeTo(out); //Headers can be UTF-8 or US-ASCII.
- return out.toString("UTF-8");
- } finally {
- getAndSetContextClassLoader(ccl);
- }
- } 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 charset = getEncodingName();
- try {
- final ByteArrayOutputStream out =
- new ByteArrayOutputStream(MIN_HEADER_SIZE);
-
- //Create an output stream writer so streams are not double buffered.
- try (OutputStreamWriter ows = new OutputStreamWriter(out, charset);
- PrintWriter pw = new PrintWriter(ows)) {
- pw.println(t.getMessage());
- t.printStackTrace(pw);
- pw.flush();
- } //Close OSW before generating string. JDK-6995537
- return out.toString(charset);
- } catch (final RuntimeException unexpected) {
- return t.toString() + ' ' + unexpected.toString();
- } catch (final Exception badMimeCharset) {
- return t.toString() + ' ' + badMimeCharset.toString();
- }
- }
-
- /**
- * Replaces the current context class loader with our class loader.
- * @param ccl 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.
- * @return null for the boot class loader, a class loader, or a marker
- * object to signal that no modification was required.
- * @since JavaMail 1.5.3
- */
- private Object getAndSetContextClassLoader(final Object ccl) {
- if (ccl != GetAndSetContext.NOT_MODIFIED) {
- try {
- final PrivilegedAction> pa;
- if (ccl instanceof PrivilegedAction) {
- pa = (PrivilegedAction>) ccl;
- } else {
- pa = new GetAndSetContext(ccl);
- }
- return AccessController.doPrivileged(pa);
- } catch (final SecurityException ignore) {
- }
- }
- return GetAndSetContext.NOT_MODIFIED;
- }
-
- /**
- * A factory used to create a common attachment mismatch type.
- * @param msg the exception message.
- * @return a RuntimeException to represent the type of error.
- */
- private static RuntimeException attachmentMismatch(final String msg) {
- return new IndexOutOfBoundsException(msg);
- }
-
- /**
- * Outline the attachment mismatch message. See Bug ID 6533165.
- * @param expected the expected array length.
- * @param found the array length that was given.
- * @return a RuntimeException populated with a message.
- */
- private static RuntimeException attachmentMismatch(int expected, int found) {
- return attachmentMismatch("Attachments mismatched, expected "
- + expected + " but given " + found + '.');
- }
-
- /**
- * Try to attach a suppressed exception to a MessagingException in any order
- * that is possible.
- * @param required the exception expected to see as a reported failure.
- * @param optional the suppressed exception.
- * @return either the required or the optional exception.
- */
- 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;
- }
- }
-
- if (optional != required) {
- required.addSuppressed(optional);
- }
- }
- return required;
- }
-
- /**
- * Gets the local host from the given service object.
- * @param s the service to check.
- * @return the local host or null.
- * @since JavaMail 1.5.3
- */
- private String getLocalHost(final Service s) {
- try {
- return LogManagerProperties.getLocalHost(s);
- } catch (SecurityException | NoSuchMethodException
- | LinkageError ignore) {
- } catch (final Exception ex) {
- reportError(s.toString(), ex, ErrorManager.OPEN_FAILURE);
- }
- return null;
- }
-
- /**
- * Google App Engine doesn't support Message.getSession.
- * @param msg the message.
- * @return the session from the given message.
- * @throws NullPointerException if the given message is null.
- * @since JavaMail 1.5.3
- */
- private Session getSession(final Message msg) {
- if (msg == null) {
- throw new NullPointerException();
- }
- return new MessageContext(msg).getSession();
- }
-
- /**
- * Determines if restricted headers are allowed in the current environment.
- *
- * @return true if restricted headers are allowed.
- * @since JavaMail 1.5.3
- */
- private boolean allowRestrictedHeaders() {
- //GAE will prevent delivery of email with forbidden headers.
- //Assume the environment is GAE if access to the LogManager is
- //forbidden.
- return LogManagerProperties.hasLogManager();
- }
-
- /**
- * Outline the creation of the index error message. See JDK-6533165.
- * @param i the index.
- * @return the error message.
- */
- 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 {
-
- /**
- * Creates an Authenticator for the given password. This method is used
- * so class verification of assignments in MailHandler doesn't require
- * loading this class which otherwise can occur when using the
- * constructor. Default access to avoid generating extra class files.
- *
- * @param pass the password.
- * @return an Authenticator for the password.
- * @since JavaMail 1.5.6
- */
- static Authenticator of(final String pass) {
- return new DefaultAuthenticator(pass);
- }
-
- /**
- * The password to use.
- */
- private final String pass;
-
- /**
- * Use the factory method instead of this constructor.
- * @param pass the password.
- */
- private 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 {
- /**
- * A marker object used to signal that the class loader was not
- * modified.
- */
- public static final Object NOT_MODIFIED = GetAndSetContext.class;
- /**
- * 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. Default access to avoid generating extra class files.
- */
- 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
- * NOT_MODIFIED to indicate that nothing was modified.
- */
- @SuppressWarnings("override") //JDK-6954234
- 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 if (source instanceof Thread) {
- loader = ((Thread) source).getContextClassLoader();
- } else {
- assert !(source instanceof Class) : source;
- loader = source.getClass().getClassLoader();
- }
-
- if (ccl != loader) {
- current.setContextClassLoader(loader);
- return ccl;
- } else {
- return NOT_MODIFIED;
- }
- }
- }
-
- /**
- * Used for naming attachment file names and the main subject line.
- */
- private static final class TailNameFormatter extends Formatter {
-
- /**
- * Creates or gets a formatter from the given name. This method is used
- * so class verification of assignments in MailHandler doesn't require
- * loading this class which otherwise can occur when using the
- * constructor. Default access to avoid generating extra class files.
- *
- * @param name any not null string.
- * @return a formatter for that string.
- * @since JavaMail 1.5.6
- */
- static Formatter of(final String name) {
- return new TailNameFormatter(name);
- }
-
- /**
- * The value used as the output.
- */
- private final String name;
-
- /**
- * Use the factory method instead of this constructor.
- * @param name any not null string.
- */
- private TailNameFormatter(final String name) {
- assert name != null;
- this.name = name;
- }
-
- @Override
- 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/app/src/main/java/com/sun/mail/util/logging/SeverityComparator.java b/app/src/main/java/com/sun/mail/util/logging/SeverityComparator.java
deleted file mode 100644
index 338a780bff..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/SeverityComparator.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, 2018 Jason Mehrens. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-package com.sun.mail.util.logging;
-
-import java.io.Serializable;
-import java.util.Comparator;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-
-/**
- * Orders log records by level, thrown, sequence, and time.
- *
- * This comparator orders LogRecords by how severely each is attributed to
- * failures in a program. The primary ordering is determined by the use of the
- * logging API throughout a program by specifying a level to each log message.
- * The secondary ordering is determined at runtime by the type of errors and
- * exceptions generated by the program. The remaining ordering assumes that
- * older log records are less severe than newer log records.
- *
- *
- * The following LogRecord properties determine severity ordering:
- *
- * The natural comparison of the LogRecord
- * {@linkplain Level#intValue level}.
- * The expected recovery order of {@linkplain LogRecord#getThrown() thrown}
- * property of a LogRecord and its cause chain. This ordering is derived from
- * the JLS 11.1.1. The Kinds of Exceptions and JLS 11.5 The Exception Hierarchy.
- * This is performed by {@linkplain #apply(java.lang.Throwable) finding} the
- * throwable that best describes the entire cause chain. Once a specific
- * throwable of each chain is identified it is then ranked lowest to highest by
- * the following rules:
- *
- *
- * All LogRecords with a {@code Throwable} defined as
- * "{@link #isNormal(java.lang.Throwable) normal occurrence}".
- * All LogRecords that do not have a thrown object.
- * All checked exceptions. This is any class that is assignable to the
- * {@code java.lang.Throwable} class and is not a
- * {@code java.lang.RuntimeException} or a {@code java.lang.Error}.
- * All unchecked exceptions. This is all {@code java.lang.RuntimeException}
- * objects.
- * All errors that indicate a serious problem. This is all
- * {@code java.lang.Error} objects.
- *
- * The natural comparison of the LogRecord
- * {@linkplain LogRecord#getSequenceNumber() sequence}.
- * The natural comparison of the LogRecord
- * {@linkplain LogRecord#getMillis() millis}.
- *
- *
- * @author Jason Mehrens
- * @since JavaMail 1.5.2
- */
-public class SeverityComparator implements Comparator, Serializable {
-
- /**
- * The generated serial version UID.
- */
- private static final long serialVersionUID = -2620442245251791965L;
-
- /**
- * A single instance that is shared among the logging package.
- * The field is declared as java.util.Comparator so
- * WebappClassLoader.clearReferencesStaticFinal() method will ignore this
- * field.
- */
- private static final Comparator INSTANCE
- = new SeverityComparator();
-
- /**
- * A shared instance of a SeverityComparator. This is package private so the
- * public API is not polluted with more methods.
- *
- * @return a shared instance of a SeverityComparator.
- */
- static SeverityComparator getInstance() {
- return (SeverityComparator) INSTANCE;
- }
-
- /**
- * Identifies a single throwable that best describes the given throwable and
- * the entire {@linkplain Throwable#getCause() cause} chain. This method can
- * be overridden to change the behavior of
- * {@link #compare(java.util.logging.LogRecord, java.util.logging.LogRecord)}.
- *
- * @param chain the throwable or null.
- * @return null if null was given, otherwise the throwable that best
- * describes the entire chain.
- * @see #isNormal(java.lang.Throwable)
- */
- public Throwable apply(final Throwable chain) {
- //Matches the j.u.f.UnaryOperator interface.
- int limit = 0;
- Throwable root = chain;
- Throwable high = null;
- Throwable normal = null;
- for (Throwable cause = chain; cause != null; cause = cause.getCause()) {
- root = cause; //Find the deepest cause.
-
- //Find the deepest nomral occurrance.
- if (isNormal(cause)) {
- normal = cause;
- }
-
- //Find the deepest error that happened before a normal occurance.
- if (normal == null && cause instanceof Error) {
- high = cause;
- }
-
- //Deal with excessive cause chains and cyclic throwables.
- if (++limit == (1 << 16)) {
- break; //Give up.
- }
- }
- return high != null ? high : normal != null ? normal : root;
- }
-
- /**
- * {@link #apply(java.lang.Throwable) Reduces} each throwable chain argument
- * then compare each throwable result.
- *
- * @param tc1 the first throwable chain or null.
- * @param tc2 the second throwable chain or null.
- * @return a negative integer, zero, or a positive integer as the first
- * argument is less than, equal to, or greater than the second.
- * @see #apply(java.lang.Throwable)
- * @see #compareThrowable(java.lang.Throwable, java.lang.Throwable)
- */
- public final int applyThenCompare(Throwable tc1, Throwable tc2) {
- return tc1 == tc2 ? 0 : compareThrowable(apply(tc1), apply(tc2));
- }
-
- /**
- * Compares two throwable objects or null. This method does not
- * {@link #apply(java.lang.Throwable) reduce} each argument before
- * comparing. This is method can be overridden to change the behavior of
- * {@linkplain #compare(LogRecord, LogRecord)}.
- *
- * @param t1 the first throwable or null.
- * @param t2 the second throwable or null.
- * @return a negative integer, zero, or a positive integer as the first
- * argument is less than, equal to, or greater than the second.
- * @see #isNormal(java.lang.Throwable)
- */
- public int compareThrowable(final Throwable t1, final Throwable t2) {
- if (t1 == t2) { //Reflexive test including null.
- return 0;
- } else {
- //Only one or the other is null at this point.
- //Force normal occurrence to be lower than null.
- if (t1 == null) {
- return isNormal(t2) ? 1 : -1;
- } else {
- if (t2 == null) {
- return isNormal(t1) ? -1 : 1;
- }
- }
-
- //From this point on neither are null.
- //Follow the shortcut if we can.
- if (t1.getClass() == t2.getClass()) {
- return 0;
- }
-
- //Ensure normal occurrence flow control is ordered low.
- if (isNormal(t1)) {
- return isNormal(t2) ? 0 : -1;
- } else {
- if (isNormal(t2)) {
- return 1;
- }
- }
-
- //Rank the two unidenticial throwables using the rules from
- //JLS 11.1.1. The Kinds of Exceptions and
- //JLS 11.5 The Exception Hierarchy.
- if (t1 instanceof Error) {
- return t2 instanceof Error ? 0 : 1;
- } else if (t1 instanceof RuntimeException) {
- return t2 instanceof Error ? -1
- : t2 instanceof RuntimeException ? 0 : 1;
- } else {
- return t2 instanceof Error
- || t2 instanceof RuntimeException ? -1 : 0;
- }
- }
- }
-
- /**
- * Compares two log records based on severity.
- *
- * @param o1 the first log record.
- * @param o2 the second log record.
- * @return a negative integer, zero, or a positive integer as the first
- * argument is less than, equal to, or greater than the second.
- * @throws NullPointerException if either argument is null.
- */
- @SuppressWarnings("override") //JDK-6954234
- public int compare(final LogRecord o1, final LogRecord o2) {
- if (o1 == null || o2 == null) { //Don't allow null.
- throw new NullPointerException(toString(o1, o2));
- }
-
- /**
- * LogRecords are mutable so a reflexive relationship test is a safety
- * requirement.
- */
- if (o1 == o2) {
- return 0;
- }
-
- int cmp = compare(o1.getLevel(), o2.getLevel());
- if (cmp == 0) {
- cmp = applyThenCompare(o1.getThrown(), o2.getThrown());
- if (cmp == 0) {
- cmp = compare(o1.getSequenceNumber(), o2.getSequenceNumber());
- if (cmp == 0) {
- cmp = compare(o1.getMillis(), o2.getMillis());
- }
- }
- }
- return cmp;
- }
-
- /**
- * Determines if the given object is also a comparator and it imposes the
- * same ordering as this comparator.
- *
- * @param o the reference object with which to compare.
- * @return true if this object equal to the argument; false otherwise.
- */
- @Override
- public boolean equals(final Object o) {
- return o == null ? false : o.getClass() == getClass();
- }
-
- /**
- * Returns a hash code value for the object.
- *
- * @return Returns a hash code value for the object.
- */
- @Override
- public int hashCode() {
- return 31 * getClass().hashCode();
- }
-
- /**
- * Determines if the given throwable instance is "normal occurrence". This
- * is any checked or unchecked exception with 'Interrupt' in the class name
- * or ancestral class name. Any {@code java.lang.ThreadDeath} object or
- * subclasses.
- *
- * This method can be overridden to change the behavior of the
- * {@linkplain #apply(java.lang.Throwable)} method.
- *
- * @param t a throwable or null.
- * @return true the given throwable is a "normal occurrence".
- */
- public boolean isNormal(final Throwable t) {
- if (t == null) { //This is only needed when called directly.
- return false;
- }
-
- /**
- * Use the class names to avoid loading more classes.
- */
- final Class> root = Throwable.class;
- final Class> error = Error.class;
- for (Class> c = t.getClass(); c != root; c = c.getSuperclass()) {
- if (error.isAssignableFrom(c)) {
- if (c.getName().equals("java.lang.ThreadDeath")) {
- return true;
- }
- } else {
- //Interrupt, Interrupted or Interruption.
- if (c.getName().contains("Interrupt")) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Compare two level objects.
- *
- * @param a the first level.
- * @param b the second level.
- * @return a negative integer, zero, or a positive integer as the first
- * argument is less than, equal to, or greater than the second.
- */
- private int compare(final Level a, final Level b) {
- return a == b ? 0 : compare(a.intValue(), b.intValue());
- }
-
- /**
- * Outline the message create string.
- *
- * @param o1 argument one.
- * @param o2 argument two.
- * @return the message string.
- */
- private static String toString(final Object o1, final Object o2) {
- return o1 + ", " + o2;
- }
-
- /**
- * Compare two longs. Can be removed when JDK 1.7 is required.
- *
- * @param x the first long.
- * @param y the second long.
- * @return a negative integer, zero, or a positive integer as the first
- * argument is less than, equal to, or greater than the second.
- */
- private int compare(final long x, final long y) {
- return x < y ? -1 : x > y ? 1 : 0;
- }
-}
diff --git a/app/src/main/java/com/sun/mail/util/logging/package.html b/app/src/main/java/com/sun/mail/util/logging/package.html
deleted file mode 100644
index fcde4a4ac3..0000000000
--- a/app/src/main/java/com/sun/mail/util/logging/package.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
- Contains Jakarta Mail extensions for
- the Java™ 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/app/src/main/java/com/sun/mail/util/package.html b/app/src/main/java/com/sun/mail/util/package.html
deleted file mode 100644
index 6f809d1f5c..0000000000
--- a/app/src/main/java/com/sun/mail/util/package.html
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-com.sun.mail.util package
-
-
-
-
-Utility classes for use with the Jakarta Mail API.
-These utility classes are not part of the Jakarta Mail specification.
-While this package contains many classes used by the Jakarta Mail 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:
-
-
-com.sun.mail.util Loggers
-
-Logger Name
-Logging Level
-Purpose
-
-
-
-com.sun.mail.util.socket
-FINER
-Debugging 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/app/src/main/java/javax/mail/Address.java b/app/src/main/java/javax/mail/Address.java
deleted file mode 100644
index 2b87cf4e30..0000000000
--- a/app/src/main/java/javax/mail/Address.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- */
- @Override
- 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
- */
- @Override
- public abstract boolean equals(Object address);
-}
diff --git a/app/src/main/java/javax/mail/AuthenticationFailedException.java b/app/src/main/java/javax/mail/AuthenticationFailedException.java
deleted file mode 100644
index e0de809e6f..0000000000
--- a/app/src/main/java/javax/mail/AuthenticationFailedException.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/javax/mail/Authenticator.java b/app/src/main/java/javax/mail/Authenticator.java
deleted file mode 100644
index 5fa783801c..0000000000
--- a/app/src/main/java/javax/mail/Authenticator.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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;
-
- /**
- * 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 synchronized PasswordAuthentication requestPasswordAuthentication(
- InetAddress addr, int port, String protocol,
- String prompt, String defaultUserName) {
- 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/app/src/main/java/javax/mail/BodyPart.java b/app/src/main/java/javax/mail/BodyPart.java
deleted file mode 100644
index bb94f75e71..0000000000
--- a/app/src/main/java/javax/mail/BodyPart.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @return the parent Multipart
- */
- 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/app/src/main/java/javax/mail/EncodingAware.java b/app/src/main/java/javax/mail/EncodingAware.java
deleted file mode 100644
index 27efc70051..0000000000
--- a/app/src/main/java/javax/mail/EncodingAware.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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/app/src/main/java/javax/mail/EventQueue.java b/app/src/main/java/javax/mail/EventQueue.java
deleted file mode 100644
index 618f872c8e..0000000000
--- a/app/src/main/java/javax/mail/EventQueue.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package javax.mail;
-
-import java.util.EventListener;
-import java.util.Vector;
-import java.util.Queue;
-import java.util.WeakHashMap;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.Executor;
-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.
- *
- * @author Bill Shannon
- */
-class EventQueue implements Runnable {
-
- private volatile BlockingQueue q;
- private Executor executor;
-
- private static WeakHashMap appq;
-
- /**
- * A special event that causes the queue processing task to terminate.
- */
- static class TerminatorEvent extends MailEvent {
- private static final long serialVersionUID = -2481895000841664111L;
-
- TerminatorEvent() {
- super(new Object());
- }
-
- @Override
- public void dispatch(Object listener) {
- // Kill the event dispatching thread.
- Thread.currentThread().interrupt();
- }
- }
-
- /**
- * A "struct" to put on the queue.
- */
- static class QueueElement {
- MailEvent event = null;
- Vector extends EventListener> vector = null;
-
- QueueElement(MailEvent event, Vector extends EventListener> vector) {
- this.event = event;
- this.vector = vector;
- }
- }
-
- /**
- * Construct an EventQueue using the specified Executor.
- * If the Executor is null, threads will be created as needed.
- */
- EventQueue(Executor ex) {
- this.executor = ex;
- }
-
- /**
- * Enqueue an event.
- */
- synchronized void enqueue(MailEvent event,
- Vector extends EventListener> vector) {
- // if this is the first event, create the queue and start the event task
- if (q == null) {
- q = new LinkedBlockingQueue<>();
- if (executor != null) {
- executor.execute(this);
- } else {
- Thread qThread = new Thread(this, "Jakarta-Mail-EventQueue");
- qThread.setDaemon(true); // not a user thread
- qThread.start();
- }
- }
- q.add(new QueueElement(event, vector));
- }
-
- /**
- * Terminate the task running the queue, but only if there is a queue.
- */
- synchronized void terminateQueue() {
- if (q != null) {
- Vector dummyListeners = new Vector<>();
- dummyListeners.setSize(1); // need atleast one listener
- q.add(new QueueElement(new TerminatorEvent(), dummyListeners));
- q = null;
- }
- }
-
- /**
- * Create (if necessary) an application-scoped event queue.
- * Application scoping is based on the thread's context class loader.
- */
- static synchronized EventQueue getApplicationEventQueue(Executor ex) {
- ClassLoader cl = Session.getContextClassLoader();
- if (appq == null)
- appq = new WeakHashMap<>();
- EventQueue q = appq.get(cl);
- if (q == null) {
- q = new EventQueue(ex);
- appq.put(cl, q);
- }
- return q;
- }
-
- /**
- * Pull events off the queue and dispatch them.
- */
- @Override
- public void run() {
-
- BlockingQueue bq = q;
- if (bq == null)
- return;
- try {
- loop:
- for (;;) {
- // block until an item is available
- QueueElement qe = bq.take();
- MailEvent e = qe.event;
- Vector extends EventListener> 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
- }
- }
-}
diff --git a/app/src/main/java/javax/mail/FetchProfile.java b/app/src/main/java/javax/mail/FetchProfile.java
deleted file mode 100644
index 1b494545fe..0000000000
--- a/app/src/main/java/javax/mail/FetchProfile.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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.
- *
- * @param name the item name
- */
- protected Item(String name) {
- this.name = name;
- }
-
- /**
- * Include the name in the toString return value for debugging.
- */
- @Override
- 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 the given special item.
- *
- * @param item the Item to test
- * @return true if the fetch profile contains the given special item
- */
- public boolean contains(Item item) {
- return specials != null && specials.contains(item);
- }
-
- /**
- * Returns true if the fetch profile contains the given header name.
- *
- * @param headerName the header to test
- * @return true if the fetch profile contains the 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/app/src/main/java/javax/mail/Flags.java b/app/src/main/java/javax/mail/Flags.java
deleted file mode 100644
index 68e88b76f8..0000000000
--- a/app/src/main/java/javax/mail/Flags.java
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-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
- * Jakarta Mail 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;
- // used as a case-independent Set that preserves the original case,
- // the key is the lowercase flag name and the value is the original
- 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 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
- */
- @SuppressWarnings("unchecked")
- 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 = 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());
- }
- }
-
- /**
- * Remove any flags not in the given Flags object.
- * Useful for clearing flags not supported by a server. If the
- * given Flags object includes the Flags.Flag.USER flag, all user
- * flags in this Flags object are retained.
- *
- * @param f the flags to keep
- * @return true if this Flags object changed
- * @since JavaMail 1.6
- */
- public boolean retainAll(Flags f) {
- boolean changed = false;
- int sf = system_flags & f.system_flags;
- if (system_flags != sf) {
- system_flags = sf;
- changed = true;
- }
-
- // if we have user flags, and the USER flag is not set in "f",
- // determine which user flags to clear
- if (user_flags != null && (f.system_flags & USER_BIT) == 0) {
- if (f.user_flags != null) {
- Enumeration e = user_flags.keys();
- while (e.hasMoreElements()) {
- String key = e.nextElement();
- if (!f.user_flags.containsKey(key)) {
- user_flags.remove(key);
- changed = true;
- }
- }
- } else {
- // if anything in user_flags, throw them away
- changed = user_flags.size() > 0;
- user_flags = null;
- }
- }
- return changed;
- }
-
- /**
- * Check whether the specified system flag is present in this Flags object.
- *
- * @param flag the flag to test
- * @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.
- *
- * @param flag the flag to test
- * @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.
- *
- * @param f the flags to test
- * @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
- */
- @Override
- 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
- int size = this.user_flags == null ? 0 : this.user_flags.size();
- int fsize = f.user_flags == null ? 0 : f.user_flags.size();
- if (size == 0 && fsize == 0)
- return true;
- if (f.user_flags != null && this.user_flags != null && fsize == size)
- return user_flags.keySet().equals(f.user_flags.keySet());
-
- return false;
- }
-
- /**
- * Compute a hash code for this Flags object.
- *
- * @return the hash code
- */
- @Override
- public int hashCode() {
- int hash = system_flags;
- if (user_flags != null) {
- Enumeration e = user_flags.keys();
- while (e.hasMoreElements())
- hash += 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;
- }
-
- /**
- * Clear all of the system flags.
- *
- * @since JavaMail 1.6
- */
- public void clearSystemFlags() {
- system_flags = 0;
- }
-
- /**
- * Clear all of the user flags.
- *
- * @since JavaMail 1.6
- */
- public void clearUserFlags() {
- user_flags = null;
- }
-
- /**
- * Returns a clone of this Flags object.
- */
- @SuppressWarnings("unchecked")
- @Override
- 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;
- }
-
- /**
- * Return a string representation of this Flags object.
- * Note that the exact format of the string is subject to change.
- */
- public String toString() {
- StringBuilder sb = new StringBuilder();
-
- if ((system_flags & ANSWERED_BIT) != 0)
- sb.append("\\Answered ");
- if ((system_flags & DELETED_BIT) != 0)
- sb.append("\\Deleted ");
- if ((system_flags & DRAFT_BIT) != 0)
- sb.append("\\Draft ");
- if ((system_flags & FLAGGED_BIT) != 0)
- sb.append("\\Flagged ");
- if ((system_flags & RECENT_BIT) != 0)
- sb.append("\\Recent ");
- if ((system_flags & SEEN_BIT) != 0)
- sb.append("\\Seen ");
- if ((system_flags & USER_BIT) != 0)
- sb.append("\\* ");
-
- boolean first = true;
- if (user_flags != null) {
- Enumeration e = user_flags.elements();
-
- while (e.hasMoreElements()) {
- if (first)
- first = false;
- else
- sb.append(' ');
- sb.append(e.nextElement());
- }
- }
-
- if (first && sb.length() > 0)
- sb.setLength(sb.length() - 1); // smash trailing space
-
- return sb.toString();
- }
-
- /*****
- 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/app/src/main/java/javax/mail/Folder.java b/app/src/main/java/javax/mail/Folder.java
deleted file mode 100644
index 0ddce216fc..0000000000
--- a/app/src/main/java/javax/mail/Folder.java
+++ /dev/null
@@ -1,1657 +0,0 @@
-/*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package javax.mail;
-
-import java.lang.*;
-import java.util.ArrayList;
-import java.util.EventListener;
-import java.util.List;
-import java.util.Vector;
-import java.util.concurrent.Executor;
-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 this
- * 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 implements AutoCloseable {
-
- /**
- * 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;
-
- /*
- * The queue of events to be delivered.
- */
- private final EventQueue q;
-
- /**
- * Constructor that takes a Store object.
- *
- * @param store the Store that holds this folder
- */
- protected Folder(Store store) {
- this.store = store;
-
- // create or choose the appropriate event queue
- Session session = store.getSession();
- String scope =
- session.getProperties().getProperty("mail.event.scope", "folder");
- Executor executor =
- (Executor)session.getProperties().get("mail.event.executor");
- if (scope.equalsIgnoreCase("application"))
- q = EventQueue.getApplicationEventQueue(executor);
- else if (scope.equalsIgnoreCase("session"))
- q = session.getEventQueue();
- else if (scope.equalsIgnoreCase("store"))
- q = store.getEventQueue();
- else // if (scope.equalsIgnoreCase("folder"))
- q = new EventQueue(executor);
- }
-
- /**
- * 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
- * @exception MessagingException for failures
- * @see URLName
- * @since JavaMail 1.1
- */
- public URLName getURLName() throws MessagingException {
- URLName storeURL = getStore().getURLName();
- String fullname = getFullName();
- StringBuilder encodedName = new StringBuilder();
-
- 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
- * @exception MessagingException for failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
-
- 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 for other failures
- */
- 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 for failures
- * @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 for other failures
- */
- 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 for other failures
- */
- 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 for failures
- */
- 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:
- *
- *
- *
- * @param recurse also delete subfolders?
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @see javax.mail.event.ConnectionEvent
- */
- public abstract void close(boolean expunge) throws MessagingException;
-
- /**
- * Close this Folder and expunge deleted messages.
- *
- * 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.
- *
- * This method supports the {@link java.lang.AutoCloseable AutoCloseable}
- * interface.
- *
- * This implementation calls close(true).
- *
- * @exception IllegalStateException if this folder is not opened
- * @exception MessagingException for other failures
- * @see javax.mail.event.ConnectionEvent
- * @since JavaMail 1.6
- */
- @Override
- public void close() throws MessagingException {
- close(true);
- }
-
- /**
- * 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- * @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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- */
- 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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @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 for other failures
- * @see javax.mail.search.SearchTerm
- */
- public Message[] search(SearchTerm term, Message[] msgs)
- throws MessagingException {
- List matchedMsgs = new ArrayList<>();
-
- // Run thru the given messages
- for (Message msg : msgs) {
- try {
- if (msg.match(term)) // matched
- matchedMsgs.add(msg); // add it
- } catch(MessageRemovedException mrex) { }
- }
-
- return matchedMsgs.toArray(new Message[matchedMsgs.size()]);
- }
-
- /*
- * 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)
- q.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.
- *
- * @param msgs the messages that were added
- */
- 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.
- *
- * @param removed was the message removed by this client?
- * @param msgs the messages that were removed
- */
- 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