From 97bb065a9bf320320446a9c82d849c3fde47aafb Mon Sep 17 00:00:00 2001 From: mguessan Date: Fri, 23 Apr 2010 13:42:37 +0000 Subject: [PATCH] IMAP: use getRawInputStream instead of writeTo to avoid MIME message changes, cache message body in SharedByteArrayInputStream git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1018 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- .../davmail/exchange/ExchangeSession.java | 74 ++++++++++++++++--- src/java/davmail/imap/ImapConnection.java | 35 ++++++--- 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 9aacfb81..bdc9df6d 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -55,6 +55,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimePart; +import javax.mail.util.SharedByteArrayInputStream; import java.io.*; import java.net.HttpURLConnection; import java.net.NoRouteToHostException; @@ -1459,6 +1460,7 @@ public class ExchangeSession { /** * Get current folder messages imap uids + * * @return imap uid list */ public List getImapUidList() { @@ -1469,11 +1471,11 @@ public class ExchangeSession { return imapUidList; } - /** - * Calendar folder flag. - * - * @return true if this is a calendar folder - */ + /** + * Calendar folder flag. + * + * @return true if this is a calendar folder + */ public boolean isCalendar() { return "urn:content-classes:calendarfolder".equals(contentClass); } @@ -1491,6 +1493,7 @@ public class ExchangeSession { * drop cached message */ public void clearCache() { + messages.cachedMimeBody = null; messages.cachedMimeMessage = null; messages.cachedMessageImapUid = 0; } @@ -1562,6 +1565,11 @@ public class ExchangeSession { */ public boolean forwarded; + /** + * Unparsed message content. + */ + protected SharedByteArrayInputStream mimeBody; + /** * Message content parsed in a MIME message. */ @@ -1725,23 +1733,62 @@ public class ExchangeSession { * @throws IOException on error * @throws MessagingException on error */ - public MimeMessage getMimeMessage() throws IOException, MessagingException { + protected void loadMimeMessage() throws IOException, MessagingException { if (mimeMessage == null) { // try to get message content from cache if (this.imapUid == messageList.cachedMessageImapUid) { + mimeBody = messageList.cachedMimeBody; mimeMessage = messageList.cachedMimeMessage; - LOGGER.debug("Got message content for "+imapUid+" from cache"); + LOGGER.debug("Got message content for " + imapUid + " from cache"); } else { // load message ByteArrayOutputStream baos = new ByteArrayOutputStream(); write(baos, false); - mimeMessage = new MimeMessage(null, new ByteArrayInputStream(baos.toByteArray())); - LOGGER.debug("Downloaded message content for "+imapUid+" ("+baos.size()+ ')'); + // load and parse message + mimeBody = new SharedByteArrayInputStream(baos.toByteArray()); + mimeMessage = new MimeMessage(null, mimeBody); + LOGGER.debug("Downloaded message content for " + imapUid + " (" + baos.size() + ')'); } } + } + + /** + * Get message content as a Mime message. + * + * @return mime message + * @throws IOException on error + * @throws MessagingException on error + */ + public MimeMessage getMimeMessage() throws IOException, MessagingException { + loadMimeMessage(); return mimeMessage; } + /** + * Get message body size. + * @return mime message size + * @throws IOException on error + * @throws MessagingException on error + */ + public int getMimeMessageSize() throws IOException, MessagingException { + loadMimeMessage(); + mimeBody.reset(); + return mimeBody.available(); + } + + /** + * Get message body input stream. + * @return mime message InputStream + * @throws IOException on error + * @throws MessagingException on error + */ + public InputStream getRawInputStream() throws IOException, MessagingException { + loadMimeMessage(); + mimeBody.reset(); + return mimeBody; + } + + /** * Drop mime message to avoid keeping message content in memory, * keep a single message in MessageList cache to handle chunked fetch. @@ -1750,6 +1797,7 @@ public class ExchangeSession { // update single message cache if (mimeMessage != null) { messageList.cachedMessageImapUid = imapUid; + messageList.cachedMimeBody = mimeBody; messageList.cachedMimeMessage = mimeMessage; } mimeMessage = null; @@ -1829,6 +1877,10 @@ public class ExchangeSession { * Cached message uid. */ protected transient long cachedMessageImapUid; + /** + * Cached unparsed message + */ + protected transient SharedByteArrayInputStream cachedMimeBody; } @@ -2683,7 +2735,7 @@ public class ExchangeSession { event.getICS(); } catch (HttpException e) { // invalid event: exclude from list - LOGGER.warn("Invalid event "+event.displayName+" found at " + response.getHref(), e); + LOGGER.warn("Invalid event " + event.displayName + " found at " + response.getHref(), e); } } else { events.add(event); @@ -3050,7 +3102,7 @@ public class ExchangeSession { /** * Determine user email through various means. * - * @param hostName Exchange server host name for last failover + * @param hostName Exchange server host name for last failover */ public void buildEmail(String hostName) { // first try to get email from login name diff --git a/src/java/davmail/imap/ImapConnection.java b/src/java/davmail/imap/ImapConnection.java index cf3cc03c..c43c500f 100644 --- a/src/java/davmail/imap/ImapConnection.java +++ b/src/java/davmail/imap/ImapConnection.java @@ -31,6 +31,7 @@ import davmail.ui.tray.DavGatewayTray; import davmail.util.StringUtil; import org.apache.commons.httpclient.HttpException; +import javax.mail.BodyPart; import javax.mail.MessagingException; import javax.mail.internet.*; import java.io.*; @@ -40,7 +41,6 @@ import java.net.SocketTimeoutException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; -import java.util.logging.Logger; /** * Dav Gateway smtp connection implementation. @@ -568,12 +568,7 @@ public class ImapConnection extends AbstractConnection { if ("FLAGS".equals(param)) { buffer.append(" FLAGS (").append(message.getImapFlags()).append(')'); } else if ("RFC822.SIZE".equals(param)) { - MimeMessage mimeMessage = message.getMimeMessage(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PartialOutputStream partOutputStream = new PartialOutputStream(baos, 0, 0); - mimeMessage.writeTo(partOutputStream); - baos.close(); - buffer.append(" RFC822.SIZE ").append(partOutputStream.size); + buffer.append(" RFC822.SIZE ").append(message.getMimeMessageSize()); } else if ("ENVELOPE".equals(param)) { appendEnvelope(buffer, message); } else if ("BODYSTRUCTURE".equals(param)) { @@ -607,7 +602,9 @@ public class ImapConnection extends AbstractConnection { } } + InputStream partInputStream = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream partOutputStream; // load message MimeMessage mimeMessage = message.getMimeMessage(); @@ -615,13 +612,16 @@ public class ImapConnection extends AbstractConnection { String partIndexString = StringUtil.getToken(param, "[", "]"); if ("".equals(partIndexString)) { // write message with headers - mimeMessage.writeTo(new PartialOutputStream(baos, startIndex, maxSize)); + partOutputStream = new PartialOutputStream(baos, startIndex, maxSize); + partInputStream = message.getRawInputStream(); } else if ("TEXT".equals(partIndexString)) { // write message without headers - mimeMessage.writeTo(new PartOutputStream(baos, false, true, startIndex, maxSize)); + partOutputStream = new PartialOutputStream(baos, startIndex, maxSize); + partInputStream = mimeMessage.getRawInputStream(); } else if ("RFC822.HEADER".equals(param) || partIndexString.startsWith("HEADER")) { // write headers only - mimeMessage.writeTo(new PartOutputStream(baos, true, false, startIndex, maxSize)); + partOutputStream = new PartOutputStream(baos, true, false, startIndex, maxSize); + partInputStream = message.getRawInputStream(); } else { MimePart bodyPart = mimeMessage; String[] partIndexStrings = partIndexString.split("\\."); @@ -648,9 +648,22 @@ public class ImapConnection extends AbstractConnection { } // write selected part, without headers - bodyPart.writeTo(new PartOutputStream(baos, false, true, startIndex, maxSize)); + partOutputStream = new PartialOutputStream(baos, startIndex, maxSize); + if (bodyPart instanceof MimeMessage) { + partInputStream = ((MimeMessage)bodyPart).getRawInputStream(); + } else { + partInputStream = ((MimeBodyPart)bodyPart).getRawInputStream(); + } } + // copy selected content to baos + byte[] bytes = new byte[8192]; + int length; + while ((length = partInputStream.read(bytes)) > 0) { + partOutputStream.write(bytes, 0, length); + } + partInputStream.close(); + partOutputStream.close(); baos.close(); if ("RFC822.HEADER".equals(param)) {