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;
}
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;
}
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;
}
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);
}
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);
}
}
Aggregations