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