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:
parent
1b8fe7419d
commit
97bb065a9b
|
@ -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
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
Loading…
Reference in New Issue