mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-11 12:05:06 -05:00
Merge branch 'uidplus'
This commit is contained in:
commit
5591865f17
@ -117,6 +117,7 @@ public class MessagingController implements Runnable {
|
|||||||
|
|
||||||
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
||||||
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew";
|
||||||
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
||||||
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
||||||
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
||||||
@ -1900,6 +1901,8 @@ public class MessagingController implements Runnable {
|
|||||||
} else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) {
|
} else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) {
|
||||||
processPendingMarkAllAsRead(command, account);
|
processPendingMarkAllAsRead(command, account);
|
||||||
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK.equals(command.command)) {
|
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK.equals(command.command)) {
|
||||||
|
processPendingMoveOrCopyOld2(command, account);
|
||||||
|
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW.equals(command.command)) {
|
||||||
processPendingMoveOrCopy(command, account);
|
processPendingMoveOrCopy(command, account);
|
||||||
} else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) {
|
} else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) {
|
||||||
processPendingMoveOrCopyOld(command, account);
|
processPendingMoveOrCopyOld(command, account);
|
||||||
@ -2079,16 +2082,72 @@ public class MessagingController implements Runnable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = new PendingCommand();
|
||||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK;
|
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
|
||||||
int length = 3 + uids.length;
|
int length = 3 + uids.length;
|
||||||
command.arguments = new String[length];
|
command.arguments = new String[length];
|
||||||
command.arguments[0] = srcFolder;
|
command.arguments[0] = srcFolder;
|
||||||
command.arguments[1] = destFolder;
|
command.arguments[1] = destFolder;
|
||||||
command.arguments[2] = Boolean.toString(isCopy);
|
command.arguments[2] = Boolean.toString(isCopy);
|
||||||
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
|
command.arguments[3] = Boolean.toString(false);
|
||||||
|
System.arraycopy(uids, 0, command.arguments, 4, uids.length);
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, String uids[], Map<String, String> uidMap) {
|
||||||
|
if (uidMap == null || uidMap.isEmpty()) {
|
||||||
|
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, uids);
|
||||||
|
} else {
|
||||||
|
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PendingCommand command = new PendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
|
||||||
|
int length = 4 + uidMap.keySet().size() + uidMap.values().size();
|
||||||
|
command.arguments = new String[length];
|
||||||
|
command.arguments[0] = srcFolder;
|
||||||
|
command.arguments[1] = destFolder;
|
||||||
|
command.arguments[2] = Boolean.toString(isCopy);
|
||||||
|
command.arguments[3] = Boolean.toString(true);
|
||||||
|
System.arraycopy(uidMap.keySet().toArray(), 0, command.arguments, 4, uidMap.keySet().size());
|
||||||
|
System.arraycopy(uidMap.values().toArray(), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size());
|
||||||
|
queuePendingCommand(account, command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert pending command to new format and call
|
||||||
|
* {@link #processPendingMoveOrCopy(PendingCommand, Account)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* TODO: This method is obsolete and is only for transition from K-9 4.0 to K-9 4.2
|
||||||
|
* Eventually, it should be removed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param command
|
||||||
|
* Pending move/copy command in old format.
|
||||||
|
* @param account
|
||||||
|
* The account the pending command belongs to.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
private void processPendingMoveOrCopyOld2(PendingCommand command, Account account)
|
||||||
|
throws MessagingException {
|
||||||
|
PendingCommand newCommand = new PendingCommand();
|
||||||
|
int len = command.arguments.length;
|
||||||
|
newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
newCommand.arguments = new String[len + 1];
|
||||||
|
newCommand.arguments[0] = command.arguments[0];
|
||||||
|
newCommand.arguments[1] = command.arguments[1];
|
||||||
|
newCommand.arguments[2] = command.arguments[2];
|
||||||
|
newCommand.arguments[3] = Boolean.toString(false);
|
||||||
|
System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3);
|
||||||
|
|
||||||
|
processPendingMoveOrCopy(newCommand, account);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a pending trash message command.
|
* Process a pending trash message command.
|
||||||
*
|
*
|
||||||
@ -2100,6 +2159,7 @@ public class MessagingController implements Runnable {
|
|||||||
throws MessagingException {
|
throws MessagingException {
|
||||||
Folder remoteSrcFolder = null;
|
Folder remoteSrcFolder = null;
|
||||||
Folder remoteDestFolder = null;
|
Folder remoteDestFolder = null;
|
||||||
|
LocalFolder localDestFolder = null;
|
||||||
try {
|
try {
|
||||||
String srcFolder = command.arguments[0];
|
String srcFolder = command.arguments[0];
|
||||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||||
@ -2107,17 +2167,45 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
String destFolder = command.arguments[1];
|
String destFolder = command.arguments[1];
|
||||||
String isCopyS = command.arguments[2];
|
String isCopyS = command.arguments[2];
|
||||||
|
String hasNewUidsS = command.arguments[3];
|
||||||
|
|
||||||
|
boolean hasNewUids = false;
|
||||||
|
if (hasNewUidsS != null) {
|
||||||
|
hasNewUids = Boolean.parseBoolean(hasNewUidsS);
|
||||||
|
}
|
||||||
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
Store remoteStore = account.getRemoteStore();
|
||||||
remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
||||||
|
|
||||||
|
Store localStore = account.getLocalStore();
|
||||||
|
localDestFolder = (LocalFolder) localStore.getFolder(destFolder);
|
||||||
List<Message> messages = new ArrayList<Message>();
|
List<Message> messages = new ArrayList<Message>();
|
||||||
for (int i = 3; i < command.arguments.length; i++) {
|
|
||||||
|
/*
|
||||||
|
* We split up the localUidMap into two parts while sending the command, here we assemble it back.
|
||||||
|
*/
|
||||||
|
Map<String, String> localUidMap = new HashMap<String, String>();
|
||||||
|
if (hasNewUids) {
|
||||||
|
int offset = (command.arguments.length - 4) / 2;
|
||||||
|
|
||||||
|
for (int i = 4; i < 4 + offset; i++) {
|
||||||
|
localUidMap.put(command.arguments[i], command.arguments[i + offset]);
|
||||||
|
|
||||||
String uid = command.arguments[i];
|
String uid = command.arguments[i];
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||||
messages.add(remoteSrcFolder.getMessage(uid));
|
messages.add(remoteSrcFolder.getMessage(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int i = 4; i < command.arguments.length; i++) {
|
||||||
|
String uid = command.arguments[i];
|
||||||
|
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||||
|
messages.add(remoteSrcFolder.getMessage(uid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean isCopy = false;
|
boolean isCopy = false;
|
||||||
if (isCopyS != null) {
|
if (isCopyS != null) {
|
||||||
isCopy = Boolean.parseBoolean(isCopyS);
|
isCopy = Boolean.parseBoolean(isCopyS);
|
||||||
@ -2135,6 +2223,8 @@ public class MessagingController implements Runnable {
|
|||||||
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy: source folder = " + srcFolder
|
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy: source folder = " + srcFolder
|
||||||
+ ", " + messages.size() + " messages, destination folder = " + destFolder + ", isCopy = " + isCopy);
|
+ ", " + messages.size() + " messages, destination folder = " + destFolder + ", isCopy = " + isCopy);
|
||||||
|
|
||||||
|
Map <String, String> remoteUidMap = null;
|
||||||
|
|
||||||
if (!isCopy && destFolder.equals(account.getTrashFolderName())) {
|
if (!isCopy && destFolder.equals(account.getTrashFolderName())) {
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy doing special case for deleting message");
|
Log.d(K9.LOG_TAG, "processingPendingMoveOrCopy doing special case for deleting message");
|
||||||
@ -2148,9 +2238,9 @@ public class MessagingController implements Runnable {
|
|||||||
remoteDestFolder = remoteStore.getFolder(destFolder);
|
remoteDestFolder = remoteStore.getFolder(destFolder);
|
||||||
|
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
remoteSrcFolder.copyMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
remoteUidMap = remoteSrcFolder.copyMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||||
} else {
|
} else {
|
||||||
remoteSrcFolder.moveMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
remoteUidMap = remoteSrcFolder.moveMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isCopy && Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
|
if (!isCopy && Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
|
||||||
@ -2159,12 +2249,32 @@ public class MessagingController implements Runnable {
|
|||||||
|
|
||||||
remoteSrcFolder.expunge();
|
remoteSrcFolder.expunge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This next part is used to bring the local UIDs of the local destination folder
|
||||||
|
* upto speed with the remote UIDs of remote destionation folder.
|
||||||
|
*/
|
||||||
|
if (!localUidMap.isEmpty() && remoteUidMap != null && !remoteUidMap.isEmpty()) {
|
||||||
|
Set<String> remoteSrcUids = remoteUidMap.keySet();
|
||||||
|
Iterator<String> remoteSrcUidsIterator = remoteSrcUids.iterator();
|
||||||
|
|
||||||
|
while (remoteSrcUidsIterator.hasNext()) {
|
||||||
|
String remoteSrcUid = remoteSrcUidsIterator.next();
|
||||||
|
String localDestUid = localUidMap.get(remoteSrcUid);
|
||||||
|
String newUid = remoteUidMap.get(remoteSrcUid);
|
||||||
|
|
||||||
|
Message localDestMessage = localDestFolder.getMessage(localDestUid);
|
||||||
|
localDestMessage.setUid(newUid);
|
||||||
|
localDestFolder.changeUid((LocalMessage)localDestMessage);
|
||||||
|
for (MessagingListener l : getListeners()) {
|
||||||
|
l.messageUidChanged(account, destFolder, localDestUid, newUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
closeFolder(remoteSrcFolder);
|
closeFolder(remoteSrcFolder);
|
||||||
closeFolder(remoteDestFolder);
|
closeFolder(remoteDestFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueSetFlag(final Account account, final String folderName, final String newState, final String flag, final String[] uids) {
|
private void queueSetFlag(final Account account, final String folderName, final String newState, final String flag, final String[] uids) {
|
||||||
@ -3281,6 +3391,7 @@ public class MessagingController implements Runnable {
|
|||||||
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, final Message[] inMessages,
|
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, final Message[] inMessages,
|
||||||
final String destFolder, final boolean isCopy, MessagingListener listener) {
|
final String destFolder, final boolean isCopy, MessagingListener listener) {
|
||||||
try {
|
try {
|
||||||
|
Map<String, String> uidMap = new HashMap<String, String>();
|
||||||
Store localStore = account.getLocalStore();
|
Store localStore = account.getLocalStore();
|
||||||
Store remoteStore = account.getRemoteStore();
|
Store remoteStore = account.getRemoteStore();
|
||||||
if (!isCopy && (!remoteStore.isMoveCapable() || !localStore.isMoveCapable())) {
|
if (!isCopy && (!remoteStore.isMoveCapable() || !localStore.isMoveCapable())) {
|
||||||
@ -3323,7 +3434,7 @@ public class MessagingController implements Runnable {
|
|||||||
fp.add(FetchProfile.Item.ENVELOPE);
|
fp.add(FetchProfile.Item.ENVELOPE);
|
||||||
fp.add(FetchProfile.Item.BODY);
|
fp.add(FetchProfile.Item.BODY);
|
||||||
localSrcFolder.fetch(messages, fp, null);
|
localSrcFolder.fetch(messages, fp, null);
|
||||||
localSrcFolder.copyMessages(messages, localDestFolder);
|
uidMap = localSrcFolder.copyMessages(messages, localDestFolder);
|
||||||
|
|
||||||
if (unreadCountAffected) {
|
if (unreadCountAffected) {
|
||||||
// If this copy operation changes the unread count in the destination
|
// If this copy operation changes the unread count in the destination
|
||||||
@ -3334,7 +3445,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localSrcFolder.moveMessages(messages, localDestFolder);
|
uidMap = localSrcFolder.moveMessages(messages, localDestFolder);
|
||||||
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
|
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
|
||||||
String origUid = entry.getKey();
|
String origUid = entry.getKey();
|
||||||
Message message = entry.getValue();
|
Message message = entry.getValue();
|
||||||
@ -3356,7 +3467,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidMap.keySet().toArray(EMPTY_STRING_ARRAY));
|
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidMap.keySet().toArray(EMPTY_STRING_ARRAY), uidMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
@ -3436,6 +3547,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
Store localStore = account.getLocalStore();
|
Store localStore = account.getLocalStore();
|
||||||
localFolder = localStore.getFolder(folder);
|
localFolder = localStore.getFolder(folder);
|
||||||
|
Map<String, String> uidMap = null;
|
||||||
if (folder.equals(account.getTrashFolderName()) || K9.FOLDER_NONE.equals(account.getTrashFolderName())) {
|
if (folder.equals(account.getTrashFolderName()) || K9.FOLDER_NONE.equals(account.getTrashFolderName())) {
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.d(K9.LOG_TAG, "Deleting messages in trash folder or trash set to -None-, not copying");
|
Log.d(K9.LOG_TAG, "Deleting messages in trash folder or trash set to -None-, not copying");
|
||||||
@ -3450,7 +3562,7 @@ public class MessagingController implements Runnable {
|
|||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.d(K9.LOG_TAG, "Deleting messages in normal folder, moving");
|
Log.d(K9.LOG_TAG, "Deleting messages in normal folder, moving");
|
||||||
|
|
||||||
localFolder.moveMessages(messages, localTrashFolder);
|
uidMap = localFolder.moveMessages(messages, localTrashFolder);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3483,7 +3595,7 @@ public class MessagingController implements Runnable {
|
|||||||
if (folder.equals(account.getTrashFolderName())) {
|
if (folder.equals(account.getTrashFolderName())) {
|
||||||
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
|
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
|
||||||
} else {
|
} else {
|
||||||
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids);
|
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap);
|
||||||
}
|
}
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
|
} else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.fsck.k9.mail;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
@ -102,11 +103,15 @@ public abstract class Folder {
|
|||||||
public abstract Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
public abstract Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
||||||
throws MessagingException;
|
throws MessagingException;
|
||||||
|
|
||||||
public abstract void appendMessages(Message[] messages) throws MessagingException;
|
public abstract Map<String, String> appendMessages(Message[] messages) throws MessagingException;
|
||||||
|
|
||||||
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {}
|
public Map<String, String> copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void moveMessages(Message[] msgs, Folder folder) throws MessagingException {}
|
public Map<String, String> moveMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
||||||
for (Message message : msgs) {
|
for (Message message : msgs) {
|
||||||
|
@ -61,6 +61,7 @@ import com.fsck.k9.Account;
|
|||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||||
|
import com.fsck.k9.helper.StringUtils;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.helper.power.TracingPowerManager;
|
import com.fsck.k9.helper.power.TracingPowerManager;
|
||||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||||
@ -89,6 +90,7 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
|||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
|
import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
|
||||||
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
||||||
|
import com.fsck.k9.mail.store.imap.ImapUtility;
|
||||||
import com.fsck.k9.mail.transport.imap.ImapSettings;
|
import com.fsck.k9.mail.transport.imap.ImapSettings;
|
||||||
import com.jcraft.jzlib.JZlib;
|
import com.jcraft.jzlib.JZlib;
|
||||||
import com.jcraft.jzlib.ZOutputStream;
|
import com.jcraft.jzlib.ZOutputStream;
|
||||||
@ -1061,53 +1063,127 @@ public class ImapStore extends Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the given messages to the specified folder.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong>
|
||||||
|
* Only the UIDs of the given {@link Message} instances are used. It is assumed that all
|
||||||
|
* UIDs represent valid messages in this folder.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param messages
|
||||||
|
* The messages to copy to the specfied folder.
|
||||||
|
* @param folder
|
||||||
|
* The name of the target folder.
|
||||||
|
*
|
||||||
|
* @return The mapping of original message UIDs to the new server UIDs.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
public Map<String, String> copyMessages(Message[] messages, Folder folder)
|
||||||
|
throws MessagingException {
|
||||||
if (!(folder instanceof ImapFolder)) {
|
if (!(folder instanceof ImapFolder)) {
|
||||||
throw new MessagingException("ImapFolder.copyMessages passed non-ImapFolder");
|
throw new MessagingException("ImapFolder.copyMessages passed non-ImapFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length == 0)
|
if (messages.length == 0) {
|
||||||
return;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
ImapFolder iFolder = (ImapFolder)folder;
|
ImapFolder iFolder = (ImapFolder)folder;
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
String[] uids = new String[messages.length];
|
String[] uids = new String[messages.length];
|
||||||
for (int i = 0, count = messages.length; i < count; i++) {
|
for (int i = 0, count = messages.length; i < count; i++) {
|
||||||
uids[i] = messages[i].getUid();
|
uids[i] = messages[i].getUid();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String remoteDestName = encodeString(encodeFolderName(iFolder.getPrefixedName()));
|
String remoteDestName = encodeString(encodeFolderName(iFolder.getPrefixedName()));
|
||||||
|
|
||||||
if (!exists(remoteDestName)) {
|
if (!exists(remoteDestName)) {
|
||||||
/*
|
// If the remote trash folder doesn't exist we try to create it.
|
||||||
* If the remote trash folder doesn't exist we try to create it.
|
if (K9.DEBUG) {
|
||||||
*/
|
Log.i(K9.LOG_TAG, "Attempting to create remote folder '" + remoteDestName +
|
||||||
if (K9.DEBUG)
|
"' for " + getLogId());
|
||||||
Log.i(K9.LOG_TAG, "IMAPMessage.copyMessages: attempting to create remote '" + remoteDestName + "' folder for " + getLogId());
|
}
|
||||||
iFolder.create(FolderType.HOLDS_MESSAGES);
|
iFolder.create(FolderType.HOLDS_MESSAGES);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists(remoteDestName)) {
|
//TODO: Split this into multiple commands if the command exceeds a certain length.
|
||||||
executeSimpleCommand(String.format("UID COPY %s %s",
|
mConnection.sendCommand(String.format("UID COPY %s %s",
|
||||||
Utility.combine(uids, ','),
|
Utility.combine(uids, ','),
|
||||||
remoteDestName));
|
remoteDestName), false);
|
||||||
} else {
|
ImapResponse response;
|
||||||
throw new MessagingException("IMAPMessage.copyMessages: remote destination folder " + folder.getName()
|
do {
|
||||||
+ " does not exist and could not be created for " + getLogId()
|
response = mConnection.readResponse();
|
||||||
, true);
|
handleUntaggedResponse(response);
|
||||||
|
} while (response.mTag == null);
|
||||||
|
|
||||||
|
Map<String, String> uidMap = null;
|
||||||
|
if (response.size() > 1) {
|
||||||
|
/*
|
||||||
|
* If the server supports UIDPLUS, then along with the COPY response it will
|
||||||
|
* return an COPYUID response code, e.g.
|
||||||
|
*
|
||||||
|
* 24 OK [COPYUID 38505 304,319:320 3956:3958] Success
|
||||||
|
*
|
||||||
|
* COPYUID is followed by UIDVALIDITY, the set of UIDs of copied messages from
|
||||||
|
* the source folder and the set of corresponding UIDs assigned to them in the
|
||||||
|
* destination folder.
|
||||||
|
*
|
||||||
|
* We can use the new UIDs included in this response to update our records.
|
||||||
|
*/
|
||||||
|
Object responseList = response.get(1);
|
||||||
|
|
||||||
|
if (responseList instanceof ImapList) {
|
||||||
|
final ImapList copyList = (ImapList) responseList;
|
||||||
|
if (copyList.size() >= 4 && copyList.getString(0).equals("COPYUID")) {
|
||||||
|
List<String> srcUids = ImapUtility.getImapSequenceValues(
|
||||||
|
copyList.getString(2));
|
||||||
|
List<String> destUids = ImapUtility.getImapSequenceValues(
|
||||||
|
copyList.getString(3));
|
||||||
|
|
||||||
|
if (srcUids != null && destUids != null) {
|
||||||
|
if (srcUids.size() == destUids.size()) {
|
||||||
|
Iterator<String> srcUidsIterator = srcUids.iterator();
|
||||||
|
Iterator<String> destUidsIterator = destUids.iterator();
|
||||||
|
uidMap = new HashMap<String, String>();
|
||||||
|
while (srcUidsIterator.hasNext() &&
|
||||||
|
destUidsIterator.hasNext()) {
|
||||||
|
String srcUid = srcUidsIterator.next();
|
||||||
|
String destUid = destUidsIterator.next();
|
||||||
|
uidMap.put(srcUid, destUid);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (K9.DEBUG) {
|
||||||
|
Log.v(K9.LOG_TAG, "Parse error: size of source UIDs " +
|
||||||
|
"list is not the same as size of destination " +
|
||||||
|
"UIDs list.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (K9.DEBUG) {
|
||||||
|
Log.v(K9.LOG_TAG, "Parsing of the sequence set failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uidMap;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw ioExceptionHandler(mConnection, ioe);
|
throw ioExceptionHandler(mConnection, ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||||
if (messages.length == 0)
|
if (messages.length == 0)
|
||||||
return;
|
return null;
|
||||||
copyMessages(messages, folder);
|
Map<String, String> uidMap = copyMessages(messages, folder);
|
||||||
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
||||||
|
return uidMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1851,20 +1927,30 @@ public class ImapStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given messages to the selected folder. This implementation also determines
|
* Appends the given messages to the selected folder.
|
||||||
* the new UID of the given message on the IMAP server and sets the Message's UID to the
|
*
|
||||||
* new server UID.
|
* <p>
|
||||||
|
* This implementation also determines the new UIDs of the given messages on the IMAP
|
||||||
|
* server and changes the messages' UIDs to the new server UIDs.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param messages
|
||||||
|
* The messages to append to the folder.
|
||||||
|
*
|
||||||
|
* @return The mapping of original message UIDs to the new server UIDs.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void appendMessages(Message[] messages) throws MessagingException {
|
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
try {
|
try {
|
||||||
|
Map<String, String> uidMap = new HashMap<String, String>();
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
mConnection.sendCommand(
|
mConnection.sendCommand(
|
||||||
String.format("APPEND %s (%s) {%d}",
|
String.format("APPEND %s (%s) {%d}",
|
||||||
encodeString(encodeFolderName(getPrefixedName())),
|
encodeString(encodeFolderName(getPrefixedName())),
|
||||||
combineFlags(message.getFlags()),
|
combineFlags(message.getFlags()),
|
||||||
message.calculateSize()), false);
|
message.calculateSize()), false);
|
||||||
|
|
||||||
ImapResponse response;
|
ImapResponse response;
|
||||||
do {
|
do {
|
||||||
response = mConnection.readResponse();
|
response = mConnection.readResponse();
|
||||||
@ -1878,16 +1964,54 @@ public class ImapStore extends Store {
|
|||||||
}
|
}
|
||||||
} while (response.mTag == null);
|
} while (response.mTag == null);
|
||||||
|
|
||||||
String newUid = getUidFromMessageId(message);
|
if (response.size() > 1) {
|
||||||
if (K9.DEBUG)
|
/*
|
||||||
Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
|
* If the server supports UIDPLUS, then along with the APPEND response it
|
||||||
|
* will return an APPENDUID response code, e.g.
|
||||||
|
*
|
||||||
|
* 11 OK [APPENDUID 2 238268] APPEND completed
|
||||||
|
*
|
||||||
|
* We can use the UID included in this response to update our records.
|
||||||
|
*/
|
||||||
|
Object responseList = response.get(1);
|
||||||
|
|
||||||
if (newUid != null) {
|
if (responseList instanceof ImapList) {
|
||||||
|
ImapList appendList = (ImapList) responseList;
|
||||||
|
if (appendList.size() >= 3 &&
|
||||||
|
appendList.getString(0).equals("APPENDUID")) {
|
||||||
|
|
||||||
|
String newUid = appendList.getString(2);
|
||||||
|
|
||||||
|
if (!StringUtils.isNullOrEmpty(newUid)) {
|
||||||
|
message.setUid(newUid);
|
||||||
|
uidMap.put(message.getUid(), newUid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This part is executed in case the server does not support UIDPLUS or does
|
||||||
|
* not implement the APPENDUID response code.
|
||||||
|
*/
|
||||||
|
String newUid = getUidFromMessageId(message);
|
||||||
|
if (K9.DEBUG) {
|
||||||
|
Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isNullOrEmpty(newUid)) {
|
||||||
|
uidMap.put(message.getUid(), newUid);
|
||||||
message.setUid(newUid);
|
message.setUid(newUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need uidMap to be null if new UIDs are not available to maintain consistency
|
||||||
|
* with the behavior of other similar methods (copyMessages, moveMessages) which
|
||||||
|
* return null.
|
||||||
|
*/
|
||||||
|
return (uidMap.size() == 0) ? null : uidMap;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw ioExceptionHandler(mConnection, ioe);
|
throw ioExceptionHandler(mConnection, ioe);
|
||||||
}
|
}
|
||||||
|
@ -1928,21 +1928,23 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
public Map<String, String> copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||||
if (!(folder instanceof LocalFolder)) {
|
if (!(folder instanceof LocalFolder)) {
|
||||||
throw new MessagingException("copyMessages called with incorrect Folder");
|
throw new MessagingException("copyMessages called with incorrect Folder");
|
||||||
}
|
}
|
||||||
((LocalFolder) folder).appendMessages(msgs, true);
|
return ((LocalFolder) folder).appendMessages(msgs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveMessages(final Message[] msgs, final Folder destFolder) throws MessagingException {
|
public Map<String, String> moveMessages(final Message[] msgs, final Folder destFolder) throws MessagingException {
|
||||||
if (!(destFolder instanceof LocalFolder)) {
|
if (!(destFolder instanceof LocalFolder)) {
|
||||||
throw new MessagingException("moveMessages called with non-LocalFolder");
|
throw new MessagingException("moveMessages called with non-LocalFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocalFolder lDestFolder = (LocalFolder)destFolder;
|
final LocalFolder lDestFolder = (LocalFolder)destFolder;
|
||||||
|
|
||||||
|
final Map<String, String> uidMap = new HashMap<String, String>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
database.execute(false, new DbCallback<Void>() {
|
database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
@ -1968,7 +1970,10 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
Log.d(K9.LOG_TAG, "Updating folder_id to " + lDestFolder.getId() + " for message with UID "
|
Log.d(K9.LOG_TAG, "Updating folder_id to " + lDestFolder.getId() + " for message with UID "
|
||||||
+ message.getUid() + ", id " + lMessage.getId() + " currently in folder " + getName());
|
+ message.getUid() + ", id " + lMessage.getId() + " currently in folder " + getName());
|
||||||
|
|
||||||
message.setUid(K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString());
|
String newUid = K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString();
|
||||||
|
message.setUid(newUid);
|
||||||
|
|
||||||
|
uidMap.put(oldUID, newUid);
|
||||||
|
|
||||||
db.execSQL("UPDATE messages " + "SET folder_id = ?, uid = ? " + "WHERE id = ?", new Object[] {
|
db.execSQL("UPDATE messages " + "SET folder_id = ?, uid = ? " + "WHERE id = ?", new Object[] {
|
||||||
lDestFolder.getId(),
|
lDestFolder.getId(),
|
||||||
@ -1976,6 +1981,11 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
lMessage.getId()
|
lMessage.getId()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a placeholder message so we won't download the original
|
||||||
|
* message again if we synchronize before the remote move is
|
||||||
|
* complete.
|
||||||
|
*/
|
||||||
LocalMessage placeHolder = new LocalMessage(oldUID, LocalFolder.this);
|
LocalMessage placeHolder = new LocalMessage(oldUID, LocalFolder.this);
|
||||||
placeHolder.setFlagInternal(Flag.DELETED, true);
|
placeHolder.setFlagInternal(Flag.DELETED, true);
|
||||||
placeHolder.setFlagInternal(Flag.SEEN, true);
|
placeHolder.setFlagInternal(Flag.SEEN, true);
|
||||||
@ -1987,6 +1997,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return uidMap;
|
||||||
} catch (WrappedException e) {
|
} catch (WrappedException e) {
|
||||||
throw(MessagingException) e.getCause();
|
throw(MessagingException) e.getCause();
|
||||||
}
|
}
|
||||||
@ -2032,8 +2043,8 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
* message, retrieve the appropriate local message instance first (if it already exists).
|
* message, retrieve the appropriate local message instance first (if it already exists).
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void appendMessages(Message[] messages) throws MessagingException {
|
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||||
appendMessages(messages, false);
|
return appendMessages(messages, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroyMessages(final Message[] messages) throws MessagingException {
|
public void destroyMessages(final Message[] messages) throws MessagingException {
|
||||||
@ -2069,10 +2080,12 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
* message, retrieve the appropriate local message instance first (if it already exists).
|
* message, retrieve the appropriate local message instance first (if it already exists).
|
||||||
* @param messages
|
* @param messages
|
||||||
* @param copy
|
* @param copy
|
||||||
|
* @return Map<String, String> uidMap of srcUids -> destUids
|
||||||
*/
|
*/
|
||||||
private void appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
|
private Map<String, String> appendMessages(final Message[] messages, final boolean copy) throws MessagingException {
|
||||||
open(OpenMode.READ_WRITE);
|
open(OpenMode.READ_WRITE);
|
||||||
try {
|
try {
|
||||||
|
final Map<String, String> uidMap = new HashMap<String, String>();
|
||||||
database.execute(true, new DbCallback<Void>() {
|
database.execute(true, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||||
@ -2085,11 +2098,26 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
long oldMessageId = -1;
|
long oldMessageId = -1;
|
||||||
String uid = message.getUid();
|
String uid = message.getUid();
|
||||||
if (uid == null || copy) {
|
if (uid == null || copy) {
|
||||||
uid = K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString();
|
/*
|
||||||
if (!copy) {
|
* Create a new message in the database
|
||||||
message.setUid(uid);
|
*/
|
||||||
}
|
String randomLocalUid = K9.LOCAL_UID_PREFIX +
|
||||||
|
UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
if (copy) {
|
||||||
|
// Save mapping: source UID -> target UID
|
||||||
|
uidMap.put(uid, randomLocalUid);
|
||||||
} else {
|
} else {
|
||||||
|
// Modify the Message instance to reference the new UID
|
||||||
|
message.setUid(randomLocalUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The message will be saved with the newly generated UID
|
||||||
|
uid = randomLocalUid;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Replace an existing message in the database
|
||||||
|
*/
|
||||||
LocalMessage oldMessage = (LocalMessage) getMessage(uid);
|
LocalMessage oldMessage = (LocalMessage) getMessage(uid);
|
||||||
|
|
||||||
if (oldMessage != null) {
|
if (oldMessage != null) {
|
||||||
@ -2169,6 +2197,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return uidMap;
|
||||||
} catch (WrappedException e) {
|
} catch (WrappedException e) {
|
||||||
throw(MessagingException) e.getCause();
|
throw(MessagingException) e.getCause();
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Pop3Store extends Store {
|
public class Pop3Store extends Store {
|
||||||
public static final String STORE_TYPE = "POP3";
|
public static final String STORE_TYPE = "POP3";
|
||||||
@ -889,7 +890,8 @@ public class Pop3Store extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessages(Message[] messages) throws MessagingException {
|
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1334,13 +1334,15 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
public Map<String, String> copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||||
moveOrCopyMessages(messages, folder.getName(), false);
|
moveOrCopyMessages(messages, folder.getName(), false);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||||
moveOrCopyMessages(messages, folder.getName(), true);
|
moveOrCopyMessages(messages, folder.getName(), true);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1915,8 +1917,9 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendMessages(Message[] messages) throws MessagingException {
|
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||||
appendWebDavMessages(messages);
|
appendWebDavMessages(messages);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
|
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
|
||||||
|
113
src/com/fsck/k9/mail/store/imap/ImapUtility.java
Normal file
113
src/com/fsck/k9/mail/store/imap/ImapUtility.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The K-9 Dog Walkers
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.fsck.k9.mail.store.imap;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for use with IMAP.
|
||||||
|
*/
|
||||||
|
public class ImapUtility {
|
||||||
|
/**
|
||||||
|
* Gets all of the values in a sequence set per RFC 3501.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Any ranges are expanded into a list of individual numbers.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* sequence-number = nz-number / "*"
|
||||||
|
* sequence-range = sequence-number ":" sequence-number
|
||||||
|
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param set
|
||||||
|
* The sequence set string as received by the server.
|
||||||
|
*
|
||||||
|
* @return The list of IDs as strings in this sequence set. If the set is invalid, an empty
|
||||||
|
* list is returned.
|
||||||
|
*/
|
||||||
|
public static List<String> getImapSequenceValues(String set) {
|
||||||
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
|
if (set != null) {
|
||||||
|
String[] setItems = set.split(",");
|
||||||
|
for (String item : setItems) {
|
||||||
|
if (item.indexOf(':') == -1) {
|
||||||
|
// simple item
|
||||||
|
try {
|
||||||
|
Integer.parseInt(item); // Don't need the value; just ensure it's valid
|
||||||
|
list.add(item);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.d(K9.LOG_TAG, "Invalid UID value", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// range
|
||||||
|
list.addAll(getImapRangeValues(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the given number range into a list of individual numbers.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* sequence-number = nz-number / "*"
|
||||||
|
* sequence-range = sequence-number ":" sequence-number
|
||||||
|
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param range
|
||||||
|
* The range string as received by the server.
|
||||||
|
*
|
||||||
|
* @return The list of IDs as strings in this range. If the range is not valid, an empty list
|
||||||
|
* is returned.
|
||||||
|
*/
|
||||||
|
public static List<String> getImapRangeValues(String range) {
|
||||||
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
|
try {
|
||||||
|
if (range != null) {
|
||||||
|
int colonPos = range.indexOf(':');
|
||||||
|
if (colonPos > 0) {
|
||||||
|
int first = Integer.parseInt(range.substring(0, colonPos));
|
||||||
|
int second = Integer.parseInt(range.substring(colonPos + 1));
|
||||||
|
if (first < second) {
|
||||||
|
for (int i = first; i <= second; i++) {
|
||||||
|
list.add(Integer.toString(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = first; i >= second; i--) {
|
||||||
|
list.add(Integer.toString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.d(K9.LOG_TAG, "Invalid range value", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
125
tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java
Normal file
125
tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The K-9 Dog Walkers
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.fsck.k9.mail.store.imap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import android.test.MoreAsserts;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class ImapUtilityTest extends TestCase {
|
||||||
|
/**
|
||||||
|
* Test getting elements of an IMAP sequence set.
|
||||||
|
*/
|
||||||
|
public void testGetImapSequenceValues() {
|
||||||
|
String[] expected;
|
||||||
|
List<String> actual;
|
||||||
|
|
||||||
|
// Test valid sets
|
||||||
|
expected = new String[] {"1"};
|
||||||
|
actual = ImapUtility.getImapSequenceValues("1");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] {"1", "3", "2"};
|
||||||
|
actual = ImapUtility.getImapSequenceValues("1,3,2");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] {"4", "5", "6"};
|
||||||
|
actual = ImapUtility.getImapSequenceValues("4:6");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] {"9", "8", "7"};
|
||||||
|
actual = ImapUtility.getImapSequenceValues("9:7");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] {"1", "2", "3", "4", "9", "8", "7"};
|
||||||
|
actual = ImapUtility.getImapSequenceValues("1,2:4,9:7");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
// Test partially invalid sets
|
||||||
|
expected = new String[] { "1", "5" };
|
||||||
|
actual = ImapUtility.getImapSequenceValues("1,x,5");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] { "1", "2", "3" };
|
||||||
|
actual = ImapUtility.getImapSequenceValues("a:d,1:3");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
// Test invalid sets
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapSequenceValues("");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapSequenceValues(null);
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapSequenceValues("a");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapSequenceValues("1:x");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting elements of an IMAP range.
|
||||||
|
*/
|
||||||
|
public void testGetImapRangeValues() {
|
||||||
|
String[] expected;
|
||||||
|
List<String> actual;
|
||||||
|
|
||||||
|
// Test valid ranges
|
||||||
|
expected = new String[] {"1", "2", "3"};
|
||||||
|
actual = ImapUtility.getImapRangeValues("1:3");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[] {"16", "15", "14"};
|
||||||
|
actual = ImapUtility.getImapRangeValues("16:14");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
// Test in-valid ranges
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues(null);
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("a");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("6");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("1:3,6");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("1:x");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
|
||||||
|
expected = new String[0];
|
||||||
|
actual = ImapUtility.getImapRangeValues("1:*");
|
||||||
|
MoreAsserts.assertEquals(expected, actual.toArray());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user