Inline POP3 operations

pull/212/head
M66B 2 years ago
parent 6c9d09faac
commit 6b21447840

@ -411,10 +411,6 @@ class Core {
messages.addAll(similar.values());
switch (op.name) {
case EntityOperation.MOVE:
onMove(context, jargs, account, folder, messages, (POP3Folder) ifolder, (POP3Store) istore, state);
break;
case EntityOperation.DELETE:
onDelete(context, jargs, account, folder, messages, (POP3Folder) ifolder, (POP3Store) istore, state);
break;
@ -1676,49 +1672,6 @@ class Core {
}
}
private static void onMove(Context context, JSONArray jargs, EntityAccount account, EntityFolder folder, List<EntityMessage> messages, POP3Folder ifolder, POP3Store istore, State state) throws JSONException, FolderNotFoundException, IOException, MessagingException {
// Move message
DB db = DB.getInstance(context);
// Get arguments
long id = jargs.getLong(0);
boolean seen = jargs.optBoolean(1);
boolean unflag = jargs.optBoolean(3);
// Get target folder
EntityFolder target = db.folder().getFolder(id);
if (target == null)
throw new FolderNotFoundException();
if (folder.id.equals(target.id))
throw new IllegalArgumentException("self");
if (Boolean.TRUE.equals(account.leave_deleted) &&
EntityFolder.INBOX.equals(folder.type) &&
EntityFolder.TRASH.equals(target.type)) {
onDelete(context, jargs, account, folder, messages, ifolder, istore, state);
return;
}
// Move from drafts/sent/trash+leave_delete only
if (!EntityFolder.DRAFTS.equals(folder.type) &&
!EntityFolder.SENT.equals(folder.type) &&
!(EntityFolder.TRASH.equals(folder.type) && account.leave_deleted))
throw new IllegalArgumentException("Invalid POP3 folder" +
" source=" + folder.type + " target=" + target.type +
" leave deleted=" + account.leave_deleted);
for (EntityMessage message : messages) {
message.folder = target.id;
if (seen)
message.ui_seen = seen;
if (unflag)
message.ui_flagged = false;
message.ui_hide = false;
db.message().updateMessage(message);
}
}
private static void onFetch(Context context, JSONArray jargs, EntityFolder folder, IMAPStore istore, IMAPFolder ifolder, State state) throws JSONException, MessagingException, IOException {
long uid = jargs.getLong(0);
boolean invalidate = jargs.optBoolean(1);
@ -1942,85 +1895,28 @@ class Core {
}
private static void onDelete(Context context, JSONArray jargs, EntityAccount account, EntityFolder folder, List<EntityMessage> messages, POP3Folder ifolder, POP3Store istore, State state) throws MessagingException, IOException {
// Delete message
DB db = DB.getInstance(context);
// Delete from server
if (EntityFolder.INBOX.equals(folder.type) && !account.leave_deleted) {
Map<EntityMessage, Message> map = findMessages(context, folder, messages, istore, ifolder);
for (EntityMessage message : messages) {
Message imessage = map.get(message);
if (imessage != null) {
Log.i(folder.name + " POP delete=" + message.uidl + "/" + message.msgid);
imessage.setFlag(Flags.Flag.DELETED, true);
}
}
if (map.size() > 0)
try {
Log.i(folder.name + " POP expunge");
ifolder.close(true);
ifolder.open(Folder.READ_WRITE);
} catch (Throwable ex) {
Log.e(ex);
state.error(new FolderClosedException(ifolder, "POP", new Exception(ex)));
}
}
if (!EntityFolder.INBOX.equals(folder.type) || account.leave_deleted)
throw new IllegalArgumentException("POP3: invalid DELETE");
Map<EntityMessage, Message> map = findMessages(context, folder, messages, istore, ifolder);
for (EntityMessage message : messages) {
// Move to trash folder
if (!EntityFolder.DRAFTS.equals(folder.type) &&
!EntityFolder.TRASH.equals(folder.type)) {
EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
if (trash == null) {
trash = new EntityFolder();
trash.account = account.id;
trash.name = context.getString(R.string.title_folder_trash);
trash.type = EntityFolder.TRASH;
trash.synchronize = false;
trash.unified = false;
trash.notify = false;
trash.sync_days = Integer.MAX_VALUE;
trash.keep_days = Integer.MAX_VALUE;
trash.initialize = 0;
trash.id = db.folder().insertFolder(trash);
}
long id = message.id;
message.id = null;
message.folder = trash.id;
message.msgid = null; // virtual message
message.ui_hide = false;
message.ui_seen = true;
message.id = db.message().insertMessage(message);
try {
File source = EntityMessage.getFile(context, id);
File target = message.getFile(context);
Helper.copy(source, target);
} catch (IOException ex) {
Log.e(ex);
}
EntityAttachment.copy(context, id, message.id);
message.id = id;
Message imessage = map.get(message);
if (imessage != null) {
Log.i(folder.name + " POP delete=" + message.uidl + "/" + message.msgid);
imessage.setFlag(Flags.Flag.DELETED, true);
}
// Delete from device
if (EntityFolder.INBOX.equals(folder.type)) {
if (account.leave_deleted) {
// Remove message/attachments files on cleanup
db.message().resetMessageContent(message.id);
db.attachment().resetAvailable(message.id);
}
// Synchronize will delete messages when needed
db.message().setMessageUiHide(message.id, true);
} else
db.message().deleteMessage(message.id);
}
if (map.size() > 0)
try {
Log.i(folder.name + " POP expunge");
ifolder.close(true);
ifolder.open(Folder.READ_WRITE);
} catch (Throwable ex) {
Log.e(ex);
state.error(new FolderClosedException(ifolder, "POP", new Exception(ex)));
}
}
private static void onHeaders(Context context, JSONArray jargs, EntityFolder folder, EntityMessage message, IMAPFolder ifolder) throws MessagingException, IOException {

@ -543,33 +543,9 @@ public class EntityOperation {
}
}
if (account != null) {
EntityAccount a = db.account().getAccount(account);
if (a != null && a.protocol == EntityAccount.TYPE_POP) {
// TODO: special cases for MOVE, DELETE, PURGE
if (SEEN.equals(name) ||
FLAG.equals(name) ||
ANSWERED.equals(name) ||
KEYWORD.equals(name) ||
ADD.equals(name) ||
REPORT.equals(name)) {
Log.i("POP3: skipping op=" + name);
return;
}
if (DELETE.equals(name)) {
EntityFolder f = db.folder().getFolder(folder);
if (f != null &&
(EntityFolder.DRAFTS.equals(f.type) ||
EntityFolder.TRASH.equals(f.type))) {
Log.i("POP3: inline DELETE folder=" + f.name);
db.message().deleteMessage(message);
return;
}
}
}
}
// Check for offline POP3 operations
if (inlinePOP3(context, account, folder, message, name, jargs))
return;
EntityOperation op = new EntityOperation();
op.account = account;
@ -593,6 +569,149 @@ public class EntityOperation {
Log.breadcrumb("queued", crumb);
}
private static boolean inlinePOP3(Context context, Long account, long folder, Long message, String name, JSONArray jargs) {
if (account == null || message == null)
return false;
DB db = DB.getInstance(context);
EntityAccount a = db.account().getAccount(account);
if (a == null || a.protocol != EntityAccount.TYPE_POP)
return false;
// TODO: special case for PURGE
if (SEEN.equals(name) ||
FLAG.equals(name) ||
ANSWERED.equals(name) ||
KEYWORD.equals(name) ||
ADD.equals(name) ||
REPORT.equals(name)) {
Log.i("POP3: skipping op=" + name);
return true;
}
if (MOVE.equals(name)) {
try {
long target = jargs.getLong(0);
boolean seen = jargs.optBoolean(1);
boolean unflag = jargs.optBoolean(3);
EntityFolder f = db.folder().getFolder(folder);
EntityFolder t = db.folder().getFolder(target);
if (f == null || t == null || f.id.equals(t.id)) {
Log.e("POP3: invalid MOVE/folders");
return true;
}
if (a.leave_deleted &&
EntityFolder.INBOX.equals(f.type) &&
EntityFolder.TRASH.equals(t.type)) {
Log.i("POP3 convert MOVE into DELETE");
name = DELETE;
} else {
EntityMessage m = db.message().getMessage(message);
if (m == null) {
Log.e("POP3: invalid MOVE/message");
return true;
}
Log.i("POP3: local MOVE " + f.type + " > " + t.type);
m.folder = t.id;
if (seen)
m.ui_seen = seen;
if (unflag)
m.ui_flagged = false;
m.ui_hide = false;
db.message().updateMessage(m);
return true;
}
} catch (JSONException ex) {
Log.e(ex);
return true;
}
}
if (DELETE.equals(name)) {
EntityFolder f = db.folder().getFolder(folder);
EntityMessage m = db.message().getMessage(message);
if (f == null || m == null) {
Log.e("POP3: invalid DELETE");
return true;
}
if (!EntityFolder.DRAFTS.equals(f.type) &&
!EntityFolder.TRASH.equals(f.type)) {
Log.i("POP3: local TRASH " + f.type);
EntityFolder trash = db.folder().getFolderByType(m.account, EntityFolder.TRASH);
if (trash == null) {
trash = new EntityFolder();
trash.account = m.id;
trash.name = context.getString(R.string.title_folder_trash);
trash.type = EntityFolder.TRASH;
trash.synchronize = false;
trash.unified = false;
trash.notify = false;
trash.sync_days = Integer.MAX_VALUE;
trash.keep_days = Integer.MAX_VALUE;
trash.initialize = 0;
trash.id = db.folder().insertFolder(trash);
}
long id = m.id;
m.id = null;
m.folder = trash.id;
m.msgid = null; // virtual message
m.ui_hide = false;
m.ui_seen = true;
m.id = db.message().insertMessage(m);
try {
File source = EntityMessage.getFile(context, id);
File target = m.getFile(context);
Helper.copy(source, target);
} catch (IOException ex) {
Log.e(ex);
}
EntityAttachment.copy(context, id, m.id);
m.id = id;
}
// Delete from device
if (EntityFolder.INBOX.equals(f.type)) {
if (a.leave_deleted) {
// Remove message/attachments files on cleanup
Log.i("POP3: DELETE reset content");
db.message().resetMessageContent(m.id);
db.attachment().resetAvailable(m.id);
}
// Synchronize will delete messages when needed
Log.i("POP3: DELETE hide " + f.type);
db.message().setMessageUiHide(m.id, true);
} else {
Log.i("POP3: local DELETE " + f.type);
db.message().deleteMessage(m.id);
}
if (EntityFolder.INBOX.equals(f.type) && !a.leave_deleted) {
Log.i("POP3: DELETE remote " + f.type);
return false;
} else {
Log.i("POP3: local only " + f.type);
return true;
}
}
return false;
}
static void poll(Context context, long fid) throws JSONException {
DB db = DB.getInstance(context);

Loading…
Cancel
Save