use of com.zimbra.cs.index.SortBy in project zm-mailbox by Zimbra.
the class MailboxIndex method search.
private ZimbraQueryResults search(ZimbraQuery zq) throws ServiceException {
SearchParams params = zq.getParams();
ZimbraLog.search.debug("query: %s", params.getQueryString());
ZimbraLog.searchstat.debug("query: %s", zq.toSanitizedtring());
// handle special-case Task-only sorts: convert them to a "normal sort" and then re-sort them at the end
// TODO: this hack (converting the sort) should be able to go away w/ the new SortBy implementation, if the
// lower-level code was modified to use the SortBy.Criterion and SortBy.Direction data (instead of switching on
// the SortBy itself). We still will need this switch so that we can wrap the results in ReSortingQueryResults.
boolean isTaskSort = false;
boolean isLocalizedSort = false;
SortBy originalSort = params.getSortBy();
switch(originalSort) {
case TASK_DUE_ASC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case TASK_DUE_DESC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case TASK_STATUS_ASC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case TASK_STATUS_DESC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case TASK_PERCENT_COMPLETE_ASC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case TASK_PERCENT_COMPLETE_DESC:
isTaskSort = true;
params.setSortBy(SortBy.DATE_DESC);
break;
case NAME_LOCALIZED_ASC:
case NAME_LOCALIZED_DESC:
isLocalizedSort = true;
break;
}
ZimbraQueryResults results = zq.execute();
if (isTaskSort) {
results = new ReSortingQueryResults(results, originalSort, null);
}
if (isLocalizedSort) {
results = new ReSortingQueryResults(results, originalSort, params);
}
return results;
}
use of com.zimbra.cs.index.SortBy in project zm-mailbox by Zimbra.
the class Search method getFolderIdListIfSimpleAppointmentsQuery.
// Calendar summary cache stuff
/**
* Returns list of folder id string if the query is a simple appointments query. Otherwise returns null.
*
* @param params search parameters
* @param zsc not used, may be used in subclass
* @throws ServiceException subclass may throw
*/
protected List<String> getFolderIdListIfSimpleAppointmentsQuery(SearchParams params, ZimbraSoapContext zsc) throws ServiceException {
// types = "appointment"
Set<MailItem.Type> types = params.getTypes();
if (types.size() != 1) {
return null;
}
MailItem.Type type = Iterables.getOnlyElement(types);
if (type != MailItem.Type.APPOINTMENT && type != MailItem.Type.TASK) {
return null;
}
// has time range
if (params.getCalItemExpandStart() == -1 || params.getCalItemExpandEnd() == -1) {
return null;
}
// offset = 0
if (params.getOffset() != 0) {
return null;
}
// sortBy = "none"
SortBy sortBy = params.getSortBy();
if (sortBy != null && !sortBy.equals(SortBy.NONE)) {
return null;
}
// query string is "inid:<folder> [OR inid:<folder>]*"
String queryString = Strings.nullToEmpty(params.getQueryString());
queryString = queryString.toLowerCase();
queryString = removeOuterParens(queryString);
// simple appointment query can't have any ANDed terms
if (queryString.contains("and")) {
return null;
}
String[] terms = queryString.split("\\s+or\\s+");
List<String> folderIdStrs = new ArrayList<String>();
for (String term : terms) {
term = term.trim();
// remove outermost parentheses (light client does this, e.g. "(inid:10)")
term = removeOuterParens(term);
if (!term.startsWith("inid:")) {
return null;
}
// everything after "inid:"
String folderId = term.substring(5);
// e.g. if query is: inid:"account-id:num", we want just account-id:num
folderId = unquote(folderId);
if (folderId.length() > 0) {
folderIdStrs.add(folderId);
}
}
return folderIdStrs;
}
use of com.zimbra.cs.index.SortBy in project zm-mailbox by Zimbra.
the class GetContacts method handle.
@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Mailbox mbox = getRequestedMailbox(zsc);
OperationContext octxt = getOperationContext(zsc, context);
ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
boolean sync = request.getAttributeBool(MailConstants.A_SYNC, false);
boolean derefContactGroupMember = request.getAttributeBool(MailConstants.A_DEREF_CONTACT_GROUP_MEMBER, false);
String folderIdStr = request.getAttribute(MailConstants.A_FOLDER, null);
int folderId = ALL_FOLDERS;
if (folderIdStr != null) {
ItemId iidFolder = new ItemId(folderIdStr, zsc);
if (iidFolder.belongsTo(mbox))
folderId = iidFolder.getId();
else
throw ServiceException.FAILURE("Got remote folderId: " + folderIdStr + " but did not proxy", null);
}
SortBy sort = SortBy.of(request.getAttribute(MailConstants.A_SORTBY, null));
if (sort == null) {
sort = SortBy.NONE;
}
ArrayList<String> attrs = null;
ArrayList<String> memberAttrs = null;
ArrayList<ItemId> ids = null;
for (Element e : request.listElements()) {
if (e.getName().equals(MailConstants.E_ATTRIBUTE)) {
String name = e.getAttribute(MailConstants.A_ATTRIBUTE_NAME);
if (attrs == null)
attrs = new ArrayList<String>();
attrs.add(name);
} else if (e.getName().equals(MailConstants.E_CONTACT_GROUP_MEMBER_ATTRIBUTE)) {
String name = e.getAttribute(MailConstants.A_ATTRIBUTE_NAME);
if (memberAttrs == null)
memberAttrs = new ArrayList<String>();
memberAttrs.add(name);
} else if (e.getName().equals(MailConstants.E_CONTACT)) {
String idStr = e.getAttribute(MailConstants.A_ID);
String[] targets = idStr.split(",");
for (String target : targets) {
ItemId iid = new ItemId(target, zsc);
if (ids == null)
ids = new ArrayList<ItemId>();
ids.add(iid);
}
// remove it from the request, so we can re-use the request for proxying below
e.detach();
}
}
long maxMembers = DEFAULT_MAX_MEMBERS;
boolean returnHiddenAttrs = false;
if (attrs == null) {
returnHiddenAttrs = request.getAttributeBool(MailConstants.A_RETURN_HIDDEN_ATTRS, false);
maxMembers = request.getAttributeLong(MailConstants.A_MAX_MEMBERS, DEFAULT_MAX_MEMBERS);
}
boolean returnCertInfo = request.getAttributeBool(MailConstants.A_RETURN_CERT_INFO, false);
Element response = zsc.createElement(MailConstants.GET_CONTACTS_RESPONSE);
// want to return modified date only on sync-related requests
int fields = ToXML.NOTIFY_FIELDS;
if (sync) {
fields |= Change.CONFLICT;
}
// for perf reason, derefContactGroupMember is not supported in this mode
if (derefContactGroupMember) {
if (ids == null) {
throw ServiceException.INVALID_REQUEST(MailConstants.A_DEREF_CONTACT_GROUP_MEMBER + " is supported only when specific contact ids are specified", null);
}
}
if (ids != null) {
ArrayList<Integer> local = new ArrayList<Integer>();
HashMap<String, StringBuffer> remote = new HashMap<String, StringBuffer>();
partitionItems(zsc, ids, local, remote);
if (remote.size() > 0) {
if (folderId > 0)
throw ServiceException.INVALID_REQUEST("Cannot specify a folder with mixed-mailbox items", null);
List<Element> responses = proxyRemote(request, remote, context);
for (Element e : responses) response.addElement(e);
}
if (local.size() > 0) {
boolean migrateDlist = CreateContact.needToMigrateDlist(zsc);
for (int id : local) {
Contact con = mbox.getContactById(octxt, id);
if (con != null && (folderId == ALL_FOLDERS || folderId == con.getFolderId())) {
ContactGroup contactGroup = null;
String migratedDlist = null;
if (migrateDlist) {
ContactGroup cg = ContactGroup.init(con, false);
if (cg != null) {
migratedDlist = cg.migrateToDlist(con.getMailbox(), octxt);
}
} else if (derefContactGroupMember) {
contactGroup = ContactGroup.init(con, false);
if (contactGroup != null) {
contactGroup.derefAllMembers(con.getMailbox(), octxt, zsc.getResponseProtocol());
}
}
ToXML.encodeContact(response, ifmt, octxt, con, contactGroup, memberAttrs, false, attrs, fields, migratedDlist, returnHiddenAttrs, maxMembers, returnCertInfo);
}
}
}
} else {
for (Contact con : mbox.getContactList(octxt, folderId, sort)) {
if (con != null) {
ToXML.encodeContact(response, ifmt, octxt, con, null, null, false, attrs, fields, null, returnHiddenAttrs, maxMembers, returnCertInfo);
}
}
}
return response;
}
use of com.zimbra.cs.index.SortBy in project zm-mailbox by Zimbra.
the class ImapHandler method executeRequest.
boolean executeRequest(ImapRequest req) throws IOException, ImapException {
boolean isProxied = imapProxy != null;
if (getCredentials() != null) {
if (reqThrottle.isAccountThrottled(getCredentials().getAccountId(), getOrigRemoteIp(), getRemoteIp())) {
ZimbraLog.imap.warn("too many IMAP requests from account %s dropping connection", getCredentials().getAccountId());
throw new ImapThrottledException("too many requests for acct");
}
}
if (reqThrottle.isIpThrottled(getOrigRemoteIp())) {
ZimbraLog.imap.warn("too many IMAP requests from original remote ip %s dropping connection", getOrigRemoteIp());
throw new ImapThrottledException("too many requests from original ip");
} else if (reqThrottle.isIpThrottled(getRemoteIp())) {
ZimbraLog.imap.warn("too many IMAP requests from remote ip %s dropping connection", getRemoteIp());
throw new ImapThrottledException("too many requests from remote ip");
}
if (isIdle()) {
boolean clean = false;
try {
clean = req.readATOM().equals("DONE") && req.eof();
} catch (ImapParseException ipe) {
}
return doIDLE(null, IDLE_STOP, clean, req);
}
String tag = req.readTag();
boolean byUID = false;
req.skipSpace();
String command = lastCommand = req.readATOM();
do {
if (!THROTTLED_COMMANDS.contains(command)) {
//we received a command that isn't throttle-aware; reset throttle counter for next pass
commandThrottle.reset();
}
switch(command.charAt(0)) {
case 'A':
if (command.equals("AUTHENTICATE")) {
req.skipSpace();
String mechanism = req.readATOM();
byte[] response = null;
if (req.peekChar() == ' ' && extensionEnabled("SASL-IR")) {
req.skipSpace();
response = req.readBase64(true);
}
checkEOF(tag, req);
return doAUTHENTICATE(tag, mechanism, response);
} else if (command.equals("APPEND")) {
List<AppendMessage> appends = new ArrayList<AppendMessage>(1);
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
do {
req.skipSpace();
appends.add(AppendMessage.parse(this, tag, req));
} while (!req.eof() && extensionEnabled("MULTIAPPEND"));
checkEOF(tag, req);
return doAPPEND(tag, path, appends);
}
break;
case 'C':
if (command.equals("CAPABILITY")) {
checkEOF(tag, req);
return doCAPABILITY(tag);
} else if (command.equals("COPY")) {
req.skipSpace();
String sequence = req.readSequence();
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doCOPY(tag, sequence, path, byUID);
} else if (command.equals("CLOSE")) {
checkEOF(tag, req);
return doCLOSE(tag);
} else if (command.equals("CREATE")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doCREATE(tag, path);
} else if (command.equals("CHECK")) {
checkEOF(tag, req);
return doCHECK(tag);
}
break;
case 'D':
if (command.equals("DELETE")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials, ImapPath.Scope.NAME);
checkEOF(tag, req);
return doDELETE(tag, path);
} else if (command.equals("DELETEACL") && extensionEnabled("ACL")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
req.skipSpace();
String principal = req.readAstring();
checkEOF(tag, req);
return doDELETEACL(tag, path, principal);
}
break;
case 'E':
if (command.equals("EXPUNGE")) {
String sequence = null;
if (byUID) {
req.skipSpace();
sequence = req.readSequence();
}
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doEXPUNGE(tag, byUID, sequence);
} else if (command.equals("EXAMINE")) {
byte params = 0;
QResyncInfo qri = null;
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
if (req.peekChar() == ' ') {
req.skipSpace();
req.skipChar('(');
while (req.peekChar() != ')') {
if (params != 0) {
req.skipSpace();
}
String param = req.readATOM();
if (param.equals("CONDSTORE") && extensionEnabled("CONDSTORE")) {
params |= ImapFolder.SELECT_CONDSTORE;
} else if (param.equals("QRESYNC") && sessionActivated(ImapExtension.QRESYNC)) {
params |= ImapFolder.SELECT_CONDSTORE;
req.skipSpace();
qri = parseQResyncInfo(req);
} else {
throw new ImapParseException(tag, "unknown EXAMINE parameter \"" + param + '"');
}
}
req.skipChar(')');
}
checkEOF(tag, req);
return doEXAMINE(tag, path, params, qri);
} else if (command.equals("ENABLE") && extensionEnabled("ENABLE")) {
List<String> extensions = new ArrayList<String>();
do {
req.skipSpace();
extensions.add(req.readATOM());
} while (!req.eof());
checkEOF(tag, req);
return doENABLE(tag, extensions);
}
break;
case 'F':
if (command.equals("FETCH")) {
List<ImapPartSpecifier> parts = new ArrayList<ImapPartSpecifier>();
int modseq = -1;
req.skipSpace();
String sequence = req.readSequence();
req.skipSpace();
int attributes = req.readFetch(parts);
if (req.peekChar() == ' ') {
boolean first = true;
req.skipSpace();
req.skipChar('(');
while (req.peekChar() != ')') {
if (!first) {
req.skipSpace();
}
String modifier = req.readATOM();
if (modifier.equals("CHANGEDSINCE") && extensionEnabled("CONDSTORE")) {
req.skipSpace();
modseq = req.parseInteger(req.readNumber(ImapRequest.ZERO_OK));
} else if (modifier.equals("VANISHED") && byUID && sessionActivated(ImapExtension.QRESYNC)) {
attributes |= FETCH_VANISHED;
} else {
throw new ImapParseException(tag, "bad FETCH modifier: " + modifier);
}
first = false;
}
req.skipChar(')');
}
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doFETCH(tag, sequence, attributes, parts, byUID, modseq);
}
break;
case 'G':
if (command.equals("GETQUOTA") && extensionEnabled("QUOTA")) {
req.skipSpace();
ImapPath qroot = new ImapPath(req.readAstring(), credentials);
checkEOF(tag, req);
return doGETQUOTA(tag, qroot);
} else if (command.equals("GETACL") && extensionEnabled("ACL")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doGETACL(tag, path);
} else if (command.equals("GETQUOTAROOT") && extensionEnabled("QUOTA")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doGETQUOTAROOT(tag, path);
}
break;
case 'I':
if (command.equals("ID") && extensionEnabled("ID")) {
req.skipSpace();
Map<String, String> params = req.readParameters(true);
checkEOF(tag, req);
return doID(tag, params);
} else if (command.equals("IDLE") && extensionEnabled("IDLE")) {
checkEOF(tag, req);
return doIDLE(tag, IDLE_START, true, req);
}
break;
case 'L':
if (command.equals("LOGIN")) {
req.skipSpace();
String user = req.readAstring();
req.skipSpace();
String pass = req.readAstring();
checkEOF(tag, req);
return doLOGIN(tag, user, pass);
} else if (command.equals("LOGOUT")) {
checkEOF(tag, req);
return doLOGOUT(tag);
} else if (command.equals("LIST")) {
Set<String> patterns = new LinkedHashSet<String>(2);
boolean parenthesized = false;
byte selectOptions = 0, returnOptions = 0, status = 0;
req.skipSpace();
if (req.peekChar() == '(' && extensionEnabled("LIST-EXTENDED")) {
req.skipChar('(');
while (req.peekChar() != ')') {
if (selectOptions != 0) {
req.skipSpace();
}
String option = req.readATOM();
if (option.equals("RECURSIVEMATCH")) {
selectOptions |= SELECT_RECURSIVE;
} else if (option.equals("SUBSCRIBED")) {
selectOptions |= SELECT_SUBSCRIBED;
} else if (option.equals("REMOTE")) {
selectOptions |= SELECT_REMOTE;
} else {
throw new ImapParseException(tag, "unknown LIST select option \"" + option + '"');
}
}
if ((selectOptions & (SELECT_SUBSCRIBED | SELECT_RECURSIVE)) == SELECT_RECURSIVE) {
throw new ImapParseException(tag, "must include SUBSCRIBED when specifying RECURSIVEMATCH");
}
req.skipChar(')');
req.skipSpace();
}
String base = req.readFolder();
req.skipSpace();
if (req.peekChar() == '(' && extensionEnabled("LIST-EXTENDED")) {
parenthesized = true;
req.skipChar('(');
}
do {
if (!patterns.isEmpty()) {
req.skipSpace();
}
patterns.add(req.readFolderPattern());
} while (parenthesized && req.peekChar() != ')');
if (parenthesized) {
req.skipChar(')');
}
if (req.peekChar() == ' ' && extensionEnabled("LIST-EXTENDED")) {
req.skipSpace();
req.skipAtom("RETURN");
req.skipSpace();
req.skipChar('(');
while (req.peekChar() != ')') {
if (returnOptions != 0) {
req.skipSpace();
}
String option = req.readATOM();
if (option.equals("SUBSCRIBED")) {
returnOptions |= RETURN_SUBSCRIBED;
} else if (option.equals("CHILDREN")) {
returnOptions |= RETURN_CHILDREN;
} else if (option.equals("STATUS") && extensionEnabled("LIST-STATUS")) {
req.skipSpace();
status = parseStatusFields(req);
} else {
throw new ImapParseException(tag, "unknown LIST return option \"" + option + '"');
}
}
req.skipChar(')');
}
checkEOF(tag, req);
return doLIST(tag, base, patterns, selectOptions, returnOptions, status);
} else if (command.equals("LSUB")) {
req.skipSpace();
String base = req.readFolder();
req.skipSpace();
String pattern = req.readFolderPattern();
checkEOF(tag, req);
return doLSUB(tag, base, pattern);
} else if (command.equals("LISTRIGHTS") && extensionEnabled("ACL")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
req.skipSpace();
String principal = req.readAstring();
checkEOF(tag, req);
return doLISTRIGHTS(tag, path, principal);
}
break;
case 'M':
if (command.equals("MYRIGHTS") && extensionEnabled("ACL")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doMYRIGHTS(tag, path);
}
break;
case 'N':
if (command.equals("NOOP")) {
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doNOOP(tag);
} else if (command.equals("NAMESPACE") && extensionEnabled("NAMESPACE")) {
checkEOF(tag, req);
return doNAMESPACE(tag);
}
break;
case 'R':
if (command.equals("RENAME")) {
req.skipSpace();
ImapPath folder = new ImapPath(req.readFolder(), credentials, ImapPath.Scope.NAME);
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials, ImapPath.Scope.NAME);
checkEOF(tag, req);
return doRENAME(tag, folder, path);
}
break;
case 'S':
if (command.equals("STORE")) {
StoreAction operation = StoreAction.REPLACE;
boolean silent = false;
int modseq = -1;
req.skipSpace();
String sequence = req.readSequence();
req.skipSpace();
if (req.peekChar() == '(' && extensionEnabled("CONDSTORE")) {
req.skipChar('(');
req.skipAtom("UNCHANGEDSINCE");
req.skipSpace();
modseq = req.parseInteger(req.readNumber(ImapRequest.ZERO_OK));
req.skipChar(')');
req.skipSpace();
}
switch(req.peekChar()) {
case '+':
req.skipChar('+');
operation = StoreAction.ADD;
break;
case '-':
req.skipChar('-');
operation = StoreAction.REMOVE;
break;
}
String cmd = req.readATOM();
if (cmd.equals("FLAGS.SILENT")) {
silent = true;
} else if (!cmd.equals("FLAGS")) {
throw new ImapParseException(tag, "invalid store-att-flags");
}
req.skipSpace();
List<String> flags = req.readFlags();
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doSTORE(tag, sequence, flags, operation, silent, modseq, byUID);
} else if (command.equals("SELECT")) {
byte params = 0;
QResyncInfo qri = null;
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
if (req.peekChar() == ' ') {
req.skipSpace();
req.skipChar('(');
while (req.peekChar() != ')') {
if (params != 0) {
req.skipSpace();
}
String param = req.readATOM();
if (param.equals("CONDSTORE") && extensionEnabled("CONDSTORE")) {
params |= ImapFolder.SELECT_CONDSTORE;
} else if (param.equals("QRESYNC") && sessionActivated(ImapExtension.QRESYNC)) {
params |= ImapFolder.SELECT_CONDSTORE;
req.skipSpace();
qri = parseQResyncInfo(req);
} else {
throw new ImapParseException(tag, "unknown SELECT parameter \"" + param + '"');
}
}
req.skipChar(')');
}
checkEOF(tag, req);
return doSELECT(tag, path, params, qri);
} else if (command.equals("SEARCH")) {
Integer options = null;
req.skipSpace();
if ("RETURN".equals(req.peekATOM()) && extensionEnabled("ESEARCH")) {
options = parseSearchOptions(req);
req.skipSpace();
}
Charset charset = null;
if ("CHARSET".equals(req.peekATOM())) {
req.skipAtom("CHARSET");
req.skipSpace();
charset = req.readCharset();
req.skipSpace();
}
ImapSearch i4search = req.readSearch(charset);
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doSEARCH(tag, i4search, byUID, options);
} else if (command.equals("STARTTLS") && extensionEnabled("STARTTLS")) {
checkEOF(tag, req);
return doSTARTTLS(tag);
} else if (command.equals("STATUS")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
req.skipSpace();
byte status = parseStatusFields(req);
checkEOF(tag, req);
return doSTATUS(tag, path, status);
} else if (command.equals("SORT") && extensionEnabled("SORT")) {
Integer options = null;
req.skipSpace();
if ("RETURN".equals(req.peekATOM()) && extensionEnabled("ESORT")) {
options = parseSearchOptions(req);
req.skipSpace();
}
req.skipChar('(');
boolean desc = false;
List<SortBy> order = new ArrayList<SortBy>(2);
do {
if (desc || !order.isEmpty()) {
req.skipSpace();
}
SortBy sort;
String key = req.readATOM();
if (key.equals("REVERSE") && !desc) {
desc = true;
continue;
} else if (key.equals("ARRIVAL")) {
sort = desc ? SortBy.DATE_DESC : SortBy.DATE_ASC;
} else if (key.equals("CC")) {
// FIXME: CC sort not implemented
sort = SortBy.NONE;
} else if (key.equals("DATE")) {
// FIXME: DATE sorts on INTERNALDATE, not the Date header
sort = desc ? SortBy.DATE_DESC : SortBy.DATE_ASC;
} else if (key.equals("FROM")) {
sort = desc ? SortBy.NAME_DESC : SortBy.NAME_ASC;
} else if (key.equals("SIZE")) {
sort = desc ? SortBy.SIZE_DESC : SortBy.SIZE_ASC;
} else if (key.equals("SUBJECT")) {
sort = desc ? SortBy.SUBJ_DESC : SortBy.SUBJ_ASC;
} else if (key.equals("TO")) {
sort = desc ? SortBy.RCPT_DESC : SortBy.RCPT_ASC;
} else {
throw new ImapParseException(tag, "unknown SORT key \"" + key + '"');
}
order.add(sort);
desc = false;
} while (desc || req.peekChar() != ')');
req.skipChar(')');
req.skipSpace();
Charset charset = req.readCharset();
req.skipSpace();
ImapSearch i4search = req.readSearch(charset);
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doSORT(tag, i4search, byUID, options, order);
} else if (command.equals("SUBSCRIBE")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doSUBSCRIBE(tag, path);
} else if (command.equals("SETACL") && extensionEnabled("ACL")) {
StoreAction action = StoreAction.REPLACE;
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
req.skipSpace();
String principal = req.readAstring();
req.skipSpace();
String i4rights = req.readAstring();
checkEOF(tag, req);
if (i4rights.startsWith("+")) {
action = StoreAction.ADD;
i4rights = i4rights.substring(1);
} else if (i4rights.startsWith("-")) {
action = StoreAction.REMOVE;
i4rights = i4rights.substring(1);
}
return doSETACL(tag, path, principal, i4rights, action);
} else if (command.equals("SETQUOTA") && extensionEnabled("QUOTA")) {
Map<String, String> limits = new HashMap<String, String>();
req.skipSpace();
// qroot
req.readAstring();
req.skipSpace();
req.skipChar('(');
while (req.peekChar() != ')') {
if (!limits.isEmpty()) {
req.skipSpace();
}
String resource = req.readATOM();
req.skipSpace();
limits.put(resource, req.readNumber());
}
req.skipChar(')');
checkEOF(tag, req);
return doSETQUOTA(tag);
}
break;
case 'T':
if (command.equals("THREAD") && extensionEnabled("THREAD=ORDEREDSUBJECT")) {
req.skipSpace();
req.skipAtom("ORDEREDSUBJECT");
req.skipSpace();
Charset charset = req.readCharset();
req.skipSpace();
ImapSearch i4search = req.readSearch(charset);
checkEOF(tag, req);
return isProxied ? imapProxy.proxy(req) : doTHREAD(tag, i4search, byUID);
}
break;
case 'U':
if (command.equals("UID")) {
req.skipSpace();
command = req.readATOM();
if (command.equals("FETCH") || command.equals("SEARCH") || command.equals("COPY") || command.equals("STORE") || (command.equals("EXPUNGE") && extensionEnabled("UIDPLUS")) || (command.equals("SORT") && extensionEnabled("SORT")) || (command.equals("THREAD") && extensionEnabled("THREAD=ORDEREDSUBJECT"))) {
byUID = true;
lastCommand += " " + command;
continue;
}
throw new ImapParseException(tag, "command not permitted with UID");
} else if (command.equals("UNSUBSCRIBE")) {
req.skipSpace();
ImapPath path = new ImapPath(req.readFolder(), credentials);
checkEOF(tag, req);
return doUNSUBSCRIBE(tag, path);
} else if (command.equals("UNSELECT") && extensionEnabled("UNSELECT")) {
checkEOF(tag, req);
return doUNSELECT(tag);
}
break;
case 'X':
if (command.equals("XLIST")) {
req.skipSpace();
String base = req.readFolder();
req.skipSpace();
Set<String> patterns = Collections.singleton(req.readFolderPattern());
checkEOF(tag, req);
return doLIST(tag, base, patterns, (byte) 0, RETURN_XLIST, (byte) 0);
}
break;
}
} while (byUID);
throw new ImapParseException(tag, "command not implemented");
}
use of com.zimbra.cs.index.SortBy in project zm-mailbox by Zimbra.
the class ImapHandler method search.
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 {
Mailbox mbox = i4folder.getMailbox();
if (unsorted && i4search.canBeRunLocally()) {
mbox.lock.lock(false);
try {
hits = i4search.evaluate(i4folder);
hits.remove(null);
} finally {
mbox.lock.release();
}
} else {
ZimbraQueryResults zqr = runSearch(i4search, i4folder, sort, requiresMODSEQ ? SearchParams.Fetch.MODSEQ : SearchParams.Fetch.IDS);
hits = unsorted ? new ImapMessageSet() : new ArrayList<ImapMessage>();
try {
for (ZimbraHit 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());
}
} finally {
Closeables.closeQuietly(zqr);
}
}
} catch (ServiceException e) {
// variable to the empty sequence."
if (saveResults) {
i4folder.saveSearchResults(new ImapMessageSet());
}
ZimbraLog.imap.warn(command + " failed", e);
sendNO(tag, command + " failed");
return true;
}
int size = hits.size();
ImapMessage first = null, 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;
}
Aggregations