diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/handlers/handler_base.java ./app/src/main/java/com/sun/mail/handlers/handler_base.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/handlers/handler_base.java 2020-08-14 11:44:05.401359065 +0200 +++ ./app/src/main/java/com/sun/mail/handlers/handler_base.java 2020-08-26 15:38:37.903384284 +0200 @@ -17,7 +17,6 @@ package com.sun.mail.handlers; import java.io.IOException; -import java.awt.datatransfer.DataFlavor; import javax.activation.*; /** @@ -52,14 +51,8 @@ public abstract class handler_base imple * * @return The DataFlavors */ - @Override - public DataFlavor[] getTransferDataFlavors() { - ActivationDataFlavor[] adf = getDataFlavors(); - if (adf.length == 1) // the common case - return new DataFlavor[] { adf[0] }; - DataFlavor[] df = new DataFlavor[adf.length]; - System.arraycopy(adf, 0, df, 0, adf.length); - return df; + public ActivationDataFlavor[] getTransferDataFlavors() { + return getDataFlavors().clone(); } /** @@ -70,8 +63,7 @@ public abstract class handler_base imple * @return the object * @exception IOException for errors reading the data */ - @Override - public Object getTransferData(DataFlavor df, DataSource ds) + public Object getTransferData(ActivationDataFlavor df, DataSource ds) throws IOException { ActivationDataFlavor[] adf = getDataFlavors(); for (int i = 0; i < adf.length; i++) { diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/iap/Protocol.java ./app/src/main/java/com/sun/mail/iap/Protocol.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/iap/Protocol.java 2020-04-03 09:00:49.201313282 +0200 +++ ./app/src/main/java/com/sun/mail/iap/Protocol.java 2020-08-28 08:09:12.005962733 +0200 @@ -347,6 +347,15 @@ public class Protocol { * @return array of Response objects returned by the server */ public synchronized Response[] command(String command, Argument args) { + if (socket == null) + return new Response[]{Response.byeResponse(new SocketException("disconnected"))}; + if ("LOGOUT".equals(command)) + try { + socket.setSoTimeout(10 * 1000); + } catch (SocketException ex) { + eu.faircode.email.Log.e(ex); + } + commandStart(command); List v = new ArrayList<>(); boolean done = false; diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java ./app/src/main/java/com/sun/mail/imap/IMAPFolder.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java 2020-08-14 11:44:05.403359065 +0200 +++ ./app/src/main/java/com/sun/mail/imap/IMAPFolder.java 2020-08-27 09:24:28.051446453 +0200 @@ -1659,6 +1659,28 @@ public class IMAPFolder extends Folder i } } + public synchronized int getCachedCount() { + synchronized (messageCacheLock) { + if (messageCache == null) + return -1; + + try { + int count = 0; + int size = messageCache.size(); + for (int i = 1; i <= size; i++) { + Message message = messageCache.getMessage(i); + if (message != null && !message.isExpunged()) + count++; + } + + return count; + } catch (Throwable ex) { + eu.faircode.email.Log.e(ex); + return -1; + } + } + } + /** * Get the new message count. */ @@ -2608,6 +2630,20 @@ public class IMAPFolder extends Folder i throw new FolderClosedException(this, cex.getMessage()); } catch (ProtocolException pex) { throw new MessagingException(pex.getMessage(), pex); + } catch (ArrayIndexOutOfBoundsException ex) { + eu.faircode.email.Log.w(ex); + /* + java.lang.ArrayIndexOutOfBoundsException: message number (0) out of bounds (110) + at com.sun.mail.imap.MessageCache.getMessage(SourceFile:116) + at com.sun.mail.imap.MessageCache.getMessageBySeqnum(SourceFile:148) + at com.sun.mail.imap.IMAPFolder.getMessageBySeqNumber(SourceFile:3999) + at com.sun.mail.imap.IMAPFolder.processFetchResponse(SourceFile:3604) + at com.sun.mail.imap.IMAPFolder.handleResponse(SourceFile:3586) + at com.sun.mail.iap.Protocol.notifyResponseHandlers(SourceFile:245) + at com.sun.mail.imap.protocol.IMAPProtocol.fetchSequenceNumber(SourceFile:2057) + at com.sun.mail.imap.IMAPFolder.getMessageByUID(SourceFile:2598) + */ + return null; } return m; @@ -3963,7 +3999,8 @@ public class IMAPFolder extends Folder i protocol.noop(); } - if (keepStoreAlive && ((IMAPStore)store).hasSeparateStoreConnection()) { + if (keepStoreAlive && ((IMAPStore)store).hasSeparateStoreConnection() && + !((IMAPStore)store).isStoreConnectionInUse()) { IMAPProtocol p = null; try { p = ((IMAPStore)store).getFolderStoreProtocol(); diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPMessage.java ./app/src/main/java/com/sun/mail/imap/IMAPMessage.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPMessage.java 2020-08-14 11:44:05.403359065 +0200 +++ ./app/src/main/java/com/sun/mail/imap/IMAPMessage.java 2020-08-26 15:38:37.906384284 +0200 @@ -1697,4 +1697,28 @@ public class IMAPMessage extends MimeMes Session _getSession() { return session; } + + @Override + public boolean isExpunged() { + if (super.isExpunged()) + return true; + + // Workaround expunged messages without deleted flag + if (envelope != null && + envelope.date == null && + envelope.subject == null && + envelope.from == null && + envelope.sender == null && + envelope.replyTo == null && + envelope.to == null && + envelope.cc == null && + envelope.inReplyTo == null && + envelope.messageId == null && + headersLoaded && loadedHeaders.size() == 0) { + eu.faircode.email.Log.w("Expunged workaround host=" + ((IMAPStore) folder.getStore()).host); + return true; + } + + return false; + } } diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPStore.java ./app/src/main/java/com/sun/mail/imap/IMAPStore.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/IMAPStore.java 2020-08-14 11:44:05.404359065 +0200 +++ ./app/src/main/java/com/sun/mail/imap/IMAPStore.java 2020-08-26 15:38:37.907384284 +0200 @@ -887,18 +887,26 @@ public class IMAPStore extends Store 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; - } + try { + 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; + } + } catch (ProtocolException ex) { + if (m.equals("PLAIN") || m.equals("LOGIN")) { + eu.faircode.email.Log.i("Falling back to classic LOGIN"); + p.authclassic(user, password); + } else + throw ex; + } return; } @@ -1014,6 +1022,8 @@ public class IMAPStore extends Store p.disconnect(); } catch (Exception ex2) { } p = null; + eu.faircode.email.Log.e(new MessagingException("IMAP connection failure", ex1)); + throw new MessagingException("connection failure", ex1); } if (p == null) @@ -1175,7 +1185,11 @@ public class IMAPStore extends Store // someone else is using the connection, give up // and wait until they're done p = null; - pool.wait(); + pool.wait(pool.clientTimeoutInterval); + if (pool.storeConnectionInUse) { + eu.faircode.email.Log.e("getStoreProtocol timeout"); + throw new InterruptedException("getStoreProtocol timeout"); + } } catch (InterruptedException ex) { // restore the interrupted state, which callers might // depend on @@ -1250,7 +1264,11 @@ public class IMAPStore extends Store return pool.separateStoreConnection; } - /** + boolean isStoreConnectionInUse() { + return pool.storeConnectionInUse; + } + + /** * Return the connection pool logger. */ MailLogger getConnectionPoolLogger() { @@ -1529,6 +1547,30 @@ public class IMAPStore extends Store } } + public synchronized String getCapability(String capability) + throws MessagingException { + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + Map caps = p.getCapabilities(); + if (caps != null) + for (String cap : caps.values()) { + int eq = (cap == null ? -1 : cap.indexOf('=')); + if (eq > 0) { + String key = cap.substring(0, eq); + String value = cap.substring(eq + 1); + if (capability.equals(key)) + return value; + } + } + return null; + } 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 @@ -2035,7 +2077,11 @@ public class IMAPStore extends Store // without aborting it ourselves try { // give up lock and wait to be not idle - pool.wait(); + pool.wait(pool.clientTimeoutInterval); + if (pool.idleState != ConnectionPool.RUNNING) { + eu.faircode.email.Log.e("idle timeout"); + throw new InterruptedException("idle timeout"); + } } catch (InterruptedException ex) { // restore the interrupted state, which callers might // depend on @@ -2128,7 +2174,11 @@ public class IMAPStore extends Store } try { // give up lock and wait to be not idle - pool.wait(); + pool.wait(pool.clientTimeoutInterval); + if (pool.idleState != ConnectionPool.RUNNING) { + eu.faircode.email.Log.e("waitIfIdle timeout"); + throw new InterruptedException("waitIfIdle timeout"); + } } 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 diff -rupN /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java ./app/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java --- /home/marcel/JavaMail/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java 2020-08-14 11:44:05.406359065 +0200 +++ ./app/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java 2020-08-28 17:41:28.427621762 +0200 @@ -462,6 +462,9 @@ public class IMAPProtocol extends Protoc */ public void logout() throws ProtocolException { try { + if (!authenticated) + return; + Response[] r = command("LOGOUT", null); authenticated = false; @@ -627,6 +630,59 @@ public class IMAPProtocol extends Protoc authenticated = true; } + public synchronized void authclassic(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("LOGIN command trace suppressed"); + suspendTracing(); + } + + try { + Argument arg = new Argument(); + arg.writeNString(u); + arg.writeNString(p); + tag = writeCommand("LOGIN", arg); + } catch (Exception ex) { + r = Response.byeResponse(ex); + done = true; + } + + while (!done) { + try { + r = readResponse(); + if (r.isTagged() && r.getTag().equals(tag)) + done = true; + else if (r.isBYE()) // outta here + done = true; + } catch (Exception ioex) { + r = Response.byeResponse(ioex); + done = true; + } + v.add(r); + } + + } finally { + resumeTracing(); + } + + Response[] responses = v.toArray(new Response[v.size()]); + + handleCapabilityResponse(responses); + notifyResponseHandlers(responses); + + if (noauthdebug && isTracing()) + logger.fine("LOGIN command result: " + r); + handleLoginResult(r); + setCapabilities(r); + authenticated = true; + } /** * The AUTHENTICATE command with AUTH=PLAIN authentication scheme. --- /home/marcel/JavaMail/mail/src/main/java/javax/mail/EventQueue.java 2020-08-14 11:44:05.413359065 +0200 +++ app/src/main/java/javax/mail/EventQueue.java 2020-09-21 11:04:19.648547947 +0200 @@ -154,6 +154,19 @@ class EventQueue implements Runnable { } } catch (InterruptedException e) { // just die + } catch (Error ex) { + /* + java.lang.Error: java.lang.InterruptedException + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1173) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) + at java.lang.Thread.run(Thread.java:919) + Caused by: java.lang.InterruptedException + at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1248) + at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:344) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:439) + at javax.mail.EventQueue.run(SourceFile:140) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) + */ } } } diff --git a/app/src/main/java/com/sun/mail/imap/IMAPStore.java b/app/src/main/java/com/sun/mail/imap/IMAPStore.java index 087e5a6f5..5fa720f3d 100644 --- a/app/src/main/java/com/sun/mail/imap/IMAPStore.java +++ b/app/src/main/java/com/sun/mail/imap/IMAPStore.java @@ -1571,6 +1571,19 @@ public class IMAPStore extends Store } } + public synchronized Map getCapabilities() + throws MessagingException { + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + return p.getCapabilities(); + } 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