Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java +1 −0 Original line number Diff line number Diff line Loading @@ -17,4 +17,5 @@ public class Commands { public static final String UID_SEARCH = "UID SEARCH"; public static final String UID_STORE = "UID STORE"; public static final String UID_FETCH = "UID FETCH"; public static final String UID_COPY = "UID COPY"; } k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +23 −27 Original line number Diff line number Diff line Loading @@ -35,10 +35,12 @@ import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.imap.command.ImapCommandFactory; import com.fsck.k9.mail.store.imap.command.UidCopyCommand; import com.fsck.k9.mail.store.imap.command.UidFetchCommand; import com.fsck.k9.mail.store.imap.command.UidSearchCommand; import com.fsck.k9.mail.store.imap.command.UidStoreCommand; import com.fsck.k9.mail.store.imap.response.BaseResponse; import com.fsck.k9.mail.store.imap.response.CopyUidResponse; import com.fsck.k9.mail.store.imap.response.SearchResponse; import timber.log.Timber; Loading Loading @@ -349,12 +351,11 @@ public class ImapFolder extends Folder<ImapMessage> { ImapFolder imapFolder = (ImapFolder) folder; checkOpen(); //only need READ access String[] uids = new String[messages.size()]; List<Long> uids = new ArrayList<>(messages.size()); for (int i = 0, count = messages.size(); i < count; i++) { uids[i] = messages.get(i).getUid(); uids.add(Long.parseLong(messages.get(i).getUid())); } try { String encodedDestinationFolderName = folderNameCodec.encode(imapFolder.getPrefixedName()); String escapedDestinationFolderName = ImapUtility.encodeString(encodedDestinationFolderName); Loading @@ -369,22 +370,17 @@ public class ImapFolder extends Folder<ImapMessage> { imapFolder.create(FolderType.HOLDS_MESSAGES); } //TODO: Split this into multiple commands if the command exceeds a certain length. List<ImapResponse> responses = executeSimpleCommand(String.format("UID COPY %s %s", combine(uids, ','), escapedDestinationFolderName)); // Get the tagged response for the UID COPY command ImapResponse response = getLastResponse(responses); UidCopyCommand command = commandFactory.createUidCopyCommandBuilder(this) .idSet(uids) .destinationFolderName(escapedDestinationFolderName) .build(); CopyUidResponse copyUidResponse = CopyUidResponse.parse(response); CopyUidResponse copyUidResponse = command.execute(); if (copyUidResponse == null) { return null; } return copyUidResponse.getUidMapping(); } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } } @Override Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/command/ImapCommandFactory.java +4 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,10 @@ public class ImapCommandFactory { return new UidFetchCommand.Builder(this, folder, maximumAutoDownloadMessageSize); } public UidCopyCommand.Builder createUidCopyCommandBuilder(ImapFolder folder) { return new UidCopyCommand.Builder(this, folder); } ImapConnection getConnection() { return connection; } Loading k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/command/UidCopyCommand.java 0 → 100644 +81 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.store.imap.command; import java.io.IOException; import java.util.List; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.store.imap.Commands; import com.fsck.k9.mail.store.imap.ImapFolder; import com.fsck.k9.mail.store.imap.ImapResponse; import com.fsck.k9.mail.store.imap.response.CopyUidResponse; public class UidCopyCommand extends SelectByIdCommand { private String destinationFolderName; private UidCopyCommand(ImapCommandFactory commandFactory) { super(commandFactory); } @Override public String createCommandString() { StringBuilder builder = new StringBuilder(Commands.UID_COPY).append(" "); super.addIds(builder); addDestinationFolderName(builder); return builder.toString().trim(); } @Override public CopyUidResponse execute() throws MessagingException { try { List<List<ImapResponse>> responses = executeInternal(false); CopyUidResponse response = CopyUidResponse.parse(commandFactory, responses); folder.handleUntaggedResponses(response); return response; } catch (IOException ioe) { throw folder.ioExceptionHandler(commandFactory.getConnection(), ioe); } } private void addDestinationFolderName(StringBuilder builder) { builder.append(destinationFolderName); } @Override Builder newBuilder() { return new Builder(commandFactory, folder) .useUids(useUids) .idSet(idSet) .idRanges(idRanges) .destinationFolderName(destinationFolderName); } public static class Builder extends SelectByIdCommand.Builder<UidCopyCommand, Builder> { public Builder(ImapCommandFactory commandFactory, ImapFolder folder) { super(commandFactory, folder); } public Builder destinationFolderName(String destinationFolderName) { command.destinationFolderName = destinationFolderName; return builder; } @Override UidCopyCommand createCommand() { return new UidCopyCommand(null); } @Override Builder createBuilder() { return this; } } } k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/CopyUidResponse.java→k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/response/CopyUidResponse.java +87 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.store.imap; package com.fsck.k9.mail.store.imap.response; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fsck.k9.mail.store.imap.ImapList; import com.fsck.k9.mail.store.imap.ImapResponse; import com.fsck.k9.mail.store.imap.ImapUtility; import com.fsck.k9.mail.store.imap.Responses; import com.fsck.k9.mail.store.imap.command.ImapCommandFactory; import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase; import static com.fsck.k9.mail.store.imap.ImapUtility.getImapSequenceValues; class CopyUidResponse { private final Map<String, String> uidMapping; public class CopyUidResponse extends BaseResponse { private Map<String, String> uidMapping; private CopyUidResponse(ImapCommandFactory commandFactory, List<ImapResponse> imapResponse) { super(commandFactory, imapResponse); } public static CopyUidResponse parse(ImapCommandFactory commandFactory, List<List<ImapResponse>> imapResponses) { CopyUidResponse combinedResponse = null; for (List<ImapResponse> imapResponse : imapResponses) { CopyUidResponse copyUidResponse = new CopyUidResponse(commandFactory, imapResponse); if (combinedResponse == null) { combinedResponse = copyUidResponse; } else { combinedResponse.combine(copyUidResponse); } } private CopyUidResponse(Map<String, String> uidMapping) { this.uidMapping = Collections.unmodifiableMap(uidMapping); return combinedResponse; } public static CopyUidResponse parse(ImapResponse response) { @Override void parseResponse(List<ImapResponse> imapResponses) { ImapResponse response = ImapUtility.getLastResponse(imapResponses); if (!response.isTagged() || response.size() < 2 || !equalsIgnoreCase(response.get(0), Responses.OK) || !response.isList(1)) { return null; return; } ImapList responseTextList = response.getList(1); if (responseTextList.size() < 4 || !equalsIgnoreCase(responseTextList.get(0), Responses.COPYUID) || !responseTextList.isString(1) || !responseTextList.isString(2) || !responseTextList.isString(3)) { return null; return; } List<String> sourceUids = getImapSequenceValues(responseTextList.getString(2)); Loading @@ -35,19 +59,28 @@ class CopyUidResponse { int size = sourceUids.size(); if (size == 0 || size != destinationUids.size()) { return null; return; } Map<String, String> uidMapping = new HashMap<>(size); uidMapping = new HashMap<>(size); for (int i = 0; i < size; i++) { String sourceUid = sourceUids.get(i); String destinationUid = destinationUids.get(i); uidMapping.put(sourceUid, destinationUid); } } return new CopyUidResponse(uidMapping); @Override void combine(BaseResponse baseResponse) { if (baseResponse == null) { return; } super.combine(baseResponse); CopyUidResponse copyUidResponse = (CopyUidResponse) baseResponse; this.uidMapping.putAll(copyUidResponse.getUidMapping()); } public Map<String, String> getUidMapping() { return uidMapping; } Loading Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java +1 −0 Original line number Diff line number Diff line Loading @@ -17,4 +17,5 @@ public class Commands { public static final String UID_SEARCH = "UID SEARCH"; public static final String UID_STORE = "UID STORE"; public static final String UID_FETCH = "UID FETCH"; public static final String UID_COPY = "UID COPY"; }
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +23 −27 Original line number Diff line number Diff line Loading @@ -35,10 +35,12 @@ import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.imap.command.ImapCommandFactory; import com.fsck.k9.mail.store.imap.command.UidCopyCommand; import com.fsck.k9.mail.store.imap.command.UidFetchCommand; import com.fsck.k9.mail.store.imap.command.UidSearchCommand; import com.fsck.k9.mail.store.imap.command.UidStoreCommand; import com.fsck.k9.mail.store.imap.response.BaseResponse; import com.fsck.k9.mail.store.imap.response.CopyUidResponse; import com.fsck.k9.mail.store.imap.response.SearchResponse; import timber.log.Timber; Loading Loading @@ -349,12 +351,11 @@ public class ImapFolder extends Folder<ImapMessage> { ImapFolder imapFolder = (ImapFolder) folder; checkOpen(); //only need READ access String[] uids = new String[messages.size()]; List<Long> uids = new ArrayList<>(messages.size()); for (int i = 0, count = messages.size(); i < count; i++) { uids[i] = messages.get(i).getUid(); uids.add(Long.parseLong(messages.get(i).getUid())); } try { String encodedDestinationFolderName = folderNameCodec.encode(imapFolder.getPrefixedName()); String escapedDestinationFolderName = ImapUtility.encodeString(encodedDestinationFolderName); Loading @@ -369,22 +370,17 @@ public class ImapFolder extends Folder<ImapMessage> { imapFolder.create(FolderType.HOLDS_MESSAGES); } //TODO: Split this into multiple commands if the command exceeds a certain length. List<ImapResponse> responses = executeSimpleCommand(String.format("UID COPY %s %s", combine(uids, ','), escapedDestinationFolderName)); // Get the tagged response for the UID COPY command ImapResponse response = getLastResponse(responses); UidCopyCommand command = commandFactory.createUidCopyCommandBuilder(this) .idSet(uids) .destinationFolderName(escapedDestinationFolderName) .build(); CopyUidResponse copyUidResponse = CopyUidResponse.parse(response); CopyUidResponse copyUidResponse = command.execute(); if (copyUidResponse == null) { return null; } return copyUidResponse.getUidMapping(); } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } } @Override Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/command/ImapCommandFactory.java +4 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,10 @@ public class ImapCommandFactory { return new UidFetchCommand.Builder(this, folder, maximumAutoDownloadMessageSize); } public UidCopyCommand.Builder createUidCopyCommandBuilder(ImapFolder folder) { return new UidCopyCommand.Builder(this, folder); } ImapConnection getConnection() { return connection; } Loading
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/command/UidCopyCommand.java 0 → 100644 +81 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.store.imap.command; import java.io.IOException; import java.util.List; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.store.imap.Commands; import com.fsck.k9.mail.store.imap.ImapFolder; import com.fsck.k9.mail.store.imap.ImapResponse; import com.fsck.k9.mail.store.imap.response.CopyUidResponse; public class UidCopyCommand extends SelectByIdCommand { private String destinationFolderName; private UidCopyCommand(ImapCommandFactory commandFactory) { super(commandFactory); } @Override public String createCommandString() { StringBuilder builder = new StringBuilder(Commands.UID_COPY).append(" "); super.addIds(builder); addDestinationFolderName(builder); return builder.toString().trim(); } @Override public CopyUidResponse execute() throws MessagingException { try { List<List<ImapResponse>> responses = executeInternal(false); CopyUidResponse response = CopyUidResponse.parse(commandFactory, responses); folder.handleUntaggedResponses(response); return response; } catch (IOException ioe) { throw folder.ioExceptionHandler(commandFactory.getConnection(), ioe); } } private void addDestinationFolderName(StringBuilder builder) { builder.append(destinationFolderName); } @Override Builder newBuilder() { return new Builder(commandFactory, folder) .useUids(useUids) .idSet(idSet) .idRanges(idRanges) .destinationFolderName(destinationFolderName); } public static class Builder extends SelectByIdCommand.Builder<UidCopyCommand, Builder> { public Builder(ImapCommandFactory commandFactory, ImapFolder folder) { super(commandFactory, folder); } public Builder destinationFolderName(String destinationFolderName) { command.destinationFolderName = destinationFolderName; return builder; } @Override UidCopyCommand createCommand() { return new UidCopyCommand(null); } @Override Builder createBuilder() { return this; } } }
k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/CopyUidResponse.java→k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/response/CopyUidResponse.java +87 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.store.imap; package com.fsck.k9.mail.store.imap.response; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fsck.k9.mail.store.imap.ImapList; import com.fsck.k9.mail.store.imap.ImapResponse; import com.fsck.k9.mail.store.imap.ImapUtility; import com.fsck.k9.mail.store.imap.Responses; import com.fsck.k9.mail.store.imap.command.ImapCommandFactory; import static com.fsck.k9.mail.store.imap.ImapResponseParser.equalsIgnoreCase; import static com.fsck.k9.mail.store.imap.ImapUtility.getImapSequenceValues; class CopyUidResponse { private final Map<String, String> uidMapping; public class CopyUidResponse extends BaseResponse { private Map<String, String> uidMapping; private CopyUidResponse(ImapCommandFactory commandFactory, List<ImapResponse> imapResponse) { super(commandFactory, imapResponse); } public static CopyUidResponse parse(ImapCommandFactory commandFactory, List<List<ImapResponse>> imapResponses) { CopyUidResponse combinedResponse = null; for (List<ImapResponse> imapResponse : imapResponses) { CopyUidResponse copyUidResponse = new CopyUidResponse(commandFactory, imapResponse); if (combinedResponse == null) { combinedResponse = copyUidResponse; } else { combinedResponse.combine(copyUidResponse); } } private CopyUidResponse(Map<String, String> uidMapping) { this.uidMapping = Collections.unmodifiableMap(uidMapping); return combinedResponse; } public static CopyUidResponse parse(ImapResponse response) { @Override void parseResponse(List<ImapResponse> imapResponses) { ImapResponse response = ImapUtility.getLastResponse(imapResponses); if (!response.isTagged() || response.size() < 2 || !equalsIgnoreCase(response.get(0), Responses.OK) || !response.isList(1)) { return null; return; } ImapList responseTextList = response.getList(1); if (responseTextList.size() < 4 || !equalsIgnoreCase(responseTextList.get(0), Responses.COPYUID) || !responseTextList.isString(1) || !responseTextList.isString(2) || !responseTextList.isString(3)) { return null; return; } List<String> sourceUids = getImapSequenceValues(responseTextList.getString(2)); Loading @@ -35,19 +59,28 @@ class CopyUidResponse { int size = sourceUids.size(); if (size == 0 || size != destinationUids.size()) { return null; return; } Map<String, String> uidMapping = new HashMap<>(size); uidMapping = new HashMap<>(size); for (int i = 0; i < size; i++) { String sourceUid = sourceUids.get(i); String destinationUid = destinationUids.get(i); uidMapping.put(sourceUid, destinationUid); } } return new CopyUidResponse(uidMapping); @Override void combine(BaseResponse baseResponse) { if (baseResponse == null) { return; } super.combine(baseResponse); CopyUidResponse copyUidResponse = (CopyUidResponse) baseResponse; this.uidMapping.putAll(copyUidResponse.getUidMapping()); } public Map<String, String> getUidMapping() { return uidMapping; } Loading