Search in sources :

Example 46 with ItemIdFormatter

use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.

the class SendInviteReply method handle.

@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
    ZimbraSoapContext zsc = getZimbraSoapContext(context);
    Mailbox mbox = getRequestedMailbox(zsc);
    Account acct = getRequestedAccount(zsc);
    Account authAcct = getAuthenticatedAccount(zsc);
    boolean isAdmin = zsc.isUsingAdminPrivileges();
    OperationContext octxt = getOperationContext(zsc, context);
    boolean onBehalfOf = isOnBehalfOfRequest(zsc);
    ItemId iid = new ItemId(request.getAttribute(MailConstants.A_ID), zsc);
    int compNum = (int) request.getAttributeLong(MailConstants.A_CAL_COMPONENT_NUM);
    String verbStr = request.getAttribute(MailConstants.A_VERB);
    Verb verb = CalendarMailSender.parseVerb(verbStr);
    boolean isDecline = CalendarMailSender.VERB_DECLINE.equals(verb);
    boolean updateOrg = request.getAttributeBool(MailConstants.A_CAL_UPDATE_ORGANIZER, true);
    // Get the identity/persona being used in the reply.  It is set at the request level, but
    // let's also look for it in the <m> child element too, because that is the precedent in
    // the SendMsg request.  For SendInviteReply we have to insist it at request level because
    // <m> is an optional element.
    String identityId = request.getAttribute(MailConstants.A_IDENTITY_ID, null);
    if (identityId == null) {
        Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
        if (msgElem != null)
            identityId = msgElem.getAttribute(MailConstants.A_IDENTITY_ID, null);
    }
    Element response = getResponseElement(zsc);
    boolean intendedForMe = true;
    Invite oldInv = null;
    int calItemId;
    int inviteMsgId;
    CalendarItem calItem = null;
    boolean wasInTrash = false;
    // calendar item (id="aaaa-nnnn") --- work in both cases
    if (iid.hasSubpart()) {
        // directly accepting the calendar item
        calItemId = iid.getId();
        inviteMsgId = iid.getSubpartId();
        calItem = safeGetCalendarItemById(mbox, octxt, iid.getId());
        if (calItem == null)
            throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
        oldInv = calItem.getInvite(inviteMsgId, compNum);
    } else {
        // accepting the message: go find the calendar item and then the invite
        inviteMsgId = iid.getId();
        Message msg = mbox.getMessageById(octxt, inviteMsgId);
        Message.CalendarItemInfo info = msg.getCalendarItemInfo(compNum);
        if (info == null)
            throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
        String intendedFor = msg.getCalendarIntendedFor();
        Account intendedAcct = null;
        if (intendedFor != null) {
            try {
                InternetAddress intendedForAddr = new JavaMailInternetAddress(intendedFor);
                intendedAcct = Provisioning.getInstance().get(AccountBy.name, intendedForAddr.getAddress());
            } catch (AddressException e) {
                throw ServiceException.INVALID_REQUEST("The intended account " + intendedFor + " is invalid", e);
            }
            if (intendedAcct == null) {
                throw ServiceException.INVALID_REQUEST("The intended account " + intendedFor + " was not found", null);
            }
            // Special case: intended account = me.
            if (intendedAcct.equals(mbox.getAccount()))
                intendedAcct = null;
            else
                intendedForMe = false;
        }
        if (intendedAcct != null) {
            // trace logging: let's just indicate we're replying to a remote appointment
            ZimbraLog.calendar.info("<SendInviteReply> (remote mbox) id=%s, verb=%s, notifyOrg=%s", new ItemIdFormatter(zsc).formatItemId(iid), verb.toString(), Boolean.toString(updateOrg));
            // Replying to a remote appointment
            calItem = null;
            calItemId = 0;
            ZMailbox zmbx = getRemoteZMailbox(octxt, authAcct, intendedAcct);
            // Try to add the appointment to remote mailbox.
            AddInviteResult addInviteResult = sendAddInvite(zmbx, octxt, msg);
            if (addInviteResult == null)
                throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
            // Forward the reply request.
            remoteSendInviteReply(zmbx, request, addInviteResult);
        } else {
            // Replying to a local appointment
            if (info.getInvite() != null) {
                calItem = mbox.getCalendarItemByUid(octxt, info.getInvite().getUid());
                wasInTrash = calItem != null && calItem.inTrash();
                if (calItem != null && !wasInTrash) {
                    Invite newInv = info.getInvite();
                    // If appointment exists, check if our invite has been outdated.
                    Invite curr = calItem.getInvite(newInv.getRecurId());
                    if (curr != null && !newInv.isSameOrNewerVersion(curr))
                        throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
                }
                Invite inv = info.getInvite().newCopy();
                Invite.setDefaultAlarm(inv, acct);
                inv.setMailItemId(inviteMsgId);
                // (TODO: Is it better to delete the existing appointment/instance when declining?)
                if (calItem != null || !isDecline) {
                    // Add the invite.  This will either create or update the appointment.
                    int folder;
                    boolean untrashing = wasInTrash && !isDecline;
                    if (calItem == null || untrashing) {
                        // If appointment/task doesn't exist, create in default folder.
                        // If it exists but is in Trash and is not a decline, move it out of Trash.
                        // If it's in trash and we're declining, leave it in trash.
                        folder = inv.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
                    } else {
                        folder = calItem.getFolderId();
                    }
                    ParsedMessage pm = new ParsedMessage(msg.getMimeMessage(false), false);
                    AddInviteData aid = mbox.addInvite(octxt, inv, folder, pm, false, untrashing, true);
                    if (aid == null)
                        throw ServiceException.FAILURE("Could not create/update calendar item", null);
                    calItemId = aid.calItemId;
                    // Refetch updated item.
                    calItem = safeGetCalendarItemById(mbox, octxt, aid.calItemId);
                    if (calItem == null)
                        throw ServiceException.FAILURE("Could not refetch created/updated calendar item", null);
                } else {
                    calItemId = 0;
                }
                oldInv = inv;
            } else if (info.calItemCreated()) {
                // legacy case (before we added Invite info to Message metadata)
                calItem = safeGetCalendarItemById(mbox, octxt, info.getCalendarItemId());
                if (calItem == null)
                    throw ServiceException.FAILURE("Missing invite data", null);
                // Must be for this mailbox
                calItemId = info.getCalendarItemId().getId();
                wasInTrash = calItem.inTrash();
                oldInv = calItem.getInvite(inviteMsgId, compNum);
            } else {
                throw ServiceException.FAILURE("Missing invite data", null);
            }
        }
    }
    if (intendedForMe) {
        if (oldInv == null)
            throw MailServiceException.INVITE_OUT_OF_DATE(iid.toString());
        if (calItem != null && (mbox.getEffectivePermissions(octxt, calItemId, MailItem.Type.UNKNOWN) & ACL.RIGHT_ACTION) == 0) {
            throw ServiceException.PERM_DENIED("You do not have ACTION rights for CalendarItem " + calItemId);
        }
        // check if invite organizer requested rsvp or not
        updateOrg = updateOrg && oldInv.getRsvp();
        // Don't allow creating/editing a private appointment on behalf of another user,
        // unless that other user is a calendar resource.
        boolean allowPrivateAccess = calItem != null ? calItem.allowPrivateAccess(authAcct, isAdmin) : true;
        boolean isCalendarResource = acct instanceof CalendarResource;
        if (!allowPrivateAccess && !oldInv.isPublic() && !isCalendarResource)
            throw ServiceException.PERM_DENIED("Cannot reply to a private appointment/task on behalf of another user");
        // see if there is a specific Exception being referenced by this reply...
        Element exc = request.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID);
        ParsedDateTime exceptDt = null;
        if (exc != null) {
            TimeZoneMap tzmap = oldInv.getTimeZoneMap();
            Element tzElem = request.getOptionalElement(MailConstants.E_CAL_TZ);
            ICalTimeZone tz = null;
            if (tzElem != null) {
                tz = CalendarUtils.parseTzElement(tzElem);
                tzmap.add(tz);
            }
            exceptDt = CalendarUtils.parseDateTime(exc, tzmap);
        } else if (oldInv.hasRecurId()) {
            exceptDt = oldInv.getRecurId().getDt();
        }
        // trace logging
        String calItemIdStr = calItem != null ? Integer.toString(calItem.getId()) : "none";
        String folderIdStr = calItem != null ? Integer.toString(calItem.getFolderId()) : "none";
        if (exceptDt == null)
            ZimbraLog.calendar.info("<SendInviteReply> id=%s, folderId=%s, verb=%s, notifyOrg=%s, subject=\"%s\", UID=%s", calItemIdStr, folderIdStr, verb.toString(), Boolean.toString(updateOrg), oldInv.isPublic() ? oldInv.getName() : "(private)", oldInv.getUid());
        else
            ZimbraLog.calendar.info("<SendInviteReply> id=%s, folderId=%s, verb=%s, notifyOrg=%s, subject=\"%s\", UID=%s, recurId=%s", calItemIdStr, folderIdStr, verb.toString(), Boolean.toString(updateOrg), oldInv.isPublic() ? oldInv.getName() : "(private)", oldInv.getUid(), exceptDt.getUtcString());
        // exception instance first.  Then reply to it.
        if (calItem != null && oldInv.isRecurrence() && exceptDt != null) {
            Invite localException = oldInv.makeInstanceInvite(exceptDt);
            long now = octxt != null ? octxt.getTimestamp() : System.currentTimeMillis();
            localException.setDtStamp(now);
            String partStat = verb.getXmlPartStat();
            localException.setPartStat(partStat);
            ZAttendee at = localException.getMatchingAttendee(acct, identityId);
            if (at != null)
                at.setPartStat(partStat);
            // Carry over the MimeMessage/ParsedMessage to preserve any attachments.
            MimeMessage mmInv = calItem.getSubpartMessage(oldInv.getMailItemId());
            ParsedMessage pm = mmInv != null ? new ParsedMessage(mmInv, false) : null;
            int folder;
            boolean untrashing = wasInTrash && !isDecline;
            if (untrashing) {
                // If it exists but is in Trash and is not a decline, move it out of Trash.
                // If it's in trash and we're declining, leave it in trash.
                folder = localException.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
            } else {
                folder = calItem.getFolderId();
            }
            mbox.addInvite(octxt, localException, folder, pm, true, untrashing, true);
            // Refetch the updated calendar item and set oldInv to refetched local exception instance.
            calItem = safeGetCalendarItemById(mbox, octxt, calItemId);
            if (calItem == null)
                throw MailServiceException.NO_SUCH_CALITEM(iid.toString(), "Could not find calendar item");
            oldInv = calItem.getInvite(new RecurId(exceptDt, RecurId.RANGE_NONE));
        }
        if (updateOrg && oldInv.hasOrganizer()) {
            Locale locale;
            Account organizer = oldInv.getOrganizerAccount();
            if (organizer != null)
                locale = organizer.getLocale();
            else
                locale = !onBehalfOf ? acct.getLocale() : authAcct.getLocale();
            String subject;
            if (!allowPrivateAccess && !oldInv.isPublic())
                subject = L10nUtil.getMessage(MsgKey.calendarSubjectWithheld, locale);
            else
                subject = oldInv.getName();
            String replySubject = CalendarMailSender.getReplySubject(verb, subject, locale);
            CalSendData csd = new CalSendData();
            csd.mOrigId = new ItemId(mbox, oldInv.getMailItemId());
            csd.mReplyType = MailSender.MSGTYPE_REPLY;
            csd.mInvite = CalendarMailSender.replyToInvite(acct, identityId, authAcct, onBehalfOf, allowPrivateAccess, oldInv, verb, replySubject, exceptDt);
            ZVCalendar iCal = csd.mInvite.newToICalendar(true);
            ParseMimeMessage.MimeMessageData parsedMessageData = new ParseMimeMessage.MimeMessageData();
            // did they specify a custom <m> message?  If so, then we don't have to build one...
            Element msgElem = request.getOptionalElement(MailConstants.E_MSG);
            if (msgElem != null) {
                String text = ParseMimeMessage.getTextPlainContent(msgElem);
                String html = ParseMimeMessage.getTextHtmlContent(msgElem);
                iCal.addDescription(text, html);
                MimeBodyPart[] mbps = new MimeBodyPart[1];
                mbps[0] = CalendarMailSender.makeICalIntoMimePart(iCal);
                // the <inv> element is *NOT* allowed -- we always build it manually
                // based on the params to the <SendInviteReply> and stick it in the
                // mbps (additionalParts) parameter...
                csd.mMm = ParseMimeMessage.parseMimeMsgSoap(zsc, octxt, mbox, msgElem, mbps, ParseMimeMessage.NO_INV_ALLOWED_PARSER, parsedMessageData);
            } else {
                // build a default "Accepted" response
                if (!(acct instanceof CalendarResource)) {
                    csd.mMm = CalendarMailSender.createDefaultReply(acct, identityId, authAcct, identityId, isAdmin, onBehalfOf, calItem, oldInv, null, replySubject, verb, null, iCal);
                } else {
                    // different template for calendar resources
                    RecurId rid = oldInv.getRecurId();
                    ParsedDateTime ridDt = rid != null ? rid.getDt() : null;
                    Invite replyInv = CalendarMailSender.replyToInvite(acct, authAcct, onBehalfOf, allowPrivateAccess, oldInv, verb, replySubject, ridDt);
                    MimeMessage mmInv = calItem.getSubpartMessage(oldInv.getMailItemId());
                    csd.mMm = CalendarMailSender.createResourceAutoReply(octxt, identityId, identityId, mbox, verb, false, null, calItem, oldInv, new Invite[] { replyInv }, mmInv, true);
                }
            }
            int apptFolderId;
            if (calItem != null)
                apptFolderId = calItem.getFolderId();
            else
                apptFolderId = oldInv.isTodo() ? Mailbox.ID_FOLDER_TASKS : Mailbox.ID_FOLDER_CALENDAR;
            MailSendQueue sendQueue = new MailSendQueue();
            try {
                sendCalendarMessage(zsc, octxt, apptFolderId, acct, mbox, csd, response, sendQueue);
            } finally {
                sendQueue.send();
            }
        }
        RecurId recurId = null;
        if (exceptDt != null) {
            recurId = new RecurId(exceptDt, RecurId.RANGE_NONE);
        }
        ZAttendee me = oldInv.getMatchingAttendee(acct);
        String cnStr = null;
        String addressStr = acct.getName();
        String role = IcalXmlStrMap.ROLE_OPT_PARTICIPANT;
        int seqNo = oldInv.getSeqNo();
        long dtStamp = oldInv.getDTStamp();
        if (me != null) {
            if (me.hasCn()) {
                cnStr = me.getCn();
            }
            addressStr = me.getAddress();
            if (me.hasRole()) {
                role = me.getRole();
            }
        }
        if (calItem != null)
            mbox.modifyPartStat(octxt, calItemId, recurId, cnStr, addressStr, null, role, verb.getXmlPartStat(), Boolean.FALSE, seqNo, dtStamp);
    }
    // move the invite to the Trash if the user wants it
    if (deleteInviteOnReply(acct)) {
        try {
            if (onBehalfOf) {
                // HACK: Run the move in the context of the organizer
                // mailbox because the authenticated account doesn't
                // have rights on Inbox and Trash folders.
                octxt = new OperationContext(mbox);
            }
            mbox.move(octxt, inviteMsgId, MailItem.Type.MESSAGE, Mailbox.ID_FOLDER_TRASH);
        } catch (MailServiceException.NoSuchItemException nsie) {
            ZimbraLog.calendar.debug("can't move nonexistent invite to Trash: " + inviteMsgId);
        }
    }
    return response;
}
Also used : Locale(java.util.Locale) Account(com.zimbra.cs.account.Account) InternetAddress(javax.mail.internet.InternetAddress) JavaMailInternetAddress(com.zimbra.common.mime.shim.JavaMailInternetAddress) Message(com.zimbra.cs.mailbox.Message) MimeMessage(javax.mail.internet.MimeMessage) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) AddInviteData(com.zimbra.cs.mailbox.Mailbox.AddInviteData) Element(com.zimbra.common.soap.Element) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) ItemId(com.zimbra.cs.service.util.ItemId) CalendarItem(com.zimbra.cs.mailbox.CalendarItem) ZVCalendar(com.zimbra.common.calendar.ZCalendar.ZVCalendar) ZMailbox(com.zimbra.client.ZMailbox) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZMailbox(com.zimbra.client.ZMailbox) MimeMessage(javax.mail.internet.MimeMessage) Verb(com.zimbra.cs.mailbox.calendar.CalendarMailSender.Verb) AddressException(javax.mail.internet.AddressException) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) CalendarResource(com.zimbra.cs.account.CalendarResource) OperationContext(com.zimbra.cs.mailbox.OperationContext) ParsedMessage(com.zimbra.cs.mime.ParsedMessage) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) JavaMailInternetAddress(com.zimbra.common.mime.shim.JavaMailInternetAddress) MimeBodyPart(javax.mail.internet.MimeBodyPart) Invite(com.zimbra.cs.mailbox.calendar.Invite) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone)

Example 47 with ItemIdFormatter

use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.

the class SendInviteReply method sendAddInvite.

private static AddInviteResult sendAddInvite(ZMailbox zmbx, OperationContext octxt, Message msg) throws ServiceException {
    ItemIdFormatter ifmt = new ItemIdFormatter();
    Element addInvite = Element.create(SoapProtocol.SoapJS, MailConstants.ADD_APPOINTMENT_INVITE_REQUEST);
    ToXML.encodeMessageAsMIME(addInvite, ifmt, octxt, msg, null, true, false);
    Element response = zmbx.invoke(addInvite);
    int calItemId = (int) response.getAttributeLong(MailConstants.A_CAL_ID, 0);
    int invId = (int) response.getAttributeLong(MailConstants.A_CAL_INV_ID, 0);
    int compNum = (int) response.getAttributeLong(MailConstants.A_CAL_COMPONENT_NUM, 0);
    if (calItemId != 0)
        return new AddInviteResult(calItemId, invId, compNum);
    else
        return null;
}
Also used : ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) Element(com.zimbra.common.soap.Element)

Example 48 with ItemIdFormatter

use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.

the class Sync 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);
    SyncRequest syncRequest = JaxbUtil.elementToJaxb(request);
    String token = syncRequest.getToken();
    Element response = zsc.createElement(MailConstants.SYNC_RESPONSE);
    response.addAttribute(MailConstants.A_CHANGE_DATE, System.currentTimeMillis() / 1000);
    // the sync token is of the form "last fully synced change id" (e.g. "32425") or
    // last fully synced change id-last item synced in next change id" (e.g. "32425-99213") or
    // last fully synced change id-last item synced in next change id and last fully synced delete change id" (e.g. "32425-99213:d1231232") or
    // last fully synced change id-last item synced in next change id and 
    // last fully synced delete id-last item synced in next delete id (e.g. "32425-99213:d12312-82134")
    SyncToken syncToken = null;
    int tokenInt = 0;
    if (!StringUtil.isNullOrEmpty(token)) {
        syncToken = new SyncToken(token);
        tokenInt = syncToken.getChangeId();
    }
    if (syncToken == null) {
        syncToken = new SyncToken(0);
    }
    int deleleLimit = syncRequest.getDeleteLimit();
    int changeLimit = syncRequest.getChangeLimit();
    // server can apply delete pagination through debugconfig/localconfig.
    if (deleleLimit <= 0) {
        deleleLimit = DebugConfig.syncMaximumDeleteCount;
    }
    // client specify more than DebugConfig.syncMaximumChangeCount It will use DebugConfig.syncMaximumChangeCount
    if (changeLimit <= 0 || changeLimit > DebugConfig.syncMaximumChangeCount) {
        changeLimit = DebugConfig.syncMaximumChangeCount;
    }
    boolean initialSync = tokenInt <= 0;
    // permit the caller to restrict initial sync only to calendar items with a recurrence after a given date
    long calendarStart = (syncRequest.getCalendarCutoff() != null) ? syncRequest.getCalendarCutoff() : -1;
    int messageSyncStart = (syncRequest.getMsgCutoff() != null) ? syncRequest.getMsgCutoff() : -1;
    // if the sync is constrained to a folder subset, we need to first figure out what can be seen
    Folder root = null;
    ItemId iidFolder = null;
    try {
        iidFolder = new ItemId(request.getAttribute(MailConstants.A_FOLDER, DEFAULT_FOLDER_ID + ""), zsc);
        OperationContext octxtOwner = new OperationContext(mbox);
        root = mbox.getFolderById(octxtOwner, iidFolder.getId());
    } catch (MailServiceException.NoSuchItemException nsie) {
    }
    Set<Folder> visible = octxt.isDelegatedRequest(mbox) ? mbox.getVisibleFolders(octxt) : null;
    FolderNode rootNode = null;
    if (root == null || iidFolder == null) {
        // resolve grantee names of all ACLs on the mailbox
        rootNode = mbox.getFolderTree(octxt, null, true);
    } else {
        // resolve grantee names of all ACLs on all sub-folders of the requested folder
        rootNode = mbox.getFolderTree(octxt, iidFolder, true);
    }
    OperationContextData.addGranteeNames(octxt, rootNode);
    // actually perform the sync
    mbox.lock.lock();
    try {
        mbox.beginTrackingSync();
        if (initialSync) {
            response.addAttribute(MailConstants.A_TOKEN, mbox.getLastChangeID());
            response.addAttribute(MailConstants.A_SIZE, mbox.getSize());
            boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, calendarStart, messageSyncStart, SyncPhase.INITIAL);
            // if no folders are visible, add an empty "<folder/>" as a hint
            if (!anyFolders) {
                response.addElement(MailConstants.E_FOLDER);
            }
        } else {
            boolean typedDeletes = request.getAttributeBool(MailConstants.A_TYPED_DELETES, false);
            String newToken = deltaSync(response, octxt, ifmt, mbox, syncToken, deleleLimit, changeLimit, typedDeletes, root, visible, messageSyncStart);
            response.addAttribute(MailConstants.A_TOKEN, newToken);
        }
    } finally {
        mbox.lock.release();
    }
    return response;
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) FolderNode(com.zimbra.cs.mailbox.Mailbox.FolderNode) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) Element(com.zimbra.common.soap.Element) Folder(com.zimbra.cs.mailbox.Folder) ItemId(com.zimbra.cs.service.util.ItemId) SyncToken(com.zimbra.cs.service.util.SyncToken) Mailbox(com.zimbra.cs.mailbox.Mailbox) SyncRequest(com.zimbra.soap.mail.message.SyncRequest) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) MailServiceException(com.zimbra.cs.mailbox.MailServiceException)

Example 49 with ItemIdFormatter

use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.

the class Search method searchRemoteAccountCalendars.

private static void searchRemoteAccountCalendars(Element parent, SearchParams params, ZimbraSoapContext zsc, Account authAcct, Map<String, List<Integer>> accountFolders) throws ServiceException {
    // mail service soap requests want to see a target account
    String nominalTargetAcctId = null;
    StringBuilder queryStr = new StringBuilder();
    for (Map.Entry<String, List<Integer>> entry : accountFolders.entrySet()) {
        String acctId = entry.getKey();
        if (nominalTargetAcctId == null)
            nominalTargetAcctId = acctId;
        ItemIdFormatter ifmt = new ItemIdFormatter(authAcct.getId(), acctId, false);
        List<Integer> folderIds = entry.getValue();
        for (int folderId : folderIds) {
            if (queryStr.length() > 0)
                queryStr.append(" OR ");
            // must quote the qualified folder id
            queryStr.append("inid:\"").append(ifmt.formatItemId(folderId)).append("\"");
        }
    }
    Element req = zsc.createElement(MailConstants.SEARCH_REQUEST);
    req.addAttribute(MailConstants.A_SEARCH_TYPES, MailItem.Type.toString(params.getTypes()));
    if (params.getSortBy() != null) {
        req.addAttribute(MailConstants.A_SORTBY, params.getSortBy().toString());
    }
    req.addAttribute(MailConstants.A_QUERY_OFFSET, params.getOffset());
    if (params.getLimit() != 0)
        req.addAttribute(MailConstants.A_QUERY_LIMIT, params.getLimit());
    req.addAttribute(MailConstants.A_CAL_EXPAND_INST_START, params.getCalItemExpandStart());
    req.addAttribute(MailConstants.A_CAL_EXPAND_INST_END, params.getCalItemExpandEnd());
    req.addAttribute(MailConstants.E_QUERY, queryStr.toString(), Element.Disposition.CONTENT);
    Account target = Provisioning.getInstance().get(Key.AccountBy.id, nominalTargetAcctId);
    AuthToken authToken = AuthToken.getCsrfUnsecuredAuthToken(zsc.getAuthToken());
    String pxyAuthToken = authToken.getProxyAuthToken();
    ZAuthToken zat = pxyAuthToken == null ? authToken.toZAuthToken() : new ZAuthToken(pxyAuthToken);
    ZMailbox.Options zoptions = new ZMailbox.Options(zat, AccountUtil.getSoapUri(target));
    zoptions.setTargetAccount(nominalTargetAcctId);
    zoptions.setTargetAccountBy(AccountBy.id);
    zoptions.setNoSession(true);
    ZMailbox zmbx = ZMailbox.getMailbox(zoptions);
    Element resp = zmbx.invoke(req);
    for (Element hit : resp.listElements()) {
        hit.detach();
        parent.addElement(hit);
    }
}
Also used : Account(com.zimbra.cs.account.Account) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) Element(com.zimbra.common.soap.Element) ZAuthToken(com.zimbra.common.auth.ZAuthToken) ZMailbox(com.zimbra.client.ZMailbox) AuthToken(com.zimbra.cs.account.AuthToken) ZAuthToken(com.zimbra.common.auth.ZAuthToken) ArrayList(java.util.ArrayList) List(java.util.List) HashMap(java.util.HashMap) Map(java.util.Map)

Example 50 with ItemIdFormatter

use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.

the class SearchConv 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);
    SearchConvRequest req = JaxbUtil.elementToJaxb(request);
    boolean nest = ZmBoolean.toBool(req.getNestMessages(), false);
    Account acct = getRequestedAccount(zsc);
    SearchParams params = SearchParams.parse(req, zsc, acct.getPrefMailInitialSearch());
    // append (conv:(convid)) onto the beginning of the queryStr
    ItemId cid = new ItemId(req.getConversationId(), zsc);
    params.setQueryString("conv:\"" + cid.toString(ifmt) + "\" (" + params.getQueryString() + ')');
    // force to group-by-message
    params.setTypes(EnumSet.of(MailItem.Type.MESSAGE));
    Element response = null;
    if (cid.belongsTo(mbox)) {
        // local
        ZimbraQueryResults results = mbox.index.search(zsc.getResponseProtocol(), octxt, params);
        try {
            response = zsc.createElement(MailConstants.SEARCH_CONV_RESPONSE);
            response.addAttribute(MailConstants.A_QUERY_OFFSET, Integer.toString(params.getOffset()));
            SortBy sort = results.getSortBy();
            response.addAttribute(MailConstants.A_SORTBY, sort.toString());
            List<Message> msgs = mbox.getMessagesByConversation(octxt, cid.getId(), sort, -1);
            if (msgs.isEmpty() && zsc.isDelegatedRequest()) {
                throw ServiceException.PERM_DENIED("you do not have sufficient permissions");
            }
            // filter out IMAP \Deleted messages from the message lists
            Conversation conv = mbox.getConversationById(octxt, cid.getId());
            if (conv.isTagged(Flag.FlagInfo.DELETED)) {
                List<Message> raw = msgs;
                msgs = new ArrayList<Message>();
                for (Message msg : raw) {
                    if (!msg.isTagged(Flag.FlagInfo.DELETED)) {
                        msgs.add(msg);
                    }
                }
            }
            Element container = nest ? ToXML.encodeConversationSummary(response, ifmt, octxt, conv, CONVERSATION_FIELD_MASK) : response;
            SearchResponse builder = new SearchResponse(zsc, octxt, container, params);
            builder.setAllRead(conv.getUnreadCount() == 0);
            boolean more = putHits(octxt, ifmt, builder, msgs, results, params, conv);
            response.addAttribute(MailConstants.A_QUERY_MORE, more);
            // call me AFTER putHits since some of the <info> is generated by the getting of the hits!
            builder.add(results.getResultInfo());
        } finally {
            Closeables.closeQuietly(results);
        }
        return response;
    } else {
        // remote
        try {
            Element proxyRequest = zsc.createElement(MailConstants.SEARCH_CONV_REQUEST);
            Account target = Provisioning.getInstance().get(AccountBy.id, cid.getAccountId(), zsc.getAuthToken());
            if (target != null) {
                params.setInlineRule(params.getInlineRule().toLegacyExpandResults(target.getServer()));
            }
            params.encodeParams(proxyRequest);
            proxyRequest.addAttribute(MailConstants.A_NEST_MESSAGES, nest);
            proxyRequest.addAttribute(MailConstants.A_CONV_ID, cid.toString());
            // okay, lets run the search through the query parser -- this has the side-effect of
            // re-writing the query in a format that is OK to proxy to the other server -- since the
            // query has an "AND conv:remote-conv-id" part, the query parser will figure out the right
            // format for us.  TODO somehow make this functionality a bit more exposed in the
            // ZimbraQuery APIs...
            String rewrittenQueryString = mbox.getRewrittenQueryString(octxt, params);
            proxyRequest.addAttribute(MailConstants.E_QUERY, rewrittenQueryString, Element.Disposition.CONTENT);
            // proxy to remote account
            response = proxyRequest(proxyRequest, context, target.getId());
            return response.detach();
        } catch (SoapFaultException e) {
            throw ServiceException.FAILURE("SoapFaultException: ", e);
        }
    }
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) Account(com.zimbra.cs.account.Account) SearchParams(com.zimbra.cs.index.SearchParams) Message(com.zimbra.cs.mailbox.Message) ItemIdFormatter(com.zimbra.cs.service.util.ItemIdFormatter) SortBy(com.zimbra.cs.index.SortBy) Element(com.zimbra.common.soap.Element) Conversation(com.zimbra.cs.mailbox.Conversation) ItemId(com.zimbra.cs.service.util.ItemId) SoapFaultException(com.zimbra.common.soap.SoapFaultException) Mailbox(com.zimbra.cs.mailbox.Mailbox) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) ZimbraQueryResults(com.zimbra.cs.index.ZimbraQueryResults) SearchConvRequest(com.zimbra.soap.mail.message.SearchConvRequest)

Aggregations

ItemIdFormatter (com.zimbra.cs.service.util.ItemIdFormatter)63 Element (com.zimbra.common.soap.Element)56 Mailbox (com.zimbra.cs.mailbox.Mailbox)48 OperationContext (com.zimbra.cs.mailbox.OperationContext)46 ItemId (com.zimbra.cs.service.util.ItemId)44 ZimbraSoapContext (com.zimbra.soap.ZimbraSoapContext)44 Account (com.zimbra.cs.account.Account)21 ServiceException (com.zimbra.common.service.ServiceException)16 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)14 CalendarItem (com.zimbra.cs.mailbox.CalendarItem)11 Folder (com.zimbra.cs.mailbox.Folder)11 Message (com.zimbra.cs.mailbox.Message)11 HashMap (java.util.HashMap)11 MailItem (com.zimbra.cs.mailbox.MailItem)10 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)8 Mountpoint (com.zimbra.cs.mailbox.Mountpoint)7 IOException (java.io.IOException)7 ArrayList (java.util.ArrayList)7 MessagingException (javax.mail.MessagingException)7 MimeMessage (javax.mail.internet.MimeMessage)7