use of com.zimbra.cs.mime.Mime in project zm-mailbox by Zimbra.
the class MessageCache method getMimeMessage.
/**
* Returns a JavaMail {@link javax.mail.internet.MimeMessage}
* encapsulating the message content. If possible, TNEF and uuencoded
* attachments are expanded and their components are presented as
* standard MIME attachments. If TNEF or uuencode decoding fails, the
* MimeMessage wraps the raw message content.
*
* @return A MimeMessage wrapping the RFC822 content of the Message.
* @throws ServiceException when errors occur opening, reading,
* uncompressing, or parsing the message file,
* or when the file does not exist.
* @see #getRawContent()
* @see #getItemContent()
* @see com.zimbra.cs.mime.TnefConverter
* @see com.zimbra.cs.mime.UUEncodeConverter
*/
static MimeMessage getMimeMessage(MailItem item, boolean expand) throws ServiceException {
String digest = item.getDigest();
CacheNode cnode = null;
boolean cacheHit = true;
boolean newNode = false;
InputStream in = null;
int mboxId = item.getMailboxId();
boolean isEncrypted = false;
synchronized (sCache) {
cnode = sCache.get(digest);
if (cnode == null) {
newNode = true;
cnode = new CacheNode();
}
}
try {
if (cnode.message == null) {
sLog.debug("Loading MimeMessage for item %d.", item.getId());
cacheHit = false;
try {
in = fetchFromStore(item);
cnode.message = new Mime.FixedMimeMessage(JMSession.getSession(), in);
if (item.getSize() < MESSAGE_CACHE_DISK_STREAMING_THRESHOLD) {
cnode.size = item.getSize();
// Not the best place to increment the data size, but cacheItem()
// won't get called if we're expanding a message for an existing
// node.
sDataSize += cnode.size;
}
} finally {
ByteUtil.closeStream(in);
}
}
if (expand) {
sLog.debug("Expanding MimeMessage for item %d.", item.getId());
try {
MimeMessage decryptedMimeMessage = null;
if (item instanceof Message) {
// if the mime is encrypted; decrypt it first
if (cnode.message != null) {
isEncrypted = Mime.isEncrypted(cnode.message.getContentType());
}
if (isEncrypted) {
if (isSmimeFeatureToggled(item.getMailbox(), cnode)) {
sLog.debug("Smime feature is toggled. So remove old entry from smimeAccessInfo for mailboxId=%d and itemDigest=%s", mboxId, item.getDigest());
cnode.smimeAccessInfo.remove(mboxId);
}
if (cnode.expanded == null || !cnode.smimeAccessInfo.containsKey(mboxId)) {
cacheHit = false;
decryptedMimeMessage = doDecryption(item, cnode, mboxId);
}
}
}
// expand if the message has not yet been expanded or if the message is decrypted successfully
if (cnode.expanded == null || (decryptedMimeMessage != null && cnode.expanded != decryptedMimeMessage)) {
cacheHit = false;
expandMessage(item, cnode, decryptedMimeMessage);
}
} catch (Exception e) {
// if the conversion bombs for any reason, revert to the original
sLog.warn("MIME converter failed for message %d. Reverting to original.", item.getId(), e);
cnode.expanded = cnode.message;
}
}
if (newNode) {
cacheItem(digest, cnode);
}
} catch (IOException e) {
throw ServiceException.FAILURE("IOException while retrieving content for item " + item.getId(), e);
} catch (MessagingException e) {
throw ServiceException.FAILURE("MessagingException while creating MimeMessage for item " + item.getId(), e);
} finally {
ByteUtil.closeStream(in);
}
if (cacheHit) {
sLog.debug("Cache hit for item %d: digest=%s, expand=%b.", item.getId(), item.getDigest(), expand);
ZimbraPerf.COUNTER_MBOX_MSG_CACHE.increment(100);
} else {
sLog.debug("Cache miss for item %d: digest=%s, expand=%b.", item.getId(), item.getDigest(), expand);
ZimbraPerf.COUNTER_MBOX_MSG_CACHE.increment(0);
}
if (expand) {
if (isEncrypted && (!cnode.smimeAccessInfo.containsKey(mboxId) || cnode.smimeAccessInfo.get(mboxId) != null)) {
return cnode.message;
}
return cnode.expanded;
} else {
return cnode.message;
}
}
use of com.zimbra.cs.mime.Mime in project zm-mailbox by Zimbra.
the class ToXML method encodeMessageAsMPHelper.
/**
* Encodes a Message object into <m> element with <mp> elements for
* message body.
* @param parent The Element to add the new <tt><m></tt> to.
* @param ifmt The formatter to sue when serializing item ids.
* @param msg The Message to serialize.
* @param part If non-null, serialize this message/rfc822 subpart of
* the Message instead of the Message itself.
* @param maxSize TODO
* @param wantHTML <tt>true</tt> to prefer HTML parts as the "body",
* <tt>false</tt> to prefer text/plain parts.
* @param neuter Whether to rename "src" attributes on HTML <img> tags.
* @param headers Extra message headers to include in the returned element.
* @param serializeType If <tt>false</tt>, always serializes as an
* <tt><m></tt> element.
* @param bestEffort If <tt>true</tt>, errors serializing part content
* are swallowed.
* @return The newly-created <tt><m></tt> Element, which has already
* been added as a child to the passed-in <tt>parent</tt>.
* @throws ServiceException
*/
private static Element encodeMessageAsMPHelper(boolean bestEffort, Element parent, ItemIdFormatter ifmt, OperationContext octxt, Message msg, String part, int maxSize, boolean wantHTML, boolean neuter, Set<String> headers, boolean serializeType, boolean wantExpandGroupInfo, boolean encodeMissingBlobs, MsgContent wantContent, int fields) throws ServiceException {
Element m = null;
boolean success = false;
try {
boolean wholeMessage = part == null || part.trim().isEmpty();
m = encodeMsgCommonAndIdInfo(parent, ifmt, octxt, msg, part, serializeType, fields);
MimeMessage mm = null;
try {
String requestedAccountId = octxt.getmRequestedAccountId();
String authtokenAccountId = octxt.getmAuthTokenAccountId();
boolean isDecryptionNotAllowed = StringUtils.isNotEmpty(authtokenAccountId) && !authtokenAccountId.equalsIgnoreCase(requestedAccountId);
if (isDecryptionNotAllowed && Mime.isEncrypted(msg.getMimeMessage(false).getContentType())) {
mm = msg.getMimeMessage(false);
} else {
mm = msg.getMimeMessage();
}
} catch (MailServiceException e) {
if (encodeMissingBlobs && MailServiceException.NO_SUCH_BLOB.equals(e.getCode())) {
ZimbraLog.mailbox.error("Unable to get blob while encoding message", e);
encodeEmail(m, msg.getSender(), EmailType.FROM);
encodeEmail(m, msg.getSender(), EmailType.SENDER);
if (msg.getRecipients() != null) {
addEmails(m, Mime.parseAddressHeader(msg.getRecipients()), EmailType.TO);
}
m.addAttribute(MailConstants.A_SUBJECT, msg.getSubject());
Element mimePart = m.addNonUniqueElement(MailConstants.E_MIMEPART);
mimePart.addAttribute(MailConstants.A_PART, 1);
mimePart.addAttribute(MailConstants.A_BODY, true);
mimePart.addAttribute(MailConstants.A_CONTENT_TYPE, MimeConstants.CT_TEXT_PLAIN);
String errMsg = L10nUtil.getMessage(L10nUtil.MsgKey.errMissingBlob, msg.getAccount().getLocale(), ifmt.formatItemId(msg));
m.addAttribute(MailConstants.E_FRAG, errMsg, Element.Disposition.CONTENT);
mimePart.addAttribute(MailConstants.E_CONTENT, errMsg, Element.Disposition.CONTENT);
// not really success, but mark as such so the element is appended correctly
success = true;
return m;
}
throw e;
}
if (!wholeMessage) {
MimePart mp = Mime.getMimePart(mm, part);
if (mp == null) {
throw MailServiceException.NO_SUCH_PART(part);
}
Object content = Mime.getMessageContent(mp);
if (!(content instanceof MimeMessage)) {
throw MailServiceException.NO_SUCH_PART(part);
}
mm = (MimeMessage) content;
} else {
part = "";
}
// Add fragment before emails to maintain consistent ordering
// of elements with encodeConversation - need to do this to
// overcome JAXB issues.
String fragment = msg.getFragment();
if (fragment != null && !fragment.isEmpty()) {
m.addAttribute(MailConstants.E_FRAG, fragment, Element.Disposition.CONTENT);
}
addEmails(m, Mime.parseAddressHeader(mm, "From"), EmailType.FROM);
addEmails(m, Mime.parseAddressHeader(mm, "Sender"), EmailType.SENDER);
addEmails(m, Mime.parseAddressHeader(mm, "Reply-To"), EmailType.REPLY_TO);
addEmails(m, Mime.parseAddressHeader(mm, "To"), EmailType.TO);
addEmails(m, Mime.parseAddressHeader(mm, "Cc"), EmailType.CC);
addEmails(m, Mime.parseAddressHeader(mm, "Bcc"), EmailType.BCC);
addEmails(m, Mime.parseAddressHeader(mm.getHeader("Resent-From", null), false), EmailType.RESENT_FROM);
// read-receipts only get sent by the mailbox's owner
if (!(octxt.isDelegatedRequest(msg.getMailbox()) && octxt.isOnBehalfOfRequest(msg.getMailbox()))) {
addEmails(m, Mime.parseAddressHeader(mm, "Disposition-Notification-To"), EmailType.READ_RECEIPT);
}
String calIntendedFor = msg.getCalendarIntendedFor();
m.addAttribute(MailConstants.A_CAL_INTENDED_FOR, calIntendedFor);
String subject = Mime.getSubject(mm);
if (subject != null) {
m.addAttribute(MailConstants.E_SUBJECT, StringUtil.stripControlCharacters(subject), Element.Disposition.CONTENT);
}
String messageID = mm.getMessageID();
if (messageID != null && !messageID.trim().isEmpty()) {
m.addAttribute(MailConstants.E_MSG_ID_HDR, StringUtil.stripControlCharacters(messageID), Element.Disposition.CONTENT);
}
if (wholeMessage && msg.isDraft()) {
if (!msg.getDraftOrigId().isEmpty()) {
ItemId origId = new ItemId(msg.getDraftOrigId(), msg.getMailbox().getAccountId());
m.addAttribute(MailConstants.A_ORIG_ID, ifmt.formatItemId(origId));
}
if (!msg.getDraftReplyType().isEmpty()) {
m.addAttribute(MailConstants.A_REPLY_TYPE, msg.getDraftReplyType());
}
if (!msg.getDraftIdentityId().isEmpty()) {
m.addAttribute(MailConstants.A_IDENTITY_ID, msg.getDraftIdentityId());
}
if (!msg.getDraftAccountId().isEmpty()) {
m.addAttribute(MailConstants.A_FOR_ACCOUNT, msg.getDraftAccountId());
}
String inReplyTo = mm.getHeader("In-Reply-To", null);
if (inReplyTo != null && !inReplyTo.isEmpty()) {
m.addAttribute(MailConstants.E_IN_REPLY_TO, StringUtil.stripControlCharacters(inReplyTo), Element.Disposition.CONTENT);
}
if (msg.getDraftAutoSendTime() != 0) {
m.addAttribute(MailConstants.A_AUTO_SEND_TIME, msg.getDraftAutoSendTime());
}
}
if (!wholeMessage) {
m.addAttribute(MailConstants.A_SIZE, mm.getSize());
}
Date sent = mm.getSentDate();
if (sent != null) {
m.addAttribute(MailConstants.A_SENT_DATE, sent.getTime());
}
Calendar resent = DateUtil.parseRFC2822DateAsCalendar(mm.getHeader("Resent-Date", null));
if (resent != null) {
m.addAttribute(MailConstants.A_RESENT_DATE, resent.getTimeInMillis());
}
if (msg.isInvite() && msg.hasCalendarItemInfos()) {
encodeInvitesForMessage(m, ifmt, octxt, msg, NOTIFY_FIELDS, neuter);
}
if (headers != null) {
for (String name : headers) {
String[] values = mm.getHeader(name);
if (values != null) {
for (int i = 0; i < values.length; i++) {
m.addKeyValuePair(name, values[i], MailConstants.A_HEADER, MailConstants.A_ATTRIBUTE_NAME);
}
}
}
}
List<MPartInfo> parts = Mime.getParts(mm, getDefaultCharset(msg));
if (parts != null && !parts.isEmpty()) {
Set<MPartInfo> bodies = Mime.getBody(parts, wantHTML);
addParts(m, parts.get(0), bodies, part, maxSize, neuter, false, getDefaultCharset(msg), bestEffort, wantContent);
}
if (wantExpandGroupInfo) {
ZimbraLog.gal.trace("want expand group info");
Account authedAcct = octxt.getAuthenticatedUser();
Account requestedAcct = msg.getMailbox().getAccount();
encodeAddrsWithGroupInfo(m, requestedAcct, authedAcct);
} else {
ZimbraLog.gal.trace("do not want expand group info");
}
success = true;
// update crypto flags - isSigned/isEncrypted
if (SmimeHandler.getHandler() != null) {
if (!wholeMessage) {
// check content type of attachment message for encryption flag
SmimeHandler.getHandler().updateCryptoFlags(msg, m, mm, mm);
} else {
MimeMessage originalMimeMessage = msg.getMimeMessage(false);
SmimeHandler.getHandler().updateCryptoFlags(msg, m, originalMimeMessage, mm);
}
}
// if the mime it is signed
if (Mime.isMultipartSigned(mm.getContentType()) || Mime.isPKCS7Signed(mm.getContentType())) {
ZimbraLog.mailbox.debug("The message is signed. Forwarding it to SmimeHandler for signature verification.");
if (SmimeHandler.getHandler() != null) {
SmimeHandler.getHandler().verifyMessageSignature(msg, m, mm, octxt);
}
} else {
// decoded and stored in cache as plain mime
if ((mm instanceof Mime.FixedMimeMessage) && ((Mime.FixedMimeMessage) mm).isPKCS7Signed()) {
if (SmimeHandler.getHandler() != null) {
SmimeHandler.getHandler().addPKCS7SignedMessageSignatureDetails(msg.getMailbox().getAccount(), m, mm, octxt.getmResponseProtocol());
}
}
}
return m;
} catch (IOException ex) {
throw ServiceException.FAILURE(ex.getMessage(), ex);
} catch (MessagingException ex) {
throw ServiceException.FAILURE(ex.getMessage(), ex);
} finally {
// don't leave turds around if we're going to retry
if (!success && !bestEffort && m != null) {
m.detach();
}
}
}
Aggregations