diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 31b07e02fa..5066165e62 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -481,7 +481,10 @@ public class AdapterMessage extends RecyclerView.Adapter :folder AND folder.type = '" + EntityFolder.DRAFTS + "') THEN 0 ELSE 1 END)"; @Query("SELECT message.*" + - ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.notify AS accountNotify" + + ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.pop as accountPop, account.notify AS accountNotify" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", identity.name AS identityName, identity.email AS identityEmail, identity.synchronize AS identitySynchronize" + ", COUNT(message.id) AS count" + @@ -119,7 +119,7 @@ public interface DaoMessage { DataSource.Factory pagedFolder(long folder, boolean threading, String sort, boolean snoozed, boolean found, boolean debug); @Query("SELECT message.*" + - ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.notify AS accountNotify" + + ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.pop as accountPop, account.notify AS accountNotify" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", identity.name AS identityName, identity.email AS identityEmail, identity.synchronize AS identitySynchronize" + ", 1 AS count" + @@ -191,7 +191,7 @@ public interface DaoMessage { int countMessageByMsgId(long folder, String msgid); @Query("SELECT message.*" + - ", account.name AS accountName, identity.color AS accountColor, account.notify AS accountNotify" + + ", account.name AS accountName, identity.color AS accountColor, account.pop as accountPop, account.notify AS accountNotify" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", identity.name AS identityName, identity.email AS identityEmail, identity.synchronize AS identitySynchronize" + ", 1 AS count" + @@ -208,7 +208,7 @@ public interface DaoMessage { LiveData liveMessage(long id); @Query("SELECT message.*" + - ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.notify AS accountNotify" + + ", account.name AS accountName, IFNULL(identity.color, account.color) AS accountColor, account.pop as accountPop, account.notify AS accountNotify" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", identity.name AS identityName, identity.email AS identityEmail, identity.synchronize AS identitySynchronize" + ", 1 AS count" + diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index 3283bac662..d86806f1ec 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -635,11 +635,7 @@ public class FragmentAccount extends FragmentBase { for (Folder ifolder : istore.getDefaultFolder().list("*")) { // Check folder attributes String fullName = ifolder.getFullName(); - String[] attrs; - if (ifolder instanceof IMAPFolder) - attrs = ((IMAPFolder) ifolder).getAttributes(); - else - attrs = new String[0]; + String[] attrs = ((IMAPFolder) ifolder).getAttributes(); Log.i(fullName + " attrs=" + TextUtils.join(" ", attrs)); String type = EntityFolder.getType(attrs, fullName); @@ -862,6 +858,15 @@ public class FragmentAccount extends FragmentBase { EntityFolder left = (EntityFolder) args.getSerializable("left"); EntityFolder right = (EntityFolder) args.getSerializable("right"); + if (pop) { + drafts = new EntityFolder(); + drafts.name = "Drafts"; + drafts.synchronize = false; + drafts.initialize = false; + drafts.sync_days = 0; + drafts.keep_days = 0; + } + if (TextUtils.isEmpty(host)) throw new IllegalArgumentException(context.getString(R.string.title_no_host)); if (TextUtils.isEmpty(port)) @@ -947,8 +952,8 @@ public class FragmentAccount extends FragmentBase { inbox.unified = true; inbox.notify = true; inbox.initialize = !pop; - inbox.sync_days = EntityFolder.DEFAULT_SYNC; - inbox.keep_days = EntityFolder.DEFAULT_KEEP; + inbox.sync_days = (pop ? 0 : EntityFolder.DEFAULT_SYNC); + inbox.keep_days = (pop ? 0 : EntityFolder.DEFAULT_KEEP); } } diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index f165dffc90..f106ac946e 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -2171,7 +2171,11 @@ public class FragmentMessages extends FragmentBase { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - if (message.uid != null) { + if (message == null) + return null; + EntityAccount account = db.account().getAccount(message.account); + + if (message.uid != null || account.pop) { if (!message.content) EntityOperation.queue(context, db, message, EntityOperation.BODY); if (!message.ui_seen) diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index e554512c19..6a3f2eec20 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -951,7 +951,8 @@ public class ServiceSynchronize extends LifecycleService { EntityLog.log(this, account.name + " connected"); // Update folder list - synchronizeFolders(account, istore, state); + if (istore instanceof IMAPStore) + synchronizeFolders(account, istore, state); // Open synchronizing folders final ExecutorService pollExecutor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); @@ -1473,63 +1474,77 @@ public class ServiceSynchronize extends LifecycleService { if (message != null) db.message().setMessageError(message.id, null); - if (message != null && message.uid == null && - !(EntityOperation.ADD.equals(op.name) || - EntityOperation.DELETE.equals(op.name) || - EntityOperation.SEND.equals(op.name) || - EntityOperation.SYNC.equals(op.name))) - throw new IllegalArgumentException(op.name + " without uid " + op.args); + if (account != null && account.pop) { + if (EntityOperation.SEEN.equals(op.name)) + doSeen(folder, (POP3Folder) ifolder, message, jargs, db); - // Operations should use database transaction when needed + else if (EntityOperation.ADD.equals(op.name)) + ; // Do nothing - if (EntityOperation.SEEN.equals(op.name)) - doSeen(folder, (IMAPFolder) ifolder, message, jargs, db); + else if (EntityOperation.DELETE.equals(op.name)) + doDelete(folder, (POP3Folder) ifolder, message, jargs, db); - else if (EntityOperation.FLAG.equals(op.name)) - doFlag(folder, (IMAPFolder) ifolder, message, jargs, db); + else if (EntityOperation.SYNC.equals(op.name)) + synchronizeMessages(account, folder, (POP3Folder) ifolder, jargs, state); - else if (EntityOperation.ANSWERED.equals(op.name)) - doAnswered(folder, (IMAPFolder) ifolder, message, jargs, db); + else + throw new MessagingException("Unknown operation name=" + op.name); - else if (EntityOperation.KEYWORD.equals(op.name)) - doKeyword(folder, (IMAPFolder) ifolder, message, jargs, db); + } else { + if (message != null && message.uid == null && + !(EntityOperation.ADD.equals(op.name) || + EntityOperation.DELETE.equals(op.name) || + EntityOperation.SEND.equals(op.name) || + EntityOperation.SYNC.equals(op.name))) + throw new IllegalArgumentException(op.name + " without uid " + op.args); - else if (EntityOperation.ADD.equals(op.name)) - doAdd(folder, isession, (IMAPStore) istore, (IMAPFolder) ifolder, message, jargs, db); + // Operations should use database transaction when needed - else if (EntityOperation.MOVE.equals(op.name)) - doMove(folder, isession, (IMAPStore) istore, (IMAPFolder) ifolder, message, jargs, db); + if (EntityOperation.SEEN.equals(op.name)) + doSeen(folder, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.DELETE.equals(op.name)) - doDelete(folder, (IMAPFolder) ifolder, message, jargs, db); + else if (EntityOperation.FLAG.equals(op.name)) + doFlag(folder, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.SEND.equals(op.name)) - doSend(message, db); + else if (EntityOperation.ANSWERED.equals(op.name)) + doAnswered(folder, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.HEADERS.equals(op.name)) - doHeaders(folder, (IMAPFolder) ifolder, message, db); + else if (EntityOperation.KEYWORD.equals(op.name)) + doKeyword(folder, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.RAW.equals(op.name)) - doRaw(folder, (IMAPFolder) ifolder, message, jargs, db); + else if (EntityOperation.ADD.equals(op.name)) + doAdd(folder, isession, (IMAPStore) istore, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.BODY.equals(op.name)) - doBody(folder, (IMAPFolder) ifolder, message, db); + else if (EntityOperation.MOVE.equals(op.name)) + doMove(folder, isession, (IMAPStore) istore, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.ATTACHMENT.equals(op.name)) - doAttachment(folder, op, (IMAPFolder) ifolder, message, jargs, db); + else if (EntityOperation.DELETE.equals(op.name)) + doDelete(folder, (IMAPFolder) ifolder, message, jargs, db); - else if (EntityOperation.SYNC.equals(op.name)) - if (EntityFolder.OUTBOX.equals(folder.type)) - db.folder().setFolderError(folder.id, null); - else { - if (ifolder instanceof IMAPFolder) + else if (EntityOperation.SEND.equals(op.name)) + doSend(message, db); + + else if (EntityOperation.HEADERS.equals(op.name)) + doHeaders(folder, (IMAPFolder) ifolder, message, db); + + else if (EntityOperation.RAW.equals(op.name)) + doRaw(folder, (IMAPFolder) ifolder, message, jargs, db); + + else if (EntityOperation.BODY.equals(op.name)) + doBody(folder, (IMAPFolder) ifolder, message, db); + + else if (EntityOperation.ATTACHMENT.equals(op.name)) + doAttachment(folder, op, (IMAPFolder) ifolder, message, jargs, db); + + else if (EntityOperation.SYNC.equals(op.name)) + if (EntityFolder.OUTBOX.equals(folder.type)) + db.folder().setFolderError(folder.id, null); + else synchronizeMessages(account, folder, (IMAPFolder) ifolder, jargs, state); - else if (ifolder instanceof POP3Folder) - synchronizeMessages(account, folder, (POP3Folder) ifolder, jargs, state); - } - else - throw new MessagingException("Unknown operation name=" + op.name); + else + throw new MessagingException("Unknown operation name=" + op.name); + } // Operation succeeded db.operation().deleteOperation(op.id); @@ -1598,6 +1613,15 @@ public class ServiceSynchronize extends LifecycleService { } } + private void doSeen(EntityFolder folder, POP3Folder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException { + boolean seen = jargs.getBoolean(0); + if (message.seen.equals(seen)) + return; + + Log.i("Setting POP message=" + message.id + " seen=" + seen); + db.message().setMessageSeen(message.id, seen); + } + private void doSeen(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException { // Mark message (un)seen if (!ifolder.getPermanentFlags().contains(Flags.Flag.SEEN)) { @@ -1880,6 +1904,24 @@ public class ServiceSynchronize extends LifecycleService { } } + private void doDelete(EntityFolder folder, POP3Folder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException { + Log.i("Deleting POP message=" + message.id + " msgid=" + message.msgid); + + // Delete message + if (TextUtils.isEmpty(message.msgid)) + throw new IllegalArgumentException("Message ID missing"); + + Message[] imessages = ifolder.search(new MessageIDTerm(message.msgid)); + for (Message imessage : imessages) { + Log.i(folder.name + " deleting uid=" + message.uid + " msgid=" + message.msgid); + imessage.setFlag(Flags.Flag.DELETED, true); + } + ifolder.close(); + ifolder.open(Folder.READ_WRITE); + + db.message().deleteMessage(message.id); + } + private void doDelete(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException { // Delete message if (TextUtils.isEmpty(message.msgid)) @@ -2234,11 +2276,7 @@ public class ServiceSynchronize extends LifecycleService { for (Folder ifolder : ifolders) { String fullName = ifolder.getFullName(); - String[] attrs; - if (ifolder instanceof IMAPFolder) - attrs = ((IMAPFolder) ifolder).getAttributes(); - else - attrs = new String[0]; + String[] attrs = ((IMAPFolder) ifolder).getAttributes(); String type = EntityFolder.getType(attrs, fullName); EntityLog.log(this, account.name + ":" + fullName + diff --git a/app/src/main/java/eu/faircode/email/TupleMessageEx.java b/app/src/main/java/eu/faircode/email/TupleMessageEx.java index 0cc81f5ebe..bf370a11b5 100644 --- a/app/src/main/java/eu/faircode/email/TupleMessageEx.java +++ b/app/src/main/java/eu/faircode/email/TupleMessageEx.java @@ -24,6 +24,7 @@ import androidx.room.Ignore; public class TupleMessageEx extends EntityMessage { public String accountName; public Integer accountColor; + public boolean accountPop; public boolean accountNotify; public String folderName; public String folderDisplay; @@ -47,6 +48,7 @@ public class TupleMessageEx extends EntityMessage { return (super.uiEquals(obj) && (this.accountName == null ? other.accountName == null : this.accountName.equals(other.accountName)) && (this.accountColor == null ? other.accountColor == null : this.accountColor.equals(other.accountColor)) && + this.accountPop == other.accountPop && //this.accountNotify == other.accountNotify && this.folderName.equals(other.folderName) && (this.folderDisplay == null ? other.folderDisplay == null : this.folderDisplay.equals(other.folderDisplay)) && @@ -72,6 +74,7 @@ public class TupleMessageEx extends EntityMessage { return (super.equals(obj) && (this.accountName == null ? other.accountName == null : this.accountName.equals(other.accountName)) && (this.accountColor == null ? other.accountColor == null : this.accountColor.equals(other.accountColor)) && + this.accountPop == other.accountPop && this.accountNotify == other.accountNotify && this.folderName.equals(other.folderName) && (this.folderDisplay == null ? other.folderDisplay == null : this.folderDisplay.equals(other.folderDisplay)) &&