Search in sources :

Example 11 with ItemIdentifier

use of com.zimbra.common.mailbox.ItemIdentifier in project zm-mailbox by Zimbra.

the class Mailbox method getItemsById.

/**
 * @returns MailItems with the specified ids.
 * @throws NoSuchItemException if any item does not exist
 */
@Override
public List<ZimbraMailItem> getItemsById(OpContext octxt, Collection<ItemIdentifier> ids) throws ServiceException {
    List<Integer> idInts = Lists.newArrayListWithCapacity(ids.size());
    for (ItemIdentifier iid : ids) {
        idInts.add(iid.id);
    }
    MailItem[] mitms = getItemById(OperationContext.asOperationContext(octxt), idInts, MailItem.Type.UNKNOWN);
    if (null == mitms || (mitms.length == 0)) {
        return Collections.emptyList();
    }
    return Lists.newArrayList(mitms);
}
Also used : ItemIdentifier(com.zimbra.common.mailbox.ItemIdentifier) DbMailItem(com.zimbra.cs.db.DbMailItem) ZimbraMailItem(com.zimbra.common.mailbox.ZimbraMailItem)

Example 12 with ItemIdentifier

use of com.zimbra.common.mailbox.ItemIdentifier in project zm-mailbox by Zimbra.

the class ContactMemberOfMap method setOfMemberOf.

public static Set<String> setOfMemberOf(String acctId, int id, Map<String, Set<String>> memberOfMap) {
    if (memberOfMap == null) {
        return null;
    }
    Set<String> memberOf = memberOfMap.get(Integer.toString(id));
    if (memberOf != null) {
        return memberOf;
    }
    ItemIdentifier ident = ItemIdentifier.fromAccountIdAndItemId(acctId, id);
    return memberOfMap.get(ident.toString());
}
Also used : ItemIdentifier(com.zimbra.common.mailbox.ItemIdentifier)

Example 13 with ItemIdentifier

use of com.zimbra.common.mailbox.ItemIdentifier in project zm-mailbox by Zimbra.

the class ImapHandler method doSTORE.

private boolean doSTORE(String tag, String sequenceSet, List<String> flagNames, StoreAction operation, boolean silent, int modseq, boolean byUID) throws IOException, ImapException {
    checkCommandThrottle(new StoreCommand(sequenceSet, flagNames, operation, modseq));
    if (!checkState(tag, State.SELECTED)) {
        return true;
    }
    ImapFolder i4folder = getSelectedFolder();
    if (i4folder == null) {
        throw new ImapSessionClosedException();
    }
    if (!i4folder.isWritable()) {
        sendNO(tag, "mailbox selected READ-ONLY");
        return true;
    }
    if (modseq >= 0) {
        activateExtension(ImapExtension.CONDSTORE);
    }
    boolean modseqEnabled = sessionActivated(ImapExtension.CONDSTORE);
    // MUST reject any such command with the tagged BAD response."
    if (!modseqEnabled && modseq >= 0) {
        throw new ImapParseException(tag, "NOMODSEQ", "cannot STORE UNCHANGEDSINCE in this mailbox", true);
    }
    ImapMessageSet modifyConflicts = modseqEnabled ? new ImapMessageSet() : null;
    String command = (byUID ? "UID STORE" : "STORE");
    List<Tag> newTags = (operation != StoreAction.REMOVE ? new ArrayList<Tag>() : null);
    MailboxStore mbox = selectedFolderListener.getMailbox();
    Set<ImapMessage> i4set;
    mbox.lock(true);
    try {
        i4set = i4folder.getSubsequence(tag, sequenceSet, byUID);
    } finally {
        mbox.unlock();
    }
    boolean allPresent = byUID || !i4set.contains(null);
    i4set.remove(null);
    try {
        // list of tag names (not including Flags)
        List<String> tags = Lists.newArrayList();
        // just Flag objects, no need to convert Tag objects to ImapFlag here
        Set<ImapFlag> i4flags = new HashSet<ImapFlag>(flagNames.size());
        for (String name : flagNames) {
            ImapFlag i4flag = i4folder.getFlagByName(name);
            if (i4flag == null) {
                tags.add(name);
                // new tag for this folder
                continue;
            } else if (i4flag.mId > 0) {
                tags.add(i4flag.mName);
            } else {
                i4flags.add(i4flag);
            }
            if (operation != StoreAction.REMOVE) {
                if (i4flag.mId == Flag.ID_DELETED) {
                    if (!i4folder.getPath().isWritable(ACL.RIGHT_DELETE)) {
                        throw ServiceException.PERM_DENIED("you do not have permission to set the \\Deleted flag");
                    }
                } else if (i4flag.mPermanent && (!i4folder.getPath().isWritable(ACL.RIGHT_WRITE))) {
                    throw ServiceException.PERM_DENIED("you do not have permission to set the " + i4flag.mName + " flag");
                }
            }
        }
        // if we're doing a STORE FLAGS (i.e. replace), precompute the new set of flags for all the affected messages
        int flags = Flag.BITMASK_UNREAD;
        short sflags = 0;
        if (operation == StoreAction.REPLACE) {
            for (ImapFlag i4flag : i4flags) {
                if (!i4flag.mPermanent) {
                    sflags = (byte) (i4flag.mPositive ? sflags | i4flag.mBitmask : sflags & ~i4flag.mBitmask);
                } else {
                    flags = (int) (i4flag.mPositive ? flags | i4flag.mBitmask : flags & ~i4flag.mBitmask);
                }
            }
        }
        long checkpoint = System.currentTimeMillis();
        int i = 0;
        List<ImapMessage> i4list = new ArrayList<ImapMessage>(SUGGESTED_BATCH_SIZE);
        List<Integer> idlist = new ArrayList<Integer>(SUGGESTED_BATCH_SIZE);
        for (ImapMessage msg : i4set) {
            // we're sending 'em off in batches of 100
            i4list.add(msg);
            idlist.add(msg.msgId);
            if (++i % SUGGESTED_BATCH_SIZE != 0 && i != i4set.size()) {
                continue;
            }
            mbox.lock(true);
            try {
                String folderOwner = i4folder.getFolder().getFolderItemIdentifier().accountId;
                List<ItemIdentifier> itemIds = ItemIdentifier.fromAccountIdAndItemIds((folderOwner != null) ? folderOwner : mbox.getAccountId(), idlist);
                if (modseq >= 0) {
                    List<ZimbraMailItem> items = mbox.getItemsById(getContext(), itemIds);
                    for (int idx = items.size() - 1; idx >= 0; idx--) {
                        ImapMessage i4msg = i4list.get(idx);
                        if (items.get(idx).getModifiedSequence() > modseq) {
                            modifyConflicts.add(i4msg);
                            i4list.remove(idx);
                            idlist.remove(idx);
                            allPresent = false;
                        }
                    }
                }
                try {
                    // if it was a STORE [+-]?FLAGS.SILENT, temporarily disable notifications
                    if (silent && !modseqEnabled) {
                        i4folder.disableNotifications();
                    }
                    if (operation == StoreAction.REPLACE) {
                        // replace real tags and flags on all messages
                        mbox.setTags(getContext(), itemIds, flags, tags);
                        // replace session tags on all messages
                        for (ImapMessage i4msg : i4list) {
                            i4msg.setSessionFlags(sflags, i4folder);
                        }
                    } else {
                        for (ImapFlag i4flag : i4flags) {
                            boolean add = operation == StoreAction.ADD ^ !i4flag.mPositive;
                            if (i4flag.mPermanent) {
                                // real Flag (not a Tag); do a batch update to the DB
                                if ((i4flag.mBitmask & Flag.BITMASK_DELETED) > 0) {
                                    ZimbraLog.imap.info("IMAP client has flagged the item with id %d to be Deleted altertag", msg.msgId);
                                }
                                mbox.alterTag(getContext(), itemIds, i4flag.mName, add);
                            } else {
                                // session tag; update one-by-one in memory only
                                for (ImapMessage i4msg : i4list) {
                                    i4msg.setSessionFlags((short) (add ? i4msg.sflags | i4flag.mBitmask : i4msg.sflags & ~i4flag.mBitmask), i4folder);
                                }
                            }
                        }
                        boolean add = operation == StoreAction.ADD;
                        // add (or remove) Tags
                        for (String tagName : tags) {
                            mbox.alterTag(getContext(), itemIds, tagName, add);
                        }
                    }
                } finally {
                    // if it was a STORE [+-]?FLAGS.SILENT, reenable notifications
                    i4folder.enableNotifications();
                }
            } finally {
                mbox.unlock();
            }
            if (!silent || modseqEnabled) {
                for (ImapMessage i4msg : i4list) {
                    ImapFolder.DirtyMessage dirty = i4folder.undirtyMessage(i4msg);
                    if (silent && (dirty == null || dirty.modseq <= 0)) {
                        continue;
                    }
                    StringBuilder ntfn = new StringBuilder();
                    boolean empty = true;
                    ntfn.append(i4msg.sequence).append(" FETCH (");
                    if (!silent) {
                        ntfn.append(i4msg.getFlags(i4folder));
                        empty = false;
                    }
                    // caused by a UID command..."
                    if (byUID) {
                        ntfn.append(empty ? "" : " ").append("UID ").append(i4msg.imapUid);
                        empty = false;
                    }
                    if (dirty != null && dirty.modseq > 0 && modseqEnabled) {
                        ntfn.append(empty ? "" : " ").append("MODSEQ (").append(dirty.modseq).append(')');
                        empty = false;
                    }
                    sendUntagged(ntfn.append(')').toString());
                }
            } else {
                // 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;
                }
            }
            i4list.clear();
            idlist.clear();
        }
    } catch (ServiceException e) {
        deleteTags(newTags);
        if (e.getCode().equals(MailServiceException.INVALID_NAME)) {
            ZimbraLog.imap.info("%s failed: %s", command, e.getMessage());
        } else {
            ZimbraLog.imap.warn("%s failed", command, e);
        }
        sendNO(tag, command + " failed");
        return canContinue(e);
    }
    boolean hadConflicts = modifyConflicts != null && !modifyConflicts.isEmpty();
    String conflicts = hadConflicts ? " [MODIFIED " + ImapFolder.encodeSubsequence(modifyConflicts, byUID) + ']' : "";
    sendNotifications(byUID, false);
    // a FETCH response for the non-expunged messages along with a tagged NO."
    if (silent || allPresent) {
        sendOK(tag, command + conflicts + " completed");
    } else {
        sendNO(tag, command + conflicts + " completed");
    }
    return true;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) ArrayList(java.util.ArrayList) ItemIdentifier(com.zimbra.common.mailbox.ItemIdentifier) ZimbraMailItem(com.zimbra.common.mailbox.ZimbraMailItem) ImapMessageSet(com.zimbra.cs.imap.ImapMessage.ImapMessageSet) LinkedHashSet(java.util.LinkedHashSet) HashSet(java.util.HashSet) ImapFlag(com.zimbra.cs.imap.ImapFlagCache.ImapFlag) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) Tag(com.zimbra.cs.mailbox.Tag)

Example 14 with ItemIdentifier

use of com.zimbra.common.mailbox.ItemIdentifier in project zm-mailbox by Zimbra.

the class ImapHandler method fetch.

private boolean fetch(String tag, String sequenceSet, int attributes, List<ImapPartSpecifier> parts, boolean byUID, int changedSince, boolean standalone, boolean allowOutOfRangeMsgSeq) throws IOException, ImapException {
    if (!checkState(tag, State.SELECTED)) {
        return true;
    }
    ImapFolder i4folder = getSelectedFolder();
    if (i4folder == null) {
        throw new ImapSessionClosedException();
    }
    // of whether a UID was specified as a message data item to the FETCH."
    if (byUID) {
        attributes |= FETCH_UID;
    }
    String command = (byUID ? "UID FETCH" : "FETCH");
    boolean markRead = i4folder.isWritable() && (attributes & FETCH_MARK_READ) != 0;
    // the CHANGEDSINCE UID FETCH modifier."
    if ((attributes & FETCH_VANISHED) != 0 && (!byUID || changedSince < 0)) {
        throw new ImapParseException(tag, "cannot specify VANISHED without CHANGEDSINCE");
    }
    if (changedSince >= 0) {
        attributes |= FETCH_MODSEQ;
    }
    if ((attributes & FETCH_MODSEQ) != 0) {
        activateExtension(ImapExtension.CONDSTORE);
    }
    boolean modseqEnabled = sessionActivated(ImapExtension.CONDSTORE);
    // MUST reject any such command with the tagged BAD response."
    if (!modseqEnabled && (attributes & FETCH_MODSEQ) != 0) {
        throw new ImapParseException(tag, "NOMODSEQ", "cannot FETCH MODSEQ in this mailbox", true);
    }
    List<ImapPartSpecifier> fullMessage = new ArrayList<ImapPartSpecifier>();
    if (parts != null && !parts.isEmpty()) {
        for (Iterator<ImapPartSpecifier> it = parts.iterator(); it.hasNext(); ) {
            ImapPartSpecifier pspec = it.next();
            if (pspec.isEntireMessage()) {
                it.remove();
                fullMessage.add(pspec);
            }
        }
    }
    ImapMessageSet i4set;
    MailboxStore mbox = i4folder.getMailbox();
    mbox.lock(false);
    try {
        i4set = i4folder.getSubsequence(tag, sequenceSet, byUID, allowOutOfRangeMsgSeq, true);
        i4set.remove(null);
    } finally {
        mbox.unlock();
    }
    // if VANISHED was requested, we need to return the set of UIDs that *don't* exist in the folder
    if (byUID && (attributes & FETCH_VANISHED) != 0) {
        int highwater = Integer.MAX_VALUE;
        try {
            highwater = i4folder.getCurrentMODSEQ();
        } catch (ServiceException e) {
        }
        if (highwater > changedSince) {
            String vanished = i4folder.invertSubsequence(sequenceSet, true, i4set);
            if (!vanished.isEmpty()) {
                sendUntagged("VANISHED (EARLIER) " + vanished);
            }
        }
    }
    // make sure it's not just a set of nothing but expunged messages
    if (!byUID && !i4set.isEmpty()) {
        boolean nonePresent = true;
        for (ImapMessage i4msg : i4set) {
            if (!i4msg.isExpunged()) {
                nonePresent = false;
                break;
            }
        }
        if (nonePresent) {
            // expunged, the server SHOULD return only a tagged NO."
            if (standalone) {
                sendNO(tag, "all of the requested messages have been expunged");
            }
            return true;
        }
    }
    // if a CHANGEDSINCE sequence number was specified, narrow the message set before iterating over the messages
    if (changedSince >= 0) {
        try {
            // get a list of all the messages modified since the checkpoint
            ImapMessageSet modified = new ImapMessageSet();
            for (int id : mbox.getIdsOfModifiedItemsInFolder(getContext(), changedSince, i4folder.getId())) {
                ImapMessage i4msg = i4folder.getById(id);
                if (i4msg != null) {
                    modified.add(i4msg);
                }
            }
            // and intersect those "modified" messages with the set of requested messages
            i4set.retainAll(modified);
        } catch (ServiceException e) {
            if (standalone) {
                ZimbraLog.imap.warn(command + " failed", e);
                sendNO(tag, command + " failed");
                return canContinue(e);
            }
        }
    }
    mbox.lock(true);
    try {
        if (i4folder.areTagsDirty()) {
            sendUntagged("FLAGS (" + StringUtil.join(" ", i4folder.getFlagList(false)) + ')');
            i4folder.setTagsDirty(false);
        }
    } finally {
        mbox.unlock();
    }
    ReentrantLock lock = null;
    try {
        for (ImapMessage i4msg : i4set) {
            PrintStream result = new PrintStream(output, false, Charsets.UTF_8.name());
            try {
                result.print("* " + i4msg.sequence + " FETCH (");
                if (i4msg.isExpunged()) {
                    fetchStub(i4msg, i4folder, attributes, parts, fullMessage, result);
                    continue;
                }
                boolean markMessage = markRead && (i4msg.flags & Flag.BITMASK_UNREAD) != 0;
                boolean empty = true;
                ZimbraMailItem item = null;
                MimeMessage mm;
                if (!fullMessage.isEmpty() || (parts != null && !parts.isEmpty()) || (attributes & ~FETCH_FROM_CACHE) != 0) {
                    if (lock == null && LC.imap_throttle_fetch.booleanValue()) {
                        lock = commandThrottle.lock(credentials.getAccountId());
                    }
                    try {
                        String folderOwner = i4folder.getFolder().getFolderItemIdentifier().accountId;
                        ItemIdentifier iid = ItemIdentifier.fromAccountIdAndItemId((folderOwner != null) ? folderOwner : mbox.getAccountId(), i4msg.msgId);
                        item = mbox.getItemById(getContext(), iid, i4msg.getType().toCommon());
                    } catch (NoSuchItemException nsie) {
                        // just in case we're out of sync, force this message back into sync
                        i4folder.markMessageExpunged(i4msg);
                        fetchStub(i4msg, i4folder, attributes, parts, fullMessage, result);
                        continue;
                    }
                }
                if ((attributes & FETCH_UID) != 0) {
                    result.print((empty ? "" : " ") + "UID " + i4msg.imapUid);
                    empty = false;
                }
                if ((attributes & FETCH_INTERNALDATE) != 0) {
                    result.print((empty ? "" : " ") + "INTERNALDATE \"" + DateUtil.toImapDateTime(new Date(item.getDate())) + '"');
                    empty = false;
                }
                if ((attributes & FETCH_RFC822_SIZE) != 0) {
                    result.print((empty ? "" : " ") + "RFC822.SIZE " + i4msg.getSize(item));
                    empty = false;
                }
                if ((attributes & FETCH_BINARY_SIZE) != 0) {
                    result.print((empty ? "" : " ") + "BINARY.SIZE[] " + i4msg.getSize(item));
                    empty = false;
                }
                if (!fullMessage.isEmpty()) {
                    for (ImapPartSpecifier pspec : fullMessage) {
                        result.print(empty ? "" : " ");
                        pspec.write(result, output, item);
                        empty = false;
                    }
                }
                if ((parts != null && !parts.isEmpty()) || (attributes & FETCH_FROM_MIME) != 0) {
                    mm = ImapMessage.getMimeMessage(item);
                    if ((attributes & FETCH_BODY) != 0) {
                        result.print(empty ? "" : " ");
                        result.print("BODY ");
                        ImapMessage.serializeStructure(result, mm, false);
                        empty = false;
                    }
                    if ((attributes & FETCH_BODYSTRUCTURE) != 0) {
                        result.print(empty ? "" : " ");
                        result.print("BODYSTRUCTURE ");
                        ImapMessage.serializeStructure(result, mm, true);
                        empty = false;
                    }
                    if ((attributes & FETCH_ENVELOPE) != 0) {
                        result.print(empty ? "" : " ");
                        result.print("ENVELOPE ");
                        ImapMessage.serializeEnvelope(result, mm);
                        empty = false;
                    }
                    if (parts != null) {
                        for (ImapPartSpecifier pspec : parts) {
                            result.print(empty ? "" : " ");
                            pspec.write(result, output, mm);
                            empty = false;
                        }
                    }
                }
                // FIXME: optimize by doing a single mark-read op on multiple messages
                if (markMessage) {
                    String folderOwner = i4folder.getFolder().getFolderItemIdentifier().accountId;
                    ItemIdentifier iid = ItemIdentifier.fromAccountIdAndItemId((folderOwner != null) ? folderOwner : mbox.getAccountId(), i4msg.msgId);
                    mbox.flagItemAsRead(getContext(), iid, i4msg.getMailItemType());
                }
                ImapFolder.DirtyMessage unsolicited = i4folder.undirtyMessage(i4msg);
                if ((attributes & FETCH_FLAGS) != 0 || unsolicited != null) {
                    result.print(empty ? "" : " ");
                    result.print(i4msg.getFlags(i4folder));
                    empty = false;
                }
                // data items in all subsequent unsolicited FETCH responses."
                if ((attributes & FETCH_MODSEQ) != 0 || (modseqEnabled && unsolicited != null)) {
                    int modseq = unsolicited == null ? item.getModifiedSequence() : unsolicited.modseq;
                    result.print((empty ? "" : " ") + "MODSEQ (" + modseq + ')');
                    empty = false;
                }
            } catch (ImapPartSpecifier.BinaryDecodingException e) {
                // don't write this response line if we're returning NO
                result = null;
                throw new ImapParseException(tag, "UNKNOWN-CTE", command + "failed: unknown content-type-encoding", false);
            } catch (SoapFaultException e) {
                fetchException(e);
            } catch (ServiceException e) {
                Throwable cause = e.getCause();
                if (cause instanceof IOException) {
                    fetchException(cause);
                } else {
                    ZimbraLog.imap.warn("ignoring error during " + command + ": ", e);
                    continue;
                }
            } catch (MessagingException e) {
                ZimbraLog.imap.warn("ignoring error during " + command + ": ", e);
                continue;
            } catch (IOException ioe) {
                fetchException(ioe);
            } finally {
                if (result != null) {
                    result.write(')');
                    output.write(LINE_SEPARATOR_BYTES, 0, LINE_SEPARATOR_BYTES.length);
                    output.flush();
                }
            }
        }
    } finally {
        if (lock != null) {
            lock.unlock();
        }
    }
    if (standalone) {
        sendNotifications(byUID, false);
        sendOK(tag, command + " completed");
    }
    return true;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) ArrayList(java.util.ArrayList) ItemIdentifier(com.zimbra.common.mailbox.ItemIdentifier) MimeMessage(javax.mail.internet.MimeMessage) ZimbraMailItem(com.zimbra.common.mailbox.ZimbraMailItem) ImapMessageSet(com.zimbra.cs.imap.ImapMessage.ImapMessageSet) ReentrantLock(java.util.concurrent.locks.ReentrantLock) PrintStream(java.io.PrintStream) MessagingException(javax.mail.MessagingException) IOException(java.io.IOException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) Date(java.util.Date) SoapFaultException(com.zimbra.common.soap.SoapFaultException) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException)

Example 15 with ItemIdentifier

use of com.zimbra.common.mailbox.ItemIdentifier in project zm-mailbox by Zimbra.

the class ImapHandler method doCOPY.

/**
 * @param path of target folder
 */
protected 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 = "";
    ImapFolder i4folder = getSelectedFolder();
    if (i4folder == null) {
        throw new ImapSessionClosedException();
    }
    MailboxStore mbox = i4folder.getMailbox();
    Set<ImapMessage> i4set;
    mbox.lock(false);
    try {
        i4set = i4folder.getSubsequence(tag, sequenceSet, byUID);
    } catch (ImapParseException ipe) {
        ZimbraLog.imap.error(ipe);
        throw ipe;
    } finally {
        mbox.unlock();
    }
    if (i4set.size() > LC.imap_max_items_in_copy.intValue()) {
        sendNO(tag, "COPY rejected, too many items in copy request");
        return true;
    }
    // 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());
        }
        MailboxStore mbxStore = path.getOwnerMailbox();
        if (null == mbxStore) {
            throw AccountServiceException.NO_SUCH_ACCOUNT(path.getOwner());
        }
        FolderStore targetFolder = path.getFolder();
        FolderStore selectedFolder = null;
        try {
            selectedFolder = i4folder.getFolder();
        } catch (ServiceException e1) {
            ZimbraLog.imap.error("Problem with selected folder %s during doCOPY", e1.getMessage());
            return true;
        }
        // check target folder permissions before attempting the copy
        ImapMailboxStore selectedImapMboxStore = i4folder.getImapMailboxStore();
        boolean sameMailbox = selectedImapMboxStore.getAccountId().equalsIgnoreCase(mbxStore.getAccountId());
        boolean selectedFolderInOtherMailbox;
        ItemIdentifier fromFolderId;
        if (selectedFolder instanceof MountpointStore) {
            selectedFolderInOtherMailbox = true;
            fromFolderId = ((MountpointStore) selectedFolder).getTargetItemIdentifier();
        } else if (selectedFolder instanceof ZSharedFolder) {
            selectedFolderInOtherMailbox = true;
            fromFolderId = selectedFolder.getFolderItemIdentifier();
        } else {
            selectedFolderInOtherMailbox = false;
            fromFolderId = selectedFolder.getFolderItemIdentifier();
        }
        int uvv = targetFolder.getUIDValidity();
        ItemId iidTarget = new ItemId(targetFolder, path.getOwnerAccount().getId());
        ItemIdentifier targetIdentifier = iidTarget.toItemIdentifier();
        long checkpoint = System.currentTimeMillis();
        List<Integer> copyUIDs = extensionEnabled("UIDPLUS") ? Lists.newArrayListWithCapacity(i4set.size()) : null;
        final List<ImapMessage> i4list = Lists.newArrayList(i4set);
        final List<List<ImapMessage>> batches = Lists.partition(i4list, SUGGESTED_COPY_BATCH_SIZE);
        for (List<ImapMessage> batch : batches) {
            if (sameMailbox && !selectedFolderInOtherMailbox) {
                copyOwnItems(selectedImapMboxStore, batch, iidTarget, copyUIDs);
            } else {
                copyItemsBetweenMailboxes(selectedImapMboxStore, batch, fromFolderId, targetIdentifier, copyUIDs);
            }
            // 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 && copyUIDs != null && copyUIDs.size() > 0) {
            List<Integer> srcUIDs = Lists.newArrayListWithCapacity(i4set.size());
            for (ImapMessage i4msg : i4set) {
                srcUIDs.add(i4msg.imapUid);
            }
            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;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) SearchFolderStore(com.zimbra.common.mailbox.SearchFolderStore) FolderStore(com.zimbra.common.mailbox.FolderStore) ZSharedFolder(com.zimbra.client.ZSharedFolder) IOException(java.io.IOException) ItemId(com.zimbra.cs.service.util.ItemId) ItemIdentifier(com.zimbra.common.mailbox.ItemIdentifier) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) MountpointStore(com.zimbra.common.mailbox.MountpointStore) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList)

Aggregations

ItemIdentifier (com.zimbra.common.mailbox.ItemIdentifier)26 ZMailbox (com.zimbra.client.ZMailbox)12 Mailbox (com.zimbra.cs.mailbox.Mailbox)12 Test (org.junit.Test)11 ZMessage (com.zimbra.client.ZMessage)6 ServiceException (com.zimbra.common.service.ServiceException)6 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)6 ArrayList (java.util.ArrayList)6 ZMountpoint (com.zimbra.client.ZMountpoint)5 MailboxStore (com.zimbra.common.mailbox.MailboxStore)5 ImapMessage (com.zimbra.cs.imap.ImapMessage)5 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)5 Message (com.zimbra.cs.mailbox.Message)5 ZOutgoingMessage (com.zimbra.client.ZMailbox.ZOutgoingMessage)4 ZimbraMailItem (com.zimbra.common.mailbox.ZimbraMailItem)4 Folder (com.zimbra.cs.mailbox.Folder)4 ZContact (com.zimbra.client.ZContact)3 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 Contact (com.zimbra.cs.mailbox.Contact)3 OperationContext (com.zimbra.cs.mailbox.OperationContext)3