Search in sources :

Example 16 with MailboxStore

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

the class ImapSessionManager method cacheKey.

/**
 * Generates a cache key for the {@link ImapListener}.
 *
 * @param session IMAP session
 * @param active true to use active session cache, otherwise inactive session cache.
 * @return cache key
 */
protected String cacheKey(ImapListener session, boolean active) throws ServiceException {
    MailboxStore mbox = session.getMailbox();
    FolderStore fstore;
    if (mbox == null) {
        if (session instanceof ImapSession) {
            mbox = MailboxManager.getInstance().getMailboxByAccountId(session.getTargetAccountId());
        } else {
            ImapMailboxStore imapStore = session.mPath.getOwnerImapMailboxStore(true);
            mbox = imapStore.getMailboxStore();
        }
    }
    if (session instanceof ImapSession) {
        fstore = mbox.getFolderById((OpContext) null, session.getFolderItemIdentifier().toString());
    } else {
        if (session.getAuthenticatedAccountId() == session.getTargetAccountId()) {
            fstore = mbox.getFolderById((OpContext) null, session.getFolderItemIdentifier().toString());
        } else {
            fstore = ((ZMailbox) mbox).getSharedFolderById(session.getFolderItemIdentifier().toString());
        }
    }
    String cachekey = cacheKey(fstore, active);
    // ('+' is a good separator because it alpha-sorts before the '.' of the filename extension)
    return session.hasExpunges() ? cachekey + "+" + session.getQualifiedSessionId() : cachekey;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) FolderStore(com.zimbra.common.mailbox.FolderStore) SearchFolderStore(com.zimbra.common.mailbox.SearchFolderStore) OpContext(com.zimbra.common.mailbox.OpContext)

Example 17 with MailboxStore

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

the class SharedImapTests method acctIdForFolder.

private String acctIdForFolder(FolderStore folder) {
    MailboxStore mbox = folder.getMailboxStore();
    String acctId;
    try {
        acctId = mbox.getAccountId();
    } catch (ServiceException e) {
        acctId = "<unknown>";
    }
    ZimbraLog.test.debug("Account ID = %s for folder %s", acctId, folder);
    return acctId;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) ServiceException(com.zimbra.common.service.ServiceException)

Example 18 with MailboxStore

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

the class Mailbox method addListener.

/**
 * Adds a {@link Session} to the set of listeners notified on Mailbox
 *  changes.
 *
 * @param session  The Session registering for notifications.
 * @throws ServiceException  If the mailbox is in maintenance mode.
 */
public void addListener(Session session) throws ServiceException {
    if (session == null) {
        return;
    }
    assert (session.getSessionId() != null);
    lock.lock();
    try {
        if (maintenance != null) {
            throw MailServiceException.MAINTENANCE(mId);
        }
        if (!mListeners.contains(session)) {
            mListeners.add(session);
        }
        if (Zimbra.isAlwaysOn()) {
            if (mListeners.size() == 1) {
                // insert session into DB
                DbConnection conn = DbPool.getConnection();
                try {
                    MailboxStore sessMbox = session.getMailbox();
                    if (sessMbox instanceof Mailbox) {
                        DbSession.create(conn, ((Mailbox) sessMbox).getId(), Provisioning.getInstance().getLocalServer().getId());
                    } else {
                        throw new UnsupportedOperationException(String.format("Operation not supported for non-Mailbox MailboxStore", sessMbox == null ? "null" : sessMbox.getClass().getName()));
                    }
                    conn.commit();
                } catch (ServiceException e) {
                    ZimbraLog.session.info("exception while inserting session into DB", e);
                } finally {
                    if (conn != null) {
                        conn.closeQuietly();
                    }
                }
            // 
            }
        }
    } finally {
        lock.release();
    }
    ZimbraLog.mailbox.debug("adding listener: %s", session);
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) RenameMailbox(com.zimbra.cs.redolog.op.RenameMailbox) DbMailbox(com.zimbra.cs.db.DbMailbox) CreateMailbox(com.zimbra.cs.redolog.op.CreateMailbox) ZMailbox(com.zimbra.client.ZMailbox) DeleteMailbox(com.zimbra.cs.redolog.op.DeleteMailbox) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) DbConnection(com.zimbra.cs.db.DbPool.DbConnection)

Example 19 with MailboxStore

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

the class ImapHandler method search.

private boolean search(String tag, String command, ImapSearch i4search, boolean byUID, Integer options, List<SortBy> order) throws IOException, ImapException {
    if (!checkState(tag, State.SELECTED)) {
        return true;
    }
    ImapFolder i4folder = getSelectedFolder();
    if (i4folder == null) {
        throw new ImapSessionClosedException();
    }
    boolean requiresMODSEQ = i4search.requiresMODSEQ();
    if (requiresMODSEQ) {
        activateExtension(ImapExtension.CONDSTORE);
    }
    // MUST reject any such command with the tagged BAD response."
    if (requiresMODSEQ && !sessionActivated(ImapExtension.CONDSTORE)) {
        throw new ImapParseException(tag, "NOMODSEQ", "cannot SEARCH MODSEQ in this mailbox", true);
    }
    // only supporting one level of sorting sort at this point
    SortBy sort = SortBy.NONE;
    if (order != null && !order.isEmpty()) {
        for (SortBy level : order) {
            if ((sort = level) != SortBy.NONE) {
                break;
            }
        }
    }
    boolean saveResults = (options != null && (options & RETURN_SAVE) != 0);
    boolean unsorted = sort == SortBy.NONE;
    Collection<ImapMessage> hits;
    int modseq = 0;
    try {
        MailboxStore mboxStore = i4folder.getMailbox();
        // TODO any way this can be optimized for non-Mailbox MailboxStore?
        if (unsorted && (mboxStore instanceof Mailbox) && i4search.canBeRunLocally()) {
            mboxStore.lock(false);
            try {
                hits = i4search.evaluate(i4folder);
                hits.remove(null);
            } finally {
                mboxStore.unlock();
            }
        } else {
            hits = unsorted ? new ImapMessageSet() : new ArrayList<ImapMessage>();
            try (ZimbraQueryHitResults zqr = runSearch(i4search, i4folder, sort, requiresMODSEQ ? SearchParams.Fetch.MODSEQ : SearchParams.Fetch.IDS)) {
                for (ZimbraQueryHit hit = zqr.getNext(); hit != null; hit = zqr.getNext()) {
                    ImapMessage i4msg = i4folder.getById(hit.getItemId());
                    if (i4msg == null || i4msg.isExpunged()) {
                        continue;
                    }
                    hits.add(i4msg);
                    if (requiresMODSEQ)
                        modseq = Math.max(modseq, hit.getModifiedSequence());
                }
            }
        }
    } catch (ServiceException e) {
        // variable to the empty sequence."
        if (saveResults) {
            i4folder.saveSearchResults(new ImapMessageSet());
        }
        ZimbraLog.imap.warn("%s failed", command, e);
        sendNO(tag, command + " failed");
        return true;
    }
    int size = hits.size();
    ImapMessage first = null;
    ImapMessage last = null;
    if (size != 0 && options != null && (options & (RETURN_MIN | RETURN_MAX)) != 0) {
        if (unsorted) {
            first = ((ImapMessageSet) hits).first();
            last = ((ImapMessageSet) hits).last();
        } else {
            first = ((List<ImapMessage>) hits).get(0);
            last = ((List<ImapMessage>) hits).get(size - 1);
        }
    }
    StringBuilder result = null;
    if (options == null) {
        result = new StringBuilder(command);
        for (ImapMessage i4msg : hits) {
            result.append(' ').append(getMessageId(i4msg, byUID));
        }
    } else if (options != RETURN_SAVE) {
        // Note: rfc5267's ESORT reuses the ESEARCH response i.e. response result starts with "ESEARCH" NOT "ESORT"
        // This is slightly inconsistent as rfc5256's SORT response result starts with "SORT"...
        result = new StringBuilder("ESEARCH (TAG \"").append(tag).append("\")");
        if (byUID) {
            result.append(" UID");
        }
        if (first != null && (options & RETURN_MIN) != 0) {
            result.append(" MIN ").append(getMessageId(first, byUID));
        }
        if (last != null && (options & RETURN_MAX) != 0) {
            result.append(" MAX ").append(getMessageId(last, byUID));
        }
        if ((options & RETURN_COUNT) != 0) {
            result.append(" COUNT ").append(size);
        }
        if (size != 0 && (options & RETURN_ALL) != 0) {
            result.append(" ALL ").append(ImapFolder.encodeSubsequence(hits, byUID));
        }
    }
    if (modseq > 0 && result != null) {
        result.append(" (MODSEQ ").append(modseq).append(')');
    }
    if (saveResults) {
        if (size == 0 || options == RETURN_SAVE || (options & (RETURN_COUNT | RETURN_ALL)) != 0) {
            i4folder.saveSearchResults(unsorted ? (ImapMessageSet) hits : new ImapMessageSet(hits));
        } else {
            ImapMessageSet saved = new ImapMessageSet();
            if (first != null && (options & RETURN_MIN) != 0) {
                saved.add(first);
            }
            if (last != null && (options & RETURN_MAX) != 0) {
                saved.add(last);
            }
            i4folder.saveSearchResults(saved);
        }
    }
    if (result != null) {
        sendUntagged(result.toString());
    }
    sendNotifications(byUID, false);
    sendOK(tag, (byUID ? "UID " : "") + command + " completed");
    return true;
}
Also used : MailboxStore(com.zimbra.common.mailbox.MailboxStore) SortBy(com.zimbra.cs.index.SortBy) ZimbraQueryHit(com.zimbra.common.mailbox.ZimbraQueryHit) ArrayList(java.util.ArrayList) ZimbraQueryHitResults(com.zimbra.common.mailbox.ZimbraQueryHitResults) Mailbox(com.zimbra.cs.mailbox.Mailbox) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) ImapMessageSet(com.zimbra.cs.imap.ImapMessage.ImapMessageSet)

Example 20 with MailboxStore

use of com.zimbra.common.mailbox.MailboxStore 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)

Aggregations

MailboxStore (com.zimbra.common.mailbox.MailboxStore)34 ServiceException (com.zimbra.common.service.ServiceException)22 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)17 AccountServiceException (com.zimbra.cs.account.AccountServiceException)14 SearchFolderStore (com.zimbra.common.mailbox.SearchFolderStore)10 FolderStore (com.zimbra.common.mailbox.FolderStore)9 ArrayList (java.util.ArrayList)6 ItemIdentifier (com.zimbra.common.mailbox.ItemIdentifier)5 Account (com.zimbra.cs.account.Account)4 ZimbraMailItem (com.zimbra.common.mailbox.ZimbraMailItem)3 ImapMessageSet (com.zimbra.cs.imap.ImapMessage.ImapMessageSet)3 Mailbox (com.zimbra.cs.mailbox.Mailbox)3 IOException (java.io.IOException)3 ZMailbox (com.zimbra.client.ZMailbox)2 ZSharedFolder (com.zimbra.client.ZSharedFolder)2 MountpointStore (com.zimbra.common.mailbox.MountpointStore)2 ZimbraQueryHit (com.zimbra.common.mailbox.ZimbraQueryHit)2 ZimbraQueryHitResults (com.zimbra.common.mailbox.ZimbraQueryHitResults)2 ZimbraSearchParams (com.zimbra.common.mailbox.ZimbraSearchParams)2 GuestAccount (com.zimbra.cs.account.GuestAccount)2