use of com.zimbra.common.mailbox.ZimbraQueryHitResults 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.ZimbraQueryHitResults in project zm-mailbox by Zimbra.
the class TestZClient method testImapSearch.
@Test
public void testImapSearch() throws Exception {
ZMailbox zmbox = TestUtil.getZMailbox(USER_NAME);
Mailbox mbox = TestUtil.getMailbox(USER_NAME);
ZTag tag = zmbox.createTag("testImapSearch tag", Color.blue);
int msgId = Integer.valueOf(TestUtil.addMessage(zmbox, "testImapSearch message"));
mbox.alterTag(null, msgId, Type.MESSAGE, FlagInfo.UNREAD, false, null);
mbox.alterTag(null, msgId, Type.MESSAGE, tag.getName(), true, null);
ZSearchParams params = new ZSearchParams("testImapSearch");
params.setMailItemTypes(Sets.newHashSet(MailItemType.MESSAGE));
params.setFetch(Fetch.all);
params.setZimbraFetchMode(ZimbraFetchMode.IMAP);
ZimbraQueryHitResults results = zmbox.searchImap(null, params);
Message msg = mbox.getMessageById(null, msgId);
verifyImapSearchResults(results, msgId, msg.getImapUid(), msg.getParentId(), msg.getModifiedSequence(), MailItemType.MESSAGE, msg.getFlagBitmask(), new String[] { tag.getId() }, true);
params.setZimbraFetchMode(ZimbraFetchMode.MODSEQ);
results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, msgId, -1, msg.getParentId(), msg.getModifiedSequence(), MailItemType.MESSAGE, msg.getFlagBitmask(), new String[] { tag.getId() }, true);
params.setZimbraFetchMode(ZimbraFetchMode.IDS);
results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, msgId, -1, -1, -1, null, -1, null, false);
// verify that setting the expandResult parameter also returns the necessary fields
params.setFetch(Fetch.all);
params.setZimbraFetchMode(ZimbraFetchMode.IMAP);
results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, msgId, msg.getImapUid(), msg.getParentId(), msg.getModifiedSequence(), MailItemType.MESSAGE, msg.getFlagBitmask(), new String[] { tag.getId() }, true);
}
use of com.zimbra.common.mailbox.ZimbraQueryHitResults in project zm-mailbox by Zimbra.
the class TestZClient method testImapSearchContact.
@Test
public void testImapSearchContact() throws Exception {
ZMailbox zmbox = TestUtil.getZMailbox(USER_NAME);
Mailbox mbox = TestUtil.getMailbox(USER_NAME);
ZTag tag = zmbox.createTag("testImapSearch tag", Color.blue);
ZFolder folder = TestUtil.createFolder(zmbox, "/testImapSearch", ZFolder.View.contact);
Contact contact = TestUtil.createContact(mbox, folder.getFolderIdInOwnerMailbox(), "testImapSearch@test.local");
int contactId = contact.getId();
mbox.alterTag(null, contactId, Type.CONTACT, FlagInfo.FLAGGED, true, null);
mbox.alterTag(null, contactId, Type.CONTACT, tag.getName(), true, null);
ZSearchParams params = new ZSearchParams("testImapSearch");
params.setMailItemTypes(Sets.newHashSet(MailItemType.CONTACT));
params.setZimbraFetchMode(ZimbraFetchMode.IMAP);
ZimbraQueryHitResults results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, contactId, contact.getImapUid(), contact.getParentId(), contact.getModifiedSequence(), MailItemType.CONTACT, contact.getFlagBitmask(), new String[] { tag.getId() }, true);
params.setZimbraFetchMode(ZimbraFetchMode.MODSEQ);
results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, contactId, -1, contact.getParentId(), contact.getModifiedSequence(), MailItemType.CONTACT, contact.getFlagBitmask(), new String[] { tag.getId() }, true);
params.setZimbraFetchMode(ZimbraFetchMode.IDS);
results = zmbox.searchImap(null, params);
verifyImapSearchResults(results, contactId, -1, -1, -1, null, -1, null, false);
}
use of com.zimbra.common.mailbox.ZimbraQueryHitResults 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.ZimbraQueryHitResults 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;
}
Aggregations