Search in sources :

Example 1 with ZimbraQueryHit

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

the class ImapHandler method doTHREAD.

private boolean doTHREAD(String tag, ImapSearch i4search, boolean byUID) 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 THREAD MODSEQ in this mailbox", true);
    }
    LinkedHashMap<Integer, List<ImapMessage>> threads = new LinkedHashMap<Integer, List<ImapMessage>>();
    try {
        // RFC 5256 3: "The searched messages are sorted by base subject and then
        // by the sent date.  The messages are then split into separate
        // threads, with each thread containing messages with the same
        // base subject text.  Finally, the threads are sorted by the
        // sent date of the first message in the thread."
        ZimbraQueryHitResults zqr = runSearch(i4search, i4folder, SortBy.DATE_ASC, SearchParams.Fetch.PARENT);
        try {
            for (ZimbraQueryHit hit = zqr.getNext(); hit != null; hit = zqr.getNext()) {
                ImapMessage i4msg = i4folder.getById(hit.getItemId());
                if (i4msg == null || i4msg.isExpunged()) {
                    continue;
                }
                int parentId = hit.getParentId();
                if (parentId <= 0) {
                    threads.put(-i4msg.msgId, Arrays.asList(i4msg));
                    continue;
                }
                List<ImapMessage> contents = threads.get(parentId);
                if (contents == null) {
                    (contents = new LinkedList<ImapMessage>()).add(i4msg);
                    threads.put(parentId, contents);
                } else {
                    contents.add(i4msg);
                }
            }
        } finally {
            zqr.close();
        }
    } catch (ServiceException e) {
        ZimbraLog.imap.warn("THREAD failed", e);
        sendNO(tag, "THREAD failed");
        return true;
    }
    StringBuilder result = new StringBuilder("THREAD");
    if (!threads.isEmpty()) {
        result.append(' ');
        for (List<ImapMessage> thread : threads.values()) {
            // ORDEREDSUBJECT: "(A)" for singletons, "(A B)" for pairs, "(A (B)(C)(D)(E))" for larger threads
            Iterator<ImapMessage> it = thread.iterator();
            result.append('(').append(getMessageId(it.next(), byUID));
            if (it.hasNext()) {
                result.append(' ');
                if (thread.size() == 2) {
                    result.append(getMessageId(it.next(), byUID));
                } else {
                    while (it.hasNext()) {
                        result.append('(').append(getMessageId(it.next(), byUID)).append(')');
                    }
                }
            }
            result.append(')');
        }
    }
    sendUntagged(result.toString());
    sendNotifications(false, false);
    sendOK(tag, (byUID ? "UID " : "") + "THREAD completed");
    return true;
}
Also used : ZimbraQueryHit(com.zimbra.common.mailbox.ZimbraQueryHit) LinkedHashMap(java.util.LinkedHashMap) ZimbraQueryHitResults(com.zimbra.common.mailbox.ZimbraQueryHitResults) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList)

Example 2 with ZimbraQueryHit

use of com.zimbra.common.mailbox.ZimbraQueryHit 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 3 with ZimbraQueryHit

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

the class ImapSessionManager method loadVirtualFolder.

/**
 * Fetches the messages contained within a search folder.  When a search folder is IMAP-visible, it appears in
 * folder listings, is SELECTable READ-ONLY, and appears to have all matching messages as its contents.
 * If it is not visible, it will be completely hidden from all IMAP commands.
 * @param octxt   Encapsulation of the authenticated user.
 * @param search  The search folder being exposed.
 */
private static List<ImapMessage> loadVirtualFolder(OperationContext octxt, SearchFolderStore search) throws ServiceException {
    List<ImapMessage> i4list = Lists.newArrayList();
    Set<MailItemType> types = ImapFolder.getMailItemTypeConstraint(search);
    if (types.isEmpty()) {
        return i4list;
    }
    MailboxStore mbox = search.getMailboxStore();
    ZimbraSearchParams params = mbox.createSearchParams(search.getQuery());
    params.setIncludeTagDeleted(true);
    params.setMailItemTypes(types);
    params.setZimbraSortBy(ZimbraSortBy.dateAsc);
    params.setLimit(1000);
    params.setZimbraFetchMode(ZimbraFetchMode.IMAP);
    try {
        ZimbraQueryHitResults zqr = mbox.searchImap(octxt, params);
        try {
            for (ZimbraQueryHit hit = zqr.getNext(); hit != null; hit = zqr.getNext()) {
                i4list.add(new ImapMessage(hit));
            }
        } finally {
            zqr.close();
        }
    } catch (ServiceException e) {
        throw e;
    } catch (Exception e) {
        throw ServiceException.FAILURE("failure opening search folder", e);
    }
    return i4list;
}
Also used : ZimbraQueryHitResults(com.zimbra.common.mailbox.ZimbraQueryHitResults) MailboxStore(com.zimbra.common.mailbox.MailboxStore) ZimbraSearchParams(com.zimbra.common.mailbox.ZimbraSearchParams) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) ZimbraQueryHit(com.zimbra.common.mailbox.ZimbraQueryHit) MailItemType(com.zimbra.common.mailbox.MailItemType) ServiceException(com.zimbra.common.service.ServiceException) MailboxInMaintenanceException(com.zimbra.cs.mailbox.MailServiceException.MailboxInMaintenanceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException)

Example 4 with ZimbraQueryHit

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

the class TestZClient method searchImapWithCursor.

@Test(timeout = 50000)
public void searchImapWithCursor() throws Exception {
    ZMailbox zmbox = TestUtil.getZMailbox(USER_NAME);
    assertNotNull("ZMailbox", zmbox);
    Mailbox mbox = TestUtil.getMailbox(USER_NAME);
    int numMsgs = 400;
    for (int i = 0; i < numMsgs; i++) {
        TestUtil.addMessage(mbox, Mailbox.ID_FOLDER_INBOX, "same subject");
    }
    // A simple in:INBOX search worked fine but this complex search doesn't
    ZimbraSearchParams params = zmbox.createSearchParams("in:\"INBOX\" (item:{266--399} -tag:\\Deleted)");
    params.setIncludeTagDeleted(true);
    params.setMailItemTypes(MailItem.Type.toCommon(ImapMessage.SUPPORTED_TYPES));
    params.setZimbraSortBy(ZimbraSortBy.dateAsc);
    params.setLimit(10);
    /* Small sized window */
    params.setPrefetch(false);
    params.setZimbraFetchMode(ZimbraFetchMode.IDS);
    int numHits = 0;
    try (ZimbraQueryHitResults zqr = zmbox.searchImap((OpContext) null, params)) {
        for (ZimbraQueryHit hit = zqr.getNext(); hit != null; hit = zqr.getNext()) {
            numHits++;
        }
    } catch (Exception e) {
        throw ServiceException.FAILURE("failure opening search folder", e);
    }
    assertEquals("Number of hits", 399 - 266 + 1, numHits);
}
Also used : ZimbraQueryHitResults(com.zimbra.common.mailbox.ZimbraQueryHitResults) ZMailbox(com.zimbra.client.ZMailbox) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZMailbox(com.zimbra.client.ZMailbox) ZimbraSearchParams(com.zimbra.common.mailbox.ZimbraSearchParams) ZimbraQueryHit(com.zimbra.common.mailbox.ZimbraQueryHit) ZMountpoint(com.zimbra.client.ZMountpoint) SoapFaultException(com.zimbra.common.soap.SoapFaultException) ServiceException(com.zimbra.common.service.ServiceException) IOException(java.io.IOException) AuthTokenException(com.zimbra.cs.account.AuthTokenException) ZClientException(com.zimbra.common.zclient.ZClientException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) MessagingException(javax.mail.MessagingException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) Test(org.junit.Test)

Example 5 with ZimbraQueryHit

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

the class TestZClient method verifyImapSearchResults.

private void verifyImapSearchResults(ZimbraQueryHitResults results, int id, int imapUid, int parentId, int modSeq, MailItemType type, int flags, String[] tags, boolean nonZeroModSeq) throws ServiceException {
    ZimbraQueryHit hit = results.getNext();
    assertNotNull("ImapSearchResults hit should not be null", hit);
    assertEquals("Expected=Id Actual=hit.getItemId()", id, hit.getItemId());
    assertEquals("Expected=parentId Actual=hit.getParentId()", parentId, hit.getParentId());
    assertEquals("Expected=imapUid Actual=hit.getImapUid()", imapUid, hit.getImapUid());
    assertEquals("Expected=modSeq Actual=hit.getModifiedSequence()", modSeq, hit.getModifiedSequence());
    assertEquals("Expected=type Actual=hit.getMailItemType()", type, hit.getMailItemType());
    assertEquals("Expected=flags Actual=hit.getFlagBitmask()", flags, hit.getFlagBitmask());
    assertArrayEquals("Expected=tags Actual=hit.getTags()", tags, hit.getTags());
    if (nonZeroModSeq) {
        assertTrue(String.format("Modified sequence %s should be > 0", modSeq), modSeq > 0);
    }
}
Also used : ZimbraQueryHit(com.zimbra.common.mailbox.ZimbraQueryHit)

Aggregations

ZimbraQueryHit (com.zimbra.common.mailbox.ZimbraQueryHit)5 ZimbraQueryHitResults (com.zimbra.common.mailbox.ZimbraQueryHitResults)4 ServiceException (com.zimbra.common.service.ServiceException)4 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)4 MailboxStore (com.zimbra.common.mailbox.MailboxStore)2 ZimbraSearchParams (com.zimbra.common.mailbox.ZimbraSearchParams)2 AccountServiceException (com.zimbra.cs.account.AccountServiceException)2 Mailbox (com.zimbra.cs.mailbox.Mailbox)2 ArrayList (java.util.ArrayList)2 ZMailbox (com.zimbra.client.ZMailbox)1 ZMountpoint (com.zimbra.client.ZMountpoint)1 MailItemType (com.zimbra.common.mailbox.MailItemType)1 SoapFaultException (com.zimbra.common.soap.SoapFaultException)1 ZClientException (com.zimbra.common.zclient.ZClientException)1 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)1 AuthTokenException (com.zimbra.cs.account.AuthTokenException)1 ImapMessageSet (com.zimbra.cs.imap.ImapMessage.ImapMessageSet)1 SortBy (com.zimbra.cs.index.SortBy)1 MailboxInMaintenanceException (com.zimbra.cs.mailbox.MailServiceException.MailboxInMaintenanceException)1 IOException (java.io.IOException)1