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
This commit is contained in:
mguessan 2010-04-23 13:42:37 +00:00
parent 1b8fe7419d
commit 97bb065a9b
2 changed files with 87 additions and 22 deletions

View File

@ -55,6 +55,7 @@ import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimePart; import javax.mail.internet.MimePart;
import javax.mail.util.SharedByteArrayInputStream;
import java.io.*; import java.io.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.NoRouteToHostException; import java.net.NoRouteToHostException;
@ -1459,6 +1460,7 @@ public class ExchangeSession {
/** /**
* Get current folder messages imap uids * Get current folder messages imap uids
*
* @return imap uid list * @return imap uid list
*/ */
public List<Long> getImapUidList() { public List<Long> getImapUidList() {
@ -1469,11 +1471,11 @@ public class ExchangeSession {
return imapUidList; return imapUidList;
} }
/** /**
* Calendar folder flag. * Calendar folder flag.
* *
* @return true if this is a calendar folder * @return true if this is a calendar folder
*/ */
public boolean isCalendar() { public boolean isCalendar() {
return "urn:content-classes:calendarfolder".equals(contentClass); return "urn:content-classes:calendarfolder".equals(contentClass);
} }
@ -1491,6 +1493,7 @@ public class ExchangeSession {
* drop cached message * drop cached message
*/ */
public void clearCache() { public void clearCache() {
messages.cachedMimeBody = null;
messages.cachedMimeMessage = null; messages.cachedMimeMessage = null;
messages.cachedMessageImapUid = 0; messages.cachedMessageImapUid = 0;
} }
@ -1562,6 +1565,11 @@ public class ExchangeSession {
*/ */
public boolean forwarded; public boolean forwarded;
/**
* Unparsed message content.
*/
protected SharedByteArrayInputStream mimeBody;
/** /**
* Message content parsed in a MIME message. * Message content parsed in a MIME message.
*/ */
@ -1725,23 +1733,62 @@ public class ExchangeSession {
* @throws IOException on error * @throws IOException on error
* @throws MessagingException on error * @throws MessagingException on error
*/ */
public MimeMessage getMimeMessage() throws IOException, MessagingException { protected void loadMimeMessage() throws IOException, MessagingException {
if (mimeMessage == null) { if (mimeMessage == null) {
// try to get message content from cache // try to get message content from cache
if (this.imapUid == messageList.cachedMessageImapUid) { if (this.imapUid == messageList.cachedMessageImapUid) {
mimeBody = messageList.cachedMimeBody;
mimeMessage = messageList.cachedMimeMessage; mimeMessage = messageList.cachedMimeMessage;
LOGGER.debug("Got message content for "+imapUid+" from cache"); LOGGER.debug("Got message content for " + imapUid + " from cache");
} else { } else {
// load message // load message
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
write(baos, false); write(baos, false);
mimeMessage = new MimeMessage(null, new ByteArrayInputStream(baos.toByteArray())); // load and parse message
LOGGER.debug("Downloaded message content for "+imapUid+" ("+baos.size()+ ')'); 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; 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, * Drop mime message to avoid keeping message content in memory,
* keep a single message in MessageList cache to handle chunked fetch. * keep a single message in MessageList cache to handle chunked fetch.
@ -1750,6 +1797,7 @@ public class ExchangeSession {
// update single message cache // update single message cache
if (mimeMessage != null) { if (mimeMessage != null) {
messageList.cachedMessageImapUid = imapUid; messageList.cachedMessageImapUid = imapUid;
messageList.cachedMimeBody = mimeBody;
messageList.cachedMimeMessage = mimeMessage; messageList.cachedMimeMessage = mimeMessage;
} }
mimeMessage = null; mimeMessage = null;
@ -1829,6 +1877,10 @@ public class ExchangeSession {
* Cached message uid. * Cached message uid.
*/ */
protected transient long cachedMessageImapUid; protected transient long cachedMessageImapUid;
/**
* Cached unparsed message
*/
protected transient SharedByteArrayInputStream cachedMimeBody;
} }
@ -2683,7 +2735,7 @@ public class ExchangeSession {
event.getICS(); event.getICS();
} catch (HttpException e) { } catch (HttpException e) {
// invalid event: exclude from list // 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 { } else {
events.add(event); events.add(event);
@ -3050,7 +3102,7 @@ public class ExchangeSession {
/** /**
* Determine user email through various means. * 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) { public void buildEmail(String hostName) {
// first try to get email from login name // first try to get email from login name

View File

@ -31,6 +31,7 @@ import davmail.ui.tray.DavGatewayTray;
import davmail.util.StringUtil; import davmail.util.StringUtil;
import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpException;
import javax.mail.BodyPart;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.*; import javax.mail.internet.*;
import java.io.*; import java.io.*;
@ -40,7 +41,6 @@ import java.net.SocketTimeoutException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.logging.Logger;
/** /**
* Dav Gateway smtp connection implementation. * Dav Gateway smtp connection implementation.
@ -568,12 +568,7 @@ public class ImapConnection extends AbstractConnection {
if ("FLAGS".equals(param)) { if ("FLAGS".equals(param)) {
buffer.append(" FLAGS (").append(message.getImapFlags()).append(')'); buffer.append(" FLAGS (").append(message.getImapFlags()).append(')');
} else if ("RFC822.SIZE".equals(param)) { } else if ("RFC822.SIZE".equals(param)) {
MimeMessage mimeMessage = message.getMimeMessage(); buffer.append(" RFC822.SIZE ").append(message.getMimeMessageSize());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PartialOutputStream partOutputStream = new PartialOutputStream(baos, 0, 0);
mimeMessage.writeTo(partOutputStream);
baos.close();
buffer.append(" RFC822.SIZE ").append(partOutputStream.size);
} else if ("ENVELOPE".equals(param)) { } else if ("ENVELOPE".equals(param)) {
appendEnvelope(buffer, message); appendEnvelope(buffer, message);
} else if ("BODYSTRUCTURE".equals(param)) { } else if ("BODYSTRUCTURE".equals(param)) {
@ -607,7 +602,9 @@ public class ImapConnection extends AbstractConnection {
} }
} }
InputStream partInputStream = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream partOutputStream;
// load message // load message
MimeMessage mimeMessage = message.getMimeMessage(); MimeMessage mimeMessage = message.getMimeMessage();
@ -615,13 +612,16 @@ public class ImapConnection extends AbstractConnection {
String partIndexString = StringUtil.getToken(param, "[", "]"); String partIndexString = StringUtil.getToken(param, "[", "]");
if ("".equals(partIndexString)) { if ("".equals(partIndexString)) {
// write message with headers // 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)) { } else if ("TEXT".equals(partIndexString)) {
// write message without headers // 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")) { } else if ("RFC822.HEADER".equals(param) || partIndexString.startsWith("HEADER")) {
// write headers only // write headers only
mimeMessage.writeTo(new PartOutputStream(baos, true, false, startIndex, maxSize)); partOutputStream = new PartOutputStream(baos, true, false, startIndex, maxSize);
partInputStream = message.getRawInputStream();
} else { } else {
MimePart bodyPart = mimeMessage; MimePart bodyPart = mimeMessage;
String[] partIndexStrings = partIndexString.split("\\."); String[] partIndexStrings = partIndexString.split("\\.");
@ -648,9 +648,22 @@ public class ImapConnection extends AbstractConnection {
} }
// write selected part, without headers // 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(); baos.close();
if ("RFC822.HEADER".equals(param)) { if ("RFC822.HEADER".equals(param)) {