diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index 7d0a95ed1a..1eb85df612 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -279,6 +279,9 @@ public class EmailService implements AutoCloseable { properties.put("mail." + protocol + ".peek", "true"); properties.put("mail." + protocol + ".appendbuffersize", Integer.toString(APPEND_BUFFER_SIZE)); + if (!"gimaps".equals(protocol) && BuildConfig.DEBUG) + properties.put("mail." + protocol + ".folder.class", IMAPFolderEX.class.getName()); + } else if ("smtp".equals(protocol) || "smtps".equals(protocol)) { // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties properties.put("mail.smtps.starttls.enable", "false"); diff --git a/app/src/main/java/eu/faircode/email/IMAPFolderEX.java b/app/src/main/java/eu/faircode/email/IMAPFolderEX.java new file mode 100644 index 0000000000..0af6503af7 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/IMAPFolderEX.java @@ -0,0 +1,167 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2022 by Marcel Bokhorst (M66B) +*/ + +import com.sun.mail.iap.Argument; +import com.sun.mail.iap.CommandFailedException; +import com.sun.mail.iap.ConnectionException; +import com.sun.mail.iap.ProtocolException; +import com.sun.mail.iap.Response; +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPMessage; +import com.sun.mail.imap.IMAPStore; +import com.sun.mail.imap.Utility; +import com.sun.mail.imap.protocol.IMAPProtocol; +import com.sun.mail.imap.protocol.ListInfo; +import com.sun.mail.imap.protocol.MessageSet; +import com.sun.mail.imap.protocol.UIDSet; + +import javax.mail.FetchProfile; +import javax.mail.Flags; +import javax.mail.Folder; +import javax.mail.FolderClosedException; +import javax.mail.FolderNotFoundException; +import javax.mail.Message; +import javax.mail.MessageRemovedException; +import javax.mail.MessagingException; +import javax.mail.UIDFolder; + +public class IMAPFolderEX extends IMAPFolder { + public IMAPFolderEX(String fullName, char separator, IMAPStore store, Boolean isNamespace) { + super(fullName, separator, store, isNamespace); + } + + public IMAPFolderEX(ListInfo li, IMAPStore store) { + super(li, store); + } + + @Override + public synchronized void setFlags(Message[] msgs, Flags flag, boolean value) throws MessagingException { + checkOpened(); + // checkFlags(flag); + + if (msgs.length == 0) + return; + + synchronized (messageCacheLock) { + try { + IMAPProtocol p = getProtocol(); + if (p.hasCapability("X-UIDONLY") || + (p.hasCapability("UIDPLUS") && + Boolean.parseBoolean(System.getProperty("fairemail.uid_command")))) { + // Verizon + FetchProfile fp = new FetchProfile(); + fp.add(UIDFolder.FetchProfileItem.UID); + fetch(msgs, fp); + + UIDSet[] uids = Utility.toUIDSet(msgs); + if (uids == null) + return; + + Response[] r = p.command("UID STORE " + UIDSet.toString(uids) + + " " + (value ? '+' : '-') + "FLAGS " + p.createFlagList(new Flags(flag)), null); + p.notifyResponseHandlers(r); + p.handleResult(r[r.length - 1]); + return; + } + 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); + } + } + } + + @Override + public synchronized void copyMessages(Message[] msgs, Folder folder) throws MessagingException { + copyMessages(msgs, folder, false); + } + + public synchronized void moveMessages(Message[] msgs, Folder folder) throws MessagingException { + copyMessages(msgs, folder, true); + } + + private synchronized void copyMessages(Message[] msgs, Folder folder, boolean move) throws MessagingException { + checkOpened(); + + if (msgs.length == 0) + return; + + if (folder.getStore() == store) { + synchronized (messageCacheLock) { + try { + IMAPProtocol p = getProtocol(); + if (p.hasCapability("X-UIDONLY") || + (p.hasCapability("UIDPLUS") && + Boolean.parseBoolean(System.getProperty("fairemail.uid_command")))) { + // Verizon + FetchProfile fp = new FetchProfile(); + fp.add(UIDFolder.FetchProfileItem.UID); + fetch(msgs, fp); + + UIDSet[] uids = Utility.toUIDSet(msgs); + if (uids == null) + return; + + Argument args = new Argument(); + args.writeAtom(UIDSet.toString(uids)); + p.writeMailboxName(args, folder.getFullName()); + Response[] r = p.command(move ? "UID MOVE" : "UID COPY", args); + p.notifyResponseHandlers(r); + p.handleResult(r[r.length - 1]); + return; + } + 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() != null && + cfx.getMessage().contains("TRYCREATE")) + 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 { + if (move) + throw new MessagingException("Move between stores not supported"); + else + super.copyMessages(msgs, folder); + } + } + + @Override + public IMAPMessage newIMAPMessage(int msgnum) { + return new IMAPMessageEx(this, msgnum); + } +} diff --git a/app/src/main/java/eu/faircode/email/IMAPMessageEx.java b/app/src/main/java/eu/faircode/email/IMAPMessageEx.java new file mode 100644 index 0000000000..e02359fa95 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/IMAPMessageEx.java @@ -0,0 +1,76 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2022 by Marcel Bokhorst (M66B) +*/ + +import com.sun.mail.iap.ConnectionException; +import com.sun.mail.iap.ProtocolException; +import com.sun.mail.iap.Response; +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPMessage; +import com.sun.mail.imap.protocol.IMAPProtocol; +import com.sun.mail.imap.protocol.UID; + +import javax.mail.Flags; +import javax.mail.FolderClosedException; +import javax.mail.MessagingException; +import javax.mail.Session; + +public class IMAPMessageEx extends IMAPMessage { + protected IMAPMessageEx(IMAPFolder folder, int msgnum) { + super(folder, msgnum); + } + + protected IMAPMessageEx(Session session) { + super(session); + } + + @Override + public synchronized void setFlags(Flags flag, boolean set) throws MessagingException { + synchronized (getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + checkExpunged(); // Insure that this message is not expunged + if (p.hasCapability("X-UIDONLY") || + "imap.mail.yahoo.co.jp".equals(p.getInetAddress().getHostName()) || + (p.hasCapability("UIDPLUS") && + Boolean.parseBoolean(System.getProperty("fairemail.uid_command")))) { + // Verizon + // Yahoo: NO [CANNOT] STORE It's not possible to perform specified operation + long uid = getUID(); + if (uid < 0) { + UID u = p.fetchUID(getSequenceNumber()); + if (u != null) + uid = u.uid; + } + Response[] r = p.command("UID STORE " + uid + + " " + (set ? '+' : '-') + "FLAGS " + p.createFlagList(new Flags(flag)), null); + p.notifyResponseHandlers(r); + p.handleResult(r[r.length - 1]); + return; + } + p.storeFlags(getSequenceNumber(), flag, set); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } +}