use of com.zimbra.cs.service.mail.ItemActionHelper in project zm-mailbox by Zimbra.
the class ImapHandler method doCOPY.
boolean doCOPY(String tag, String sequenceSet, ImapPath path, boolean byUID) throws IOException, ImapException {
checkCommandThrottle(new CopyCommand(sequenceSet, path));
if (!checkState(tag, State.SELECTED)) {
return true;
}
String command = (byUID ? "UID COPY" : "COPY");
String copyuid = "";
List<MailItem> copies = new ArrayList<MailItem>();
ImapFolder i4folder = getSelectedFolder();
if (i4folder == null) {
throw new ImapSessionClosedException();
}
Mailbox mbox = i4folder.getMailbox();
Set<ImapMessage> i4set;
mbox.lock.lock();
try {
i4set = i4folder.getSubsequence(tag, sequenceSet, byUID);
} finally {
mbox.lock.release();
}
// accessed mailbox that contains expunged messages."
if (!byUID && i4set.contains(null)) {
sendNO(tag, "COPY rejected because some of the requested messages were expunged");
return true;
}
i4set.remove(null);
try {
if (!path.isVisible()) {
throw ImapServiceException.FOLDER_NOT_VISIBLE(path.asImapPath());
} else if (!path.isWritable(ACL.RIGHT_INSERT)) {
throw ImapServiceException.FOLDER_NOT_WRITABLE(path.asImapPath());
}
Object mboxobj = path.getOwnerMailbox();
ItemId iidTarget;
boolean sameMailbox = false;
int uvv;
// check target folder permissions before attempting the copy
if (mboxobj instanceof Mailbox) {
sameMailbox = mbox.getAccountId().equalsIgnoreCase(((Mailbox) mboxobj).getAccountId());
Folder folder = (Folder) path.getFolder();
iidTarget = new ItemId(folder);
uvv = ImapFolder.getUIDValidity(folder);
} else if (mboxobj instanceof ZMailbox) {
ZFolder zfolder = (ZFolder) path.getFolder();
iidTarget = new ItemId(zfolder.getId(), path.getOwnerAccount().getId());
uvv = ImapFolder.getUIDValidity(zfolder);
} else {
throw AccountServiceException.NO_SUCH_ACCOUNT(path.getOwner());
}
long checkpoint = System.currentTimeMillis();
List<Integer> srcUIDs = extensionEnabled("UIDPLUS") ? new ArrayList<Integer>() : null;
List<Integer> copyUIDs = extensionEnabled("UIDPLUS") ? new ArrayList<Integer>() : null;
int i = 0;
List<ImapMessage> i4list = new ArrayList<ImapMessage>(SUGGESTED_COPY_BATCH_SIZE);
List<Integer> idlist = new ArrayList<Integer>(SUGGESTED_COPY_BATCH_SIZE);
List<Integer> createdList = new ArrayList<Integer>(SUGGESTED_COPY_BATCH_SIZE);
for (ImapMessage i4msg : i4set) {
// we're sending 'em off in batches of 50
i4list.add(i4msg);
idlist.add(i4msg.msgId);
if (++i % SUGGESTED_COPY_BATCH_SIZE != 0 && i != i4set.size()) {
continue;
}
if (sameMailbox) {
List<MailItem> copyMsgs;
try {
MailItem.Type type = MailItem.Type.UNKNOWN;
int[] mItemIds = new int[i4list.size()];
int counter = 0;
for (ImapMessage curMsg : i4list) {
mItemIds[counter++] = curMsg.msgId;
if (counter == 1) {
type = curMsg.getType();
} else if (curMsg.getType() != type) {
type = MailItem.Type.UNKNOWN;
}
}
copyMsgs = mbox.imapCopy(getContext(), mItemIds, type, iidTarget.getId());
} catch (IOException e) {
throw ServiceException.FAILURE("Caught IOException executing " + this, e);
}
copies.addAll(copyMsgs);
for (MailItem target : copyMsgs) {
createdList.add(target.getImapUid());
}
} else {
ItemActionHelper op = ItemActionHelper.COPY(getContext(), mbox, null, idlist, MailItem.Type.UNKNOWN, null, iidTarget);
for (String target : op.getCreatedIds()) {
createdList.add(new ItemId(target, selectedFolder.getAuthenticatedAccountId()).getId());
}
}
if (createdList.size() != i4list.size()) {
throw ServiceException.FAILURE("mismatch between original and target count during IMAP COPY", null);
}
if (srcUIDs != null) {
for (ImapMessage source : i4list) {
srcUIDs.add(source.imapUid);
}
for (Integer target : createdList) {
copyUIDs.add(target);
}
}
i4list.clear();
idlist.clear();
createdList.clear();
// send a gratuitous untagged response to keep pissy clients from closing the socket from inactivity
long now = System.currentTimeMillis();
if (now - checkpoint > MAXIMUM_IDLE_PROCESSING_MILLIS) {
sendIdleUntagged();
checkpoint = now;
}
}
if (uvv > 0 && srcUIDs != null && srcUIDs.size() > 0) {
copyuid = "[COPYUID " + uvv + ' ' + ImapFolder.encodeSubsequence(srcUIDs) + ' ' + ImapFolder.encodeSubsequence(copyUIDs) + "] ";
}
} catch (IOException e) {
// 6.4.7: "If the COPY command is unsuccessful for any reason, server implementations
// MUST restore the destination mailbox to its state before the COPY attempt."
ZimbraLog.imap.warn("%s failed", command, e);
sendNO(tag, command + " failed");
return true;
} catch (ServiceException e) {
// 6.4.7: "If the COPY command is unsuccessful for any reason, server implementations
// MUST restore the destination mailbox to its state before the COPY attempt."
String rcode = "";
if (e.getCode().equals(MailServiceException.NO_SUCH_FOLDER)) {
ZimbraLog.imap.info("%s failed: no such folder: %s", command, path);
if (path.isCreatable()) {
rcode = "[TRYCREATE] ";
}
} else if (e.getCode().equals(ImapServiceException.FOLDER_NOT_VISIBLE)) {
ZimbraLog.imap.info("%s failed: folder not visible: %s", command, path);
} else if (e.getCode().equals(ImapServiceException.FOLDER_NOT_WRITABLE)) {
ZimbraLog.imap.info("%s failed: folder not writable: %s", command, path);
} else {
ZimbraLog.imap.warn("%s failed", command, e);
}
sendNO(tag, rcode + command + " failed");
return canContinue(e);
}
// RFC 2180 4.4: "COPY is the only IMAP4 sequence number command that is safe to allow
// an EXPUNGE response on. This is because a client is not permitted
// to cascade several COPY commands together."
sendNotifications(true, false);
sendOK(tag, copyuid + command + " completed");
return true;
}
Aggregations