use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.
the class CalendarRequest method sendCalendarMessageInternal.
/**
* Send an iCalendar email message and optionally create/update/cancel
* corresponding appointment/invite in sender's calendar.
* @param zsc
* @param apptFolderId
* @param acct
* @param mbox
* @param csd
* @param response
* @param updateOwnAppointment if true, corresponding change is made to
* sender's calendar
* @return
* @throws ServiceException
*/
private static Element sendCalendarMessageInternal(ZimbraSoapContext zsc, OperationContext octxt, int apptFolderId, Account acct, Mailbox mbox, CalSendData csd, Element response, boolean updateOwnAppointment, boolean forceSend, MailSendQueue sendQueue) throws ServiceException {
boolean onBehalfOf = isOnBehalfOfRequest(zsc);
boolean notifyOwner = onBehalfOf && acct.getBooleanAttr(Provisioning.A_zimbraPrefCalendarNotifyDelegatedChanges, false);
if (notifyOwner) {
try {
InternetAddress addr = AccountUtil.getFriendlyEmailAddress(acct);
csd.mMm.addRecipient(javax.mail.Message.RecipientType.TO, addr);
} catch (MessagingException e) {
throw ServiceException.FAILURE("count not add calendar owner to recipient list", e);
}
}
// in a non-delegated request.
if (!onBehalfOf) {
String[] aliases = acct.getMailAlias();
String[] addrs;
if (aliases != null && aliases.length > 0) {
addrs = new String[aliases.length + 1];
addrs[0] = acct.getAttr(Provisioning.A_mail);
for (int i = 0; i < aliases.length; i++) {
addrs[i + 1] = aliases[i];
}
} else {
addrs = new String[1];
addrs[0] = acct.getAttr(Provisioning.A_mail);
}
try {
Mime.removeRecipients(csd.mMm, addrs);
} catch (MessagingException e) {
}
}
ParsedMessage pm = new ParsedMessage(csd.mMm, false);
if (csd.mInvite.getFragment() == null || csd.mInvite.getFragment().equals("")) {
csd.mInvite.setFragment(pm.getFragment(acct.getLocale()));
}
boolean willNotify = false;
if (!csd.mDontNotifyAttendees) {
try {
Address[] rcpts = csd.mMm.getAllRecipients();
willNotify = rcpts != null && rcpts.length > 0;
} catch (MessagingException e) {
throw ServiceException.FAILURE("Checking recipients of outgoing msg ", e);
}
}
// Validate the addresses first.
if (!csd.mInvite.isCancel() && !forceSend && willNotify) {
try {
MailUtil.validateRcptAddresses(JMSession.getSmtpSession(mbox.getAccount()), csd.mMm.getAllRecipients());
} catch (MessagingException mex) {
if (mex instanceof SendFailedException) {
SendFailedException sfex = (SendFailedException) mex;
throw MailServiceException.SEND_ABORTED_ADDRESS_FAILURE("invalid addresses", sfex, sfex.getInvalidAddresses(), sfex.getValidUnsentAddresses());
}
}
}
AddInviteData aid = null;
File tempMmFile = null;
boolean queued = false;
try {
if (willNotify) {
// Write out the MimeMessage to a temp file and create a new MimeMessage from the file.
// If we don't do this, we get into trouble during modify appointment call. If the blob
// is bigger than the streaming threshold (e.g. appointment has a big attachment), the
// MimeMessage object is attached to the current blob file. But the Mailbox.addInvite()
// call below updates the blob to a new mod_content (hence new path). The attached blob
// thus having been deleted, the MainSender.sendMimeMessage() call that follows will attempt
// to read from a non-existent file and fail. We can avoid this situation by writing the
// to-be-emailed mime message to a temp file, thus detaching it from the appointment's
// current blob file. This is inefficient, but safe.
OutputStream os = null;
InputStream is = null;
try {
tempMmFile = File.createTempFile("zcal", "tmp");
os = new FileOutputStream(tempMmFile);
csd.mMm.writeTo(os);
ByteUtil.closeStream(os);
os = null;
is = new ZSharedFileInputStream(tempMmFile);
csd.mMm = new FixedMimeMessage(JMSession.getSmtpSession(acct), is);
} catch (IOException e) {
if (tempMmFile != null)
tempMmFile.delete();
throw ServiceException.FAILURE("error creating calendar message content", e);
} catch (MessagingException e) {
if (tempMmFile != null)
tempMmFile.delete();
throw ServiceException.FAILURE("error creating calendar message content", e);
} finally {
ByteUtil.closeStream(os);
ByteUtil.closeStream(is);
}
}
// because email send will delete uploaded attachments as a side-effect.
if (updateOwnAppointment) {
aid = mbox.addInvite(octxt, csd.mInvite, apptFolderId, pm);
}
// Next, notify any attendees.
if (willNotify) {
MailSendQueueEntry entry = new MailSendQueueEntry(octxt, mbox, csd, tempMmFile);
sendQueue.add(entry);
queued = true;
}
} finally {
// Delete the temp file if it wasn't queued.
if (tempMmFile != null && !queued) {
tempMmFile.delete();
}
}
if (updateOwnAppointment && response != null && aid != null) {
csd.mAddInvData = aid;
ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
String id = ifmt.formatItemId(aid.calItemId);
response.addAttribute(MailConstants.A_CAL_ID, id);
if (csd.mInvite.isEvent())
// for backward compat
response.addAttribute(MailConstants.A_APPT_ID_DEPRECATE_ME, id);
response.addAttribute(MailConstants.A_CAL_INV_ID, ifmt.formatItemId(aid.calItemId, aid.invId));
if (Invite.isOrganizerMethod(csd.mInvite.getMethod())) {
response.addAttribute(MailConstants.A_MODIFIED_SEQUENCE, aid.modSeq);
response.addAttribute(MailConstants.A_REVISION, aid.rev);
}
}
return response;
}
use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.
the class XmlFormatter method formatCallback.
@Override
public void formatCallback(UserServletContext context) throws ServiceException, IOException {
Element elt = getFactory().createElement("items");
ItemIdFormatter ifmt = new ItemIdFormatter(context.getAuthAccount(), context.targetMailbox, false);
Iterator<? extends MailItem> iterator = null;
try {
long start = context.getStartTime();
long end = context.getEndTime();
boolean hasTimeRange = start != TIME_UNSPECIFIED && end != TIME_UNSPECIFIED;
iterator = getMailItems(context, start, end, Integer.MAX_VALUE);
// this is lame
while (iterator.hasNext()) {
MailItem item = iterator.next();
if (item instanceof CalendarItem && hasTimeRange) {
// Skip appointments that have no instance in the time range.
CalendarItem calItem = (CalendarItem) item;
Collection<Instance> instances = calItem.expandInstances(start, end, false);
if (instances.isEmpty())
continue;
}
ToXML.encodeItem(elt, ifmt, context.opContext, item, ToXML.NOTIFY_FIELDS);
}
context.resp.getOutputStream().write(elt.toUTF8());
} finally {
if (iterator instanceof QueryResultIterator)
((QueryResultIterator) iterator).finished();
}
}
use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.
the class AddMsg 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);
Element msgElem = request.getElement(MailConstants.E_MSG);
String flagsStr = msgElem.getAttribute(MailConstants.A_FLAGS, null);
String[] tags = TagUtil.parseTags(msgElem, mbox, octxt);
String folderStr = msgElem.getAttribute(MailConstants.A_FOLDER);
boolean noICal = msgElem.getAttributeBool(MailConstants.A_NO_ICAL, false);
long date = msgElem.getAttributeLong(MailConstants.A_DATE, System.currentTimeMillis());
boolean filterSent = request.getAttributeBool(MailConstants.A_FILTER_SENT, false);
if (mLog.isDebugEnabled()) {
StringBuffer toPrint = new StringBuffer("<AddMsg ");
if (tags != null) {
toPrint.append(" tags=\"").append(TagUtil.encodeTags(tags)).append("\"");
}
if (folderStr != null) {
toPrint.append(" folder=\"").append(folderStr).append("\"");
}
toPrint.append(">");
mLog.debug(toPrint);
}
int folderId = -1;
Folder folder = null;
try {
folderId = new ItemId(folderStr, zsc).getId();
} catch (ServiceException e) {
}
if (folderId > 0) {
folder = mbox.getFolderById(octxt, folderId);
} else {
folder = mbox.getFolderByPath(octxt, folderStr);
folderId = folder.getId();
}
mLog.debug("folder = %s", folder.getName());
// check to see whether the entire message has been uploaded under separate cover
String attachment = msgElem.getAttribute(MailConstants.A_ATTACHMENT_ID, null);
ParseMimeMessage.MimeMessageData mimeData = new ParseMimeMessage.MimeMessageData();
Message msg;
try {
MimeMessage mm;
if (attachment != null) {
mm = SendMsg.parseUploadedMessage(zsc, attachment, mimeData);
} else {
mm = ParseMimeMessage.importMsgSoap(msgElem);
}
int flagsBitMask = Flag.toBitmask(flagsStr);
ParsedMessage pm = new ParsedMessage(mm, date, mbox.attachmentsIndexingEnabled());
if (filterSent && !DebugConfig.disableOutgoingFilter && folderId == MailSender.getSentFolderId(mbox) && (flagsBitMask & Flag.BITMASK_FROM_ME) != 0) {
List<ItemId> addedItemIds = RuleManager.applyRulesToOutgoingMessage(octxt, mbox, pm, folderId, noICal, flagsBitMask, tags, Mailbox.ID_AUTO_INCREMENT);
msg = addedItemIds.isEmpty() ? null : mbox.getMessageById(octxt, addedItemIds.get(0).getId());
} else {
DeliveryOptions dopt = new DeliveryOptions().setFolderId(folderId).setNoICal(noICal).setFlags(flagsBitMask).setTags(tags);
msg = mbox.addMessage(octxt, pm, dopt, null);
}
} catch (IOException ioe) {
throw ServiceException.FAILURE("Error While Delivering Message", ioe);
} finally {
// purge the messages fetched from other servers.
if (mimeData.fetches != null) {
FileUploadServlet.deleteUploads(mimeData.fetches);
}
}
// we can now purge the uploaded attachments
if (mimeData.uploads != null) {
FileUploadServlet.deleteUploads(mimeData.uploads);
}
Element response = zsc.createElement(MailConstants.ADD_MSG_RESPONSE);
if (msg != null) {
ToXML.encodeMessageSummary(response, ifmt, octxt, msg, null, GetMsgMetadata.SUMMARY_FIELDS);
}
return response;
}
use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.
the class SaveDraftTest method deleteRace.
@Test
public void deleteRace() throws Exception {
Account acct = Provisioning.getInstance().getAccountByName("test@zimbra.com");
// create a draft via SOAP
Element request = new Element.JSONElement(MailConstants.SAVE_DRAFT_REQUEST);
Element m = request.addElement(MailConstants.E_MSG).addAttribute(MailConstants.E_SUBJECT, "dinner appt");
m.addUniqueElement(MailConstants.E_MIMEPART).addAttribute(MailConstants.A_CONTENT_TYPE, "text/plain").addAttribute(MailConstants.E_CONTENT, ORIGINAL_CONTENT);
Element response = new SaveDraft() {
@Override
protected Element generateResponse(ZimbraSoapContext zsc, ItemIdFormatter ifmt, OperationContext octxt, Mailbox mbox, Message msg) {
// trigger the failure case by deleting the draft before it's serialized out
try {
mbox.delete(null, msg.getId(), MailItem.Type.MESSAGE);
} catch (Exception e) {
return null;
}
return super.generateResponse(zsc, ifmt, octxt, mbox, msg);
}
}.handle(request, ServiceTestUtil.getRequestContext(acct));
// make sure the response has no <m> element
Assert.assertNull("picked up delete", response.getOptionalElement(MailConstants.E_MSG));
}
use of com.zimbra.cs.service.util.ItemIdFormatter in project zm-mailbox by Zimbra.
the class FolderAction method handleFolder.
private String handleFolder(Map<String, Object> context, Element request, String operation, Element result) throws ServiceException {
Element action = request.getElement(MailConstants.E_ACTION);
ZimbraSoapContext zsc = getZimbraSoapContext(context);
Mailbox mbox = getRequestedMailbox(zsc);
OperationContext octxt = getOperationContext(zsc, context);
ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
ItemId iid = new ItemId(action.getAttribute(MailConstants.A_ID), zsc);
if (operation.equals(OP_EMPTY)) {
boolean subfolders = action.getAttributeBool(MailConstants.A_RECURSIVE, true);
mbox.emptyFolder(octxt, iid.getId(), subfolders);
// empty trash means also to purge all IMAP \Deleted messages
if (iid.getId() == Mailbox.ID_FOLDER_TRASH)
mbox.purgeImapDeleted(octxt);
} else if (operation.equals(OP_REFRESH)) {
mbox.synchronizeFolder(octxt, iid.getId());
} else if (operation.equals(OP_IMPORT)) {
String url = action.getAttribute(MailConstants.A_URL);
mbox.importFeed(octxt, iid.getId(), url, false);
} else if (operation.equals(OP_FREEBUSY)) {
boolean fb = action.getAttributeBool(MailConstants.A_EXCLUDE_FREEBUSY, false);
mbox.alterTag(octxt, iid.getId(), MailItem.Type.FOLDER, Flag.FlagInfo.EXCLUDE_FREEBUSY, fb, null);
FreeBusyProvider.mailboxChanged(zsc.getRequestedAccountId());
} else if (operation.equals(OP_CHECK) || operation.equals(OP_UNCHECK)) {
mbox.alterTag(octxt, iid.getId(), MailItem.Type.FOLDER, Flag.FlagInfo.CHECKED, operation.equals(OP_CHECK), null);
} else if (operation.equals(OP_SET_URL)) {
String url = action.getAttribute(MailConstants.A_URL, "");
mbox.setFolderUrl(octxt, iid.getId(), url);
if (!url.equals("")) {
mbox.synchronizeFolder(octxt, iid.getId());
}
if (action.getAttribute(MailConstants.A_EXCLUDE_FREEBUSY, null) != null) {
boolean fb = action.getAttributeBool(MailConstants.A_EXCLUDE_FREEBUSY, false);
mbox.alterTag(octxt, iid.getId(), MailItem.Type.FOLDER, Flag.FlagInfo.EXCLUDE_FREEBUSY, fb, null);
}
} else if (operation.equals(OP_REVOKE)) {
String zid = action.getAttribute(MailConstants.A_ZIMBRA_ID);
mbox.revokeAccess(octxt, iid.getId(), zid);
} else if (operation.equals(OP_GRANT)) {
Element grant = action.getElement(MailConstants.E_GRANT);
short rights = ACL.stringToRights(grant.getAttribute(MailConstants.A_RIGHTS));
byte gtype = ACL.stringToType(grant.getAttribute(MailConstants.A_GRANT_TYPE));
String zid = grant.getAttribute(MailConstants.A_ZIMBRA_ID, null);
long expiry = grant.getAttributeLong(MailConstants.A_EXPIRY, 0);
String secret = null;
NamedEntry nentry = null;
if (gtype == ACL.GRANTEE_AUTHUSER) {
zid = GuestAccount.GUID_AUTHUSER;
} else if (gtype == ACL.GRANTEE_PUBLIC) {
zid = GuestAccount.GUID_PUBLIC;
expiry = validateGrantExpiry(grant.getAttribute(MailConstants.A_EXPIRY, null), AccountUtil.getMaxPublicShareLifetime(mbox.getAccount(), mbox.getFolderById(octxt, iid.getId()).getDefaultView()));
} else if (gtype == ACL.GRANTEE_GUEST) {
zid = grant.getAttribute(MailConstants.A_DISPLAY);
if (zid == null || zid.indexOf('@') < 0)
throw ServiceException.INVALID_REQUEST("invalid guest id or password", null);
// first make sure they didn't accidentally specify "guest" instead of "usr"
boolean guestGrantee = true;
try {
nentry = lookupGranteeByName(zid, ACL.GRANTEE_USER, zsc);
if (nentry instanceof MailTarget) {
Domain domain = Provisioning.getInstance().getDomain(mbox.getAccount());
String granteeDomainName = ((MailTarget) nentry).getDomainName();
if (domain.isInternalSharingCrossDomainEnabled() || domain.getName().equals(granteeDomainName) || Sets.newHashSet(domain.getInternalSharingDomain()).contains(granteeDomainName)) {
guestGrantee = false;
zid = nentry.getId();
gtype = nentry instanceof Group ? ACL.GRANTEE_GROUP : ACL.GRANTEE_USER;
}
}
} catch (ServiceException e) {
// this is the normal path, where lookupGranteeByName throws account.NO_SUCH_USER
}
if (guestGrantee) {
secret = grant.getAttribute(MailConstants.A_ARGS, null);
// password is no longer required for external sharing
if (secret == null) {
secret = grant.getAttribute(MailConstants.A_PASSWORD, null);
}
}
} else if (gtype == ACL.GRANTEE_KEY) {
zid = grant.getAttribute(MailConstants.A_DISPLAY);
// unlike guest, we do not require the display name to be an email address
// unlike guest, we do not fixup grantee type for key grantees if they specify an internal user
// get the optional accesskey
secret = grant.getAttribute(MailConstants.A_ACCESSKEY, null);
} else if (zid != null) {
nentry = lookupGranteeByZimbraId(zid, gtype);
} else {
try {
nentry = lookupGranteeByName(grant.getAttribute(MailConstants.A_DISPLAY), gtype, zsc);
zid = nentry.getId();
// make sure they didn't accidentally specify "usr" instead of "grp"
if (gtype == ACL.GRANTEE_USER && nentry instanceof Group) {
gtype = ACL.GRANTEE_GROUP;
}
} catch (ServiceException e) {
if (AccountServiceException.NO_SUCH_ACCOUNT.equals(e.getCode())) {
// looks like the case of an internal user not provisioned yet
// we'll treat it as external sharing
gtype = ACL.GRANTEE_GUEST;
zid = grant.getAttribute(MailConstants.A_DISPLAY);
} else {
throw e;
}
}
}
ACL.Grant g = mbox.grantAccess(octxt, iid.getId(), zid, gtype, rights, secret, expiry);
// kinda hacky -- return the zimbra id and name of the grantee in the response
result.addAttribute(MailConstants.A_ZIMBRA_ID, zid);
if (nentry != null)
result.addAttribute(MailConstants.A_DISPLAY, nentry.getName());
else if (gtype == ACL.GRANTEE_GUEST || gtype == ACL.GRANTEE_KEY)
result.addAttribute(MailConstants.A_DISPLAY, zid);
if (gtype == ACL.GRANTEE_KEY)
result.addAttribute(MailConstants.A_ACCESSKEY, g.getPassword());
} else if (operation.equals(OP_REVOKEORPHANGRANTS)) {
String zid = action.getAttribute(MailConstants.A_ZIMBRA_ID);
byte gtype = ACL.stringToType(action.getAttribute(MailConstants.A_GRANT_TYPE));
revokeOrphanGrants(octxt, mbox, iid, zid, gtype);
} else if (operation.equals(OP_UPDATE)) {
// duplicating code from ItemAction.java for now...
String newName = action.getAttribute(MailConstants.A_NAME, null);
String folderId = action.getAttribute(MailConstants.A_FOLDER, null);
ItemId iidFolder = new ItemId(folderId == null ? "-1" : folderId, zsc);
if (!iidFolder.belongsTo(mbox)) {
throw ServiceException.INVALID_REQUEST("cannot move folder between mailboxes", null);
} else if (folderId != null && iidFolder.getId() <= 0) {
throw MailServiceException.NO_SUCH_FOLDER(iidFolder.getId());
}
String flags = action.getAttribute(MailConstants.A_FLAGS, null);
byte color = (byte) action.getAttributeLong(MailConstants.A_COLOR, -1);
String view = action.getAttribute(MailConstants.A_DEFAULT_VIEW, null);
Element eAcl = action.getOptionalElement(MailConstants.E_ACL);
ACL acl = null;
if (eAcl != null) {
acl = parseACL(eAcl, view == null ? mbox.getFolderById(octxt, iid.getId()).getDefaultView() : MailItem.Type.of(view), mbox.getAccount());
}
if (color >= 0) {
mbox.setColor(octxt, iid.getId(), MailItem.Type.FOLDER, color);
}
if (acl != null) {
mbox.setPermissions(octxt, iid.getId(), acl);
}
if (flags != null) {
mbox.setTags(octxt, iid.getId(), MailItem.Type.FOLDER, Flag.toBitmask(flags), null, null);
}
if (view != null) {
mbox.setFolderDefaultView(octxt, iid.getId(), MailItem.Type.of(view));
}
if (newName != null) {
mbox.rename(octxt, iid.getId(), MailItem.Type.FOLDER, newName, iidFolder.getId());
} else if (iidFolder.getId() > 0) {
mbox.move(octxt, iid.getId(), MailItem.Type.FOLDER, iidFolder.getId(), null);
}
} else if (operation.equals(OP_SYNCON) || operation.equals(OP_SYNCOFF)) {
mbox.alterTag(octxt, iid.getId(), MailItem.Type.FOLDER, Flag.FlagInfo.SYNC, operation.equals(OP_SYNCON), null);
} else if (operation.equals(OP_RETENTIONPOLICY)) {
mbox.setRetentionPolicy(octxt, iid.getId(), MailItem.Type.FOLDER, new RetentionPolicy(action.getElement(MailConstants.E_RETENTION_POLICY)));
} else if (operation.equals(OP_DISABLE_ACTIVESYNC) || operation.equals(OP_ENABLE_ACTIVESYNC)) {
mbox.setActiveSyncDisabled(octxt, iid.getId(), operation.equals(OP_DISABLE_ACTIVESYNC));
} else if (operation.equals(OP_WEBOFFLINESYNCDAYS)) {
mbox.setFolderWebOfflineSyncDays(octxt, iid.getId(), action.getAttributeInt(MailConstants.A_NUM_DAYS));
} else {
throw ServiceException.INVALID_REQUEST("unknown operation: " + operation, null);
}
return ifmt.formatItemId(iid);
}
Aggregations