Search in sources :

Example 1 with SortBy

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;
}
Also used : SearchParams(com.zimbra.cs.index.SearchParams) SortBy(com.zimbra.cs.index.SortBy) ZimbraQueryResults(com.zimbra.cs.index.ZimbraQueryResults) ReSortingQueryResults(com.zimbra.cs.index.ReSortingQueryResults)

Example 2 with SortBy

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;
}
Also used : MailItem(com.zimbra.cs.mailbox.MailItem) SortBy(com.zimbra.cs.index.SortBy) ArrayList(java.util.ArrayList)

Example 3 with SortBy

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;
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) HashMap(java.util.HashMap) SortBy(com.zimbra.cs.index.SortBy) Element(com.zimbra.common.soap.Element) ArrayList(java.util.ArrayList) ItemId(com.zimbra.cs.service.util.ItemId) Contact(com.zimbra.cs.mailbox.Contact) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) ContactGroup(com.zimbra.cs.mailbox.ContactGroup)

Example 4 with SortBy

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");
}
Also used : LinkedHashSet(java.util.LinkedHashSet) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) SortBy(com.zimbra.cs.index.SortBy) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) Charset(java.nio.charset.Charset) Mountpoint(com.zimbra.cs.mailbox.Mountpoint)

Example 5 with SortBy

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;
}
Also used : ZimbraHit(com.zimbra.cs.index.ZimbraHit) SortBy(com.zimbra.cs.index.SortBy) ArrayList(java.util.ArrayList) Mountpoint(com.zimbra.cs.mailbox.Mountpoint) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZMailbox(com.zimbra.client.ZMailbox) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) ZimbraQueryResults(com.zimbra.cs.index.ZimbraQueryResults) ImapMessageSet(com.zimbra.cs.imap.ImapMessage.ImapMessageSet)

Aggregations

SortBy (com.zimbra.cs.index.SortBy)6 ArrayList (java.util.ArrayList)4 ZimbraQueryResults (com.zimbra.cs.index.ZimbraQueryResults)3 Mailbox (com.zimbra.cs.mailbox.Mailbox)3 Element (com.zimbra.common.soap.Element)2 SearchParams (com.zimbra.cs.index.SearchParams)2 Mountpoint (com.zimbra.cs.mailbox.Mountpoint)2 OperationContext (com.zimbra.cs.mailbox.OperationContext)2 ItemId (com.zimbra.cs.service.util.ItemId)2 ItemIdFormatter (com.zimbra.cs.service.util.ItemIdFormatter)2 ZimbraSoapContext (com.zimbra.soap.ZimbraSoapContext)2 HashMap (java.util.HashMap)2 ZMailbox (com.zimbra.client.ZMailbox)1 ServiceException (com.zimbra.common.service.ServiceException)1 SoapFaultException (com.zimbra.common.soap.SoapFaultException)1 Account (com.zimbra.cs.account.Account)1 AccountServiceException (com.zimbra.cs.account.AccountServiceException)1 ImapMessageSet (com.zimbra.cs.imap.ImapMessage.ImapMessageSet)1 ReSortingQueryResults (com.zimbra.cs.index.ReSortingQueryResults)1 ZimbraHit (com.zimbra.cs.index.ZimbraHit)1