mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-13 11:12:22 -05:00
full inline images rewrite, merge information from htmlBody and OWA generated page to build inline image list
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@14 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
98868aa199
commit
f9b1321101
@ -535,7 +535,7 @@ public class ExchangeSession {
|
|||||||
public String subject;
|
public String subject;
|
||||||
public String priority;
|
public String priority;
|
||||||
|
|
||||||
protected Map<String, String> attachmentsMap;
|
protected Map<String, Attachment> attachmentsMap;
|
||||||
|
|
||||||
// attachment index used during write
|
// attachment index used during write
|
||||||
protected int attachmentIndex;
|
protected int attachmentIndex;
|
||||||
@ -628,7 +628,7 @@ public class ExchangeSession {
|
|||||||
|
|
||||||
// exchange message : create mime part headers
|
// exchange message : create mime part headers
|
||||||
if (boundary != null) {
|
if (boundary != null) {
|
||||||
attachmentsMap = getAttachmentsUrls(messageUrl);
|
attachmentsMap = getAttachments(messageUrl);
|
||||||
// TODO : test actual header values
|
// TODO : test actual header values
|
||||||
result.append("\n--").append(boundary)
|
result.append("\n--").append(boundary)
|
||||||
.append("\nContent-Type: text/html")
|
.append("\nContent-Type: text/html")
|
||||||
@ -642,7 +642,8 @@ public class ExchangeSession {
|
|||||||
parsedAttachmentIndex = Integer.parseInt(attachmentName);
|
parsedAttachmentIndex = Integer.parseInt(attachmentName);
|
||||||
} catch (Exception e) {/* ignore */}
|
} catch (Exception e) {/* ignore */}
|
||||||
if (parsedAttachmentIndex == 0) {
|
if (parsedAttachmentIndex == 0) {
|
||||||
String attachmentContentType = getAttachmentContentType(attachmentsMap.get(attachmentName));
|
Attachment attachment = attachmentsMap.get(attachmentName);
|
||||||
|
String attachmentContentType = getAttachmentContentType(attachment.href);
|
||||||
String attachmentContentEncoding = "base64";
|
String attachmentContentEncoding = "base64";
|
||||||
if (attachmentContentType.startsWith("text/")) {
|
if (attachmentContentType.startsWith("text/")) {
|
||||||
attachmentContentEncoding = "quoted-printable";
|
attachmentContentEncoding = "quoted-printable";
|
||||||
@ -655,22 +656,12 @@ public class ExchangeSession {
|
|||||||
.append(attachmentContentType)
|
.append(attachmentContentType)
|
||||||
.append(";")
|
.append(";")
|
||||||
.append("\n\tname=\"").append(attachmentName).append("\"");
|
.append("\n\tname=\"").append(attachmentName).append("\"");
|
||||||
int attachmentIdStartIndex = htmlBody.indexOf("cid:" + attachmentName);
|
if (attachment.contentid != null) {
|
||||||
if (attachmentIdStartIndex > 0) {
|
|
||||||
int attachmentIdEndIndex = htmlBody.indexOf('"', attachmentIdStartIndex);
|
|
||||||
if (attachmentIdEndIndex > 0) {
|
|
||||||
result.append("\nContent-ID: <")
|
|
||||||
.append(htmlBody.substring(attachmentIdStartIndex + 4, attachmentIdEndIndex))
|
|
||||||
.append(">");
|
|
||||||
}
|
|
||||||
} else if (htmlBody.indexOf("src=\"1_multipart/" + attachmentName) >= 0) {
|
|
||||||
// detect html body without cid in image link
|
|
||||||
result.append("\nContent-ID: <")
|
result.append("\nContent-ID: <")
|
||||||
.append(attachmentName)
|
.append(attachment.contentid)
|
||||||
.append(">");
|
.append(">");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result.append("\nContent-Transfer-Encoding: ").append(attachmentContentEncoding)
|
result.append("\nContent-Transfer-Encoding: ").append(attachmentContentEncoding)
|
||||||
.append("\n\n");
|
.append("\n\n");
|
||||||
}
|
}
|
||||||
@ -708,13 +699,13 @@ public class ExchangeSession {
|
|||||||
} else {
|
} else {
|
||||||
attachmentIndex = 0;
|
attachmentIndex = 0;
|
||||||
|
|
||||||
attachmentsMap = getAttachmentsUrls(messageUrl);
|
attachmentsMap = getAttachments(messageUrl);
|
||||||
writeMimeMessage(reader, os, mimeHeader, attachmentsMap);
|
writeMimeMessage(reader, os, mimeHeader, attachmentsMap);
|
||||||
}
|
}
|
||||||
os.flush();
|
os.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeMimeMessage(BufferedReader reader, OutputStream os, MimeHeader mimeHeader, Map<String, String> attachmentsMap) throws IOException {
|
public void writeMimeMessage(BufferedReader reader, OutputStream os, MimeHeader mimeHeader, Map<String, Attachment> attachmentsMap) throws IOException {
|
||||||
String line;
|
String line;
|
||||||
// with alternative, there are two body forms (plain+html)
|
// with alternative, there are two body forms (plain+html)
|
||||||
if ("multipart/alternative".equals(mimeHeader.contentType)) {
|
if ("multipart/alternative".equals(mimeHeader.contentType)) {
|
||||||
@ -743,10 +734,10 @@ public class ExchangeSession {
|
|||||||
attachmentIndex++;
|
attachmentIndex++;
|
||||||
writeBody(os, partHeader);
|
writeBody(os, partHeader);
|
||||||
} else {
|
} else {
|
||||||
String attachmentUrl = attachmentsMap.get(partHeader.name);
|
String attachmentUrl = attachmentsMap.get(partHeader.name).href;
|
||||||
// try to get attachment by index, only if no name found
|
// try to get attachment by index, only if no name found
|
||||||
if (attachmentUrl == null && partHeader.name == null) {
|
if (attachmentUrl == null && partHeader.name == null) {
|
||||||
attachmentUrl = attachmentsMap.get(String.valueOf(attachmentIndex));
|
attachmentUrl = attachmentsMap.get(String.valueOf(attachmentIndex)).href;
|
||||||
}
|
}
|
||||||
if (attachmentUrl == null) {
|
if (attachmentUrl == null) {
|
||||||
// only warn, could happen depending on IIS config
|
// only warn, could happen depending on IIS config
|
||||||
@ -982,7 +973,7 @@ public class ExchangeSession {
|
|||||||
return xmlDocument;
|
return xmlDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getAttachmentsUrls(String messageUrl) throws IOException {
|
public Map<String, Attachment> getAttachments(String messageUrl) throws IOException {
|
||||||
if (attachmentsMap != null) {
|
if (attachmentsMap != null) {
|
||||||
// do not load attachments twice
|
// do not load attachments twice
|
||||||
return attachmentsMap;
|
return attachmentsMap;
|
||||||
@ -999,11 +990,13 @@ public class ExchangeSession {
|
|||||||
// Release the connection.
|
// Release the connection.
|
||||||
getMethod.releaseConnection();
|
getMethod.releaseConnection();
|
||||||
|
|
||||||
Map<String, String> attachmentsMap = new HashMap<String, String>();
|
Map<String, Attachment> attachmentsMap = new HashMap<String, Attachment>();
|
||||||
int attachmentIndex = 2;
|
int attachmentIndex = 2;
|
||||||
|
// list file attachments identified explicitly
|
||||||
List<Attribute> list = xmlDocument.getNodes("//table[@id='idAttachmentWell']//a/@href");
|
List<Attribute> list = xmlDocument.getNodes("//table[@id='idAttachmentWell']//a/@href");
|
||||||
for (Attribute element : list) {
|
for (Attribute element : list) {
|
||||||
String attachmentHref = element.getValue();
|
String attachmentHref = element.getValue();
|
||||||
|
// exclude empty links (owa buttons)
|
||||||
if (!"#".equals(attachmentHref)) {
|
if (!"#".equals(attachmentHref)) {
|
||||||
final String ATTACH_QUERY = "?attach=1";
|
final String ATTACH_QUERY = "?attach=1";
|
||||||
if (attachmentHref.endsWith(ATTACH_QUERY)) {
|
if (attachmentHref.endsWith(ATTACH_QUERY)) {
|
||||||
@ -1011,6 +1004,7 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
// url is encoded
|
// url is encoded
|
||||||
attachmentHref = URIUtil.decode(attachmentHref);
|
attachmentHref = URIUtil.decode(attachmentHref);
|
||||||
|
// exclude external URLs
|
||||||
if (attachmentHref.startsWith(messageUrl)) {
|
if (attachmentHref.startsWith(messageUrl)) {
|
||||||
String attachmentName = attachmentHref.substring(messageUrl.length() + 1);
|
String attachmentName = attachmentHref.substring(messageUrl.length() + 1);
|
||||||
int slashIndex = attachmentName.indexOf('/');
|
int slashIndex = attachmentName.indexOf('/');
|
||||||
@ -1026,27 +1020,39 @@ public class ExchangeSession {
|
|||||||
attachmentName = attachmentName.substring(underscoreIndex + 1);
|
attachmentName = attachmentName.substring(underscoreIndex + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// decode slashes
|
// decode slashes in attachment name
|
||||||
attachmentName = attachmentName.replaceAll("_xF8FF_", "/");
|
attachmentName = attachmentName.replaceAll("_xF8FF_", "/");
|
||||||
|
|
||||||
attachmentsMap.put(attachmentName, attachmentHref);
|
Attachment attachment = new Attachment();
|
||||||
|
attachment.name = attachmentName;
|
||||||
|
attachment.href = attachmentHref;
|
||||||
|
|
||||||
|
attachmentsMap.put(attachmentName, attachment);
|
||||||
logger.debug("Attachment " + attachmentIndex + " : " + attachmentName);
|
logger.debug("Attachment " + attachmentIndex + " : " + attachmentName);
|
||||||
attachmentsMap.put(String.valueOf(attachmentIndex++), attachmentHref);
|
// add a second count based map entry
|
||||||
|
attachmentsMap.put(String.valueOf(attachmentIndex++), attachment);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Message URL : " + messageUrl + " is not a substring of attachment URL : " + attachmentHref);
|
logger.warn("Message URL : " + messageUrl + " is not a substring of attachment URL : " + attachmentHref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use htmlBody and owa generated body to look for inline images
|
// get inline images from htmlBody (without OWA transformation)
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(htmlBody.getBytes("UTF-8"));
|
ByteArrayInputStream bais = new ByteArrayInputStream(htmlBody.getBytes("UTF-8"));
|
||||||
XmlDocument xmlBody = tidyDocument(bais);
|
XmlDocument xmlBody = tidyDocument(bais);
|
||||||
|
List<Attribute> htmlBodyImgList = xmlBody.getNodes("//img/@src");
|
||||||
|
|
||||||
// get inline images
|
// use owa generated body to look for inline images
|
||||||
List<Attribute> imgList = xmlBody.getNodes("//img/@src");
|
List<Attribute> imgList = xmlDocument.getNodes("//img/@src");
|
||||||
imgList.addAll(xmlDocument.getNodes("//img/@src"));
|
|
||||||
|
// TODO : add body background, does not work in thunderbird
|
||||||
|
// htmlBodyImgList.addAll(xmlBody.getNodes("//body/@background"));
|
||||||
|
// imgList.addAll(xmlDocument.getNodes("//td/@background"));
|
||||||
|
|
||||||
|
int inlineImageCount = 0;
|
||||||
for (Attribute element : imgList) {
|
for (Attribute element : imgList) {
|
||||||
String attachmentHref = element.getValue();
|
String attachmentHref = element.getValue();
|
||||||
|
// filter external images
|
||||||
if (attachmentHref.startsWith("1_multipart")) {
|
if (attachmentHref.startsWith("1_multipart")) {
|
||||||
attachmentHref = URIUtil.decode(attachmentHref);
|
attachmentHref = URIUtil.decode(attachmentHref);
|
||||||
if (attachmentHref.endsWith("?Security=3")) {
|
if (attachmentHref.endsWith("?Security=3")) {
|
||||||
@ -1060,13 +1066,32 @@ public class ExchangeSession {
|
|||||||
if (attachmentName.startsWith("%31_multipart%3F2_")) {
|
if (attachmentName.startsWith("%31_multipart%3F2_")) {
|
||||||
attachmentName = attachmentName.substring(18);
|
attachmentName = attachmentName.substring(18);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decode slashes
|
||||||
|
attachmentName = attachmentName.replaceAll("_xF8FF_", "/");
|
||||||
// exclude inline external images
|
// exclude inline external images
|
||||||
if (!attachmentHref.startsWith("http://") && !attachmentHref.startsWith("https://")) {
|
if (!attachmentName.startsWith("http://") && !attachmentName.startsWith("https://")) {
|
||||||
attachmentsMap.put(attachmentName, messageUrl + "/" + attachmentHref);
|
Attachment attachment = new Attachment();
|
||||||
logger.debug("Inline image attachment " + attachmentIndex + " : " + attachmentName);
|
attachment.name = attachmentName;
|
||||||
attachmentsMap.put(String.valueOf(attachmentIndex++), attachmentHref);
|
attachment.href = messageUrl + "/" + attachmentHref;
|
||||||
// fix html body
|
if (htmlBodyImgList.size() > inlineImageCount) {
|
||||||
htmlBody = htmlBody.replaceFirst(attachmentHref, "cid:" + attachmentName);
|
String contentid = htmlBodyImgList.get(inlineImageCount++).getValue();
|
||||||
|
if (contentid.startsWith("cid:")) {
|
||||||
|
attachment.contentid = contentid.substring("cid:".length());
|
||||||
|
} else if (!contentid.startsWith("http://") && !contentid.startsWith("https://")) {
|
||||||
|
attachment.contentid = contentid;
|
||||||
|
// must patch htmlBody for inline image without cid
|
||||||
|
htmlBody = htmlBody.replaceFirst(attachment.contentid, "cid:"+attachment.contentid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("More images in OWA body !");
|
||||||
|
}
|
||||||
|
// add only inline images
|
||||||
|
if (attachment.contentid != null) {
|
||||||
|
attachmentsMap.put(attachmentName, attachment);
|
||||||
|
}
|
||||||
|
logger.debug("Inline image attachment ID:" + attachment.contentid
|
||||||
|
+ " name: " + attachment.name + " href: " + attachment.href);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1078,6 +1103,21 @@ public class ExchangeSession {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Attachment {
|
||||||
|
/**
|
||||||
|
* attachment file name
|
||||||
|
*/
|
||||||
|
public String name = null;
|
||||||
|
/**
|
||||||
|
* Content ID
|
||||||
|
*/
|
||||||
|
public String contentid = null;
|
||||||
|
/**
|
||||||
|
* Attachment URL
|
||||||
|
*/
|
||||||
|
public String href = null;
|
||||||
|
}
|
||||||
|
|
||||||
class MimeHeader {
|
class MimeHeader {
|
||||||
|
|
||||||
public String contentType = null;
|
public String contentType = null;
|
||||||
|
@ -8,20 +8,21 @@ import org.apache.commons.httpclient.util.URIUtil;
|
|||||||
*/
|
*/
|
||||||
public class TestExchangeSession {
|
public class TestExchangeSession {
|
||||||
public static void main(String[] argv) {
|
public static void main(String[] argv) {
|
||||||
|
Settings.setConfigFilePath(argv[0]);
|
||||||
Settings.load();
|
Settings.load();
|
||||||
|
|
||||||
ExchangeSession session = new ExchangeSession();
|
ExchangeSession session = new ExchangeSession();
|
||||||
// test auth
|
// test auth
|
||||||
try {
|
try {
|
||||||
session.login(argv[0], argv[1]);
|
session.login(argv[1], argv[2]);
|
||||||
|
|
||||||
ExchangeSession.Folder folder = session.selectFolder("tests");
|
ExchangeSession.Folder folder = session.selectFolder("tests");
|
||||||
session.selectFolder("tests");
|
session.selectFolder("tests");
|
||||||
String messageName;
|
String messageName;
|
||||||
messageName = URIUtil.decode(argv[2]);
|
messageName = URIUtil.decode(argv[3]);
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
ExchangeSession.Message messageTest = session.getMessage(folder.folderUrl + "/"+messageName);
|
ExchangeSession.Message messageTest = session.getMessage(folder.folderUrl+"/"+messageName);
|
||||||
System.out.println("******");
|
System.out.println("******");
|
||||||
messageTest.write(System.out);
|
messageTest.write(System.out);
|
||||||
System.out.println("Elapsed time " + (System.currentTimeMillis()-startTime) + " ms");
|
System.out.println("Elapsed time " + (System.currentTimeMillis()-startTime) + " ms");
|
||||||
|
Loading…
Reference in New Issue
Block a user