diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 0844548b..8947c714 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -352,8 +352,17 @@ public class ExchangeSession { } protected Message buildMessage(ResponseEntity responseEntity) throws URIException { + return buildMessage(responseEntity, null); + } + + protected Message buildMessage(ResponseEntity responseEntity, String webMessageUrl) throws URIException { Message message = new Message(); message.messageUrl = URIUtil.decode(responseEntity.getHref()); + if (webMessageUrl != null) { + message.webMessageUrl = webMessageUrl; + } else { + message.webMessageUrl = message.messageUrl; + } Enumeration propertiesEnum = responseEntity.getProperties(); while (propertiesEnum.hasMoreElements()) { Property prop = (Property) propertiesEnum.nextElement(); @@ -403,7 +412,12 @@ public class ExchangeSession { return message; } + public Message getMessage(String messageUrl) throws IOException { + return getMessage(messageUrl, null); + } + + public Message getMessage(String messageUrl, String webMessageUrl) throws IOException { // TODO switch according to Log4J log level @@ -420,7 +434,7 @@ public class ExchangeSession { } ResponseEntity entity = (ResponseEntity) messageEnum.nextElement(); - return buildMessage(entity); + return buildMessage(entity, webMessageUrl); } @@ -482,6 +496,8 @@ public class ExchangeSession { folder.folderUrl = null; if ("INBOX".equals(folderName)) { folder.folderUrl = inboxUrl; + } else if ("TRASH".equals(folderName)) { + folder.folderUrl = deleteditemsUrl; } else { folder.folderUrl = mailPath + folderName; } @@ -520,6 +536,10 @@ public class ExchangeSession { public static final String CONTENT_TYPE_HEADER = "Content-Type: "; public static final String CONTENT_TRANSFER_ENCODING_HEADER = "Content-Transfer-Encoding: "; public String messageUrl; + /** + * OWA generated page URL, maybe different from DAV url + */ + public String webMessageUrl; public String uid; public int size; public String fullHeaders; @@ -583,7 +603,7 @@ public class ExchangeSession { if (priority != null) { fullHeaders += "X-Priority: " + priority + "\n"; } - if (cc != null) { + if (cc != null && cc.length() > 0) { fullHeaders += "CC: " + cc + "\n"; } } catch (ParseException e) { @@ -633,7 +653,7 @@ public class ExchangeSession { // exchange message : create mime part headers if (boundary != null) { - attachmentsMap = getAttachments(messageUrl); + attachmentsMap = getAttachments(webMessageUrl); // TODO : test actual header values result.append("\n--").append(boundary) .append("\nContent-Type: text/html") @@ -704,7 +724,7 @@ public class ExchangeSession { } else { attachmentIndex = 0; - attachmentsMap = getAttachments(messageUrl); + attachmentsMap = getAttachments(webMessageUrl); writeMimeMessage(reader, os, mimeHeader, attachmentsMap); } os.flush(); @@ -739,18 +759,18 @@ public class ExchangeSession { attachmentIndex++; writeBody(os, partHeader); } else { - String attachmentUrl = attachmentsMap.get(partHeader.name).href; + Attachment attachment = attachmentsMap.get(partHeader.name); // try to get attachment by index, only if no name found - if (attachmentUrl == null && partHeader.name == null) { - attachmentUrl = attachmentsMap.get(String.valueOf(attachmentIndex)).href; + if (attachment == null && partHeader.name == null) { + attachment = attachmentsMap.get(String.valueOf(attachmentIndex)); } - if (attachmentUrl == null) { + if (attachment == null) { // only warn, could happen depending on IIS config //throw new IOException("Attachment " + partHeader.name + " not found in " + messageUrl); logger.warn("Attachment " + partHeader.name + " not found in " + messageUrl); } else { attachmentIndex++; - writeAttachment(os, partHeader, attachmentUrl); + writeAttachment(os, partHeader, attachment); } } } @@ -763,7 +783,7 @@ public class ExchangeSession { } } - protected void writeAttachment(OutputStream os, MimeHeader mimeHeader, String attachmentUrl) throws IOException { + protected void writeAttachment(OutputStream os, MimeHeader mimeHeader, Attachment attachment) throws IOException { try { OutputStream quotedOs; try { @@ -776,18 +796,26 @@ public class ExchangeSession { } catch (MessagingException e) { throw new IOException(e.getMessage()); } - String decodedPath = URIUtil.decode(attachmentUrl); + String decodedPath = URIUtil.decode(attachment.href); if ("message/rfc822".equals(mimeHeader.contentType)) { - // messages are not available at the attachment URL, but - // directly under the main message String messageAttachmentPath = decodedPath; - int index = decodedPath.toLowerCase().lastIndexOf(".eml"); - if (index > 0) { - messageAttachmentPath = decodedPath.substring(0, index + 4); + + // Get real attachment path from owa page content + GetMethod method = new GetMethod(URIUtil.encodePath(decodedPath)); + wdr.retrieveSessionInstance().executeMethod(method); + String body = method.getResponseBodyAsString(); + final String URL_DECLARATION = "var g_szURL\t= \""; + int messageUrlBeginIndex = body.indexOf(URL_DECLARATION); + if (messageUrlBeginIndex >= 0) { + messageUrlBeginIndex = messageUrlBeginIndex + URL_DECLARATION.length(); + int messageUrlEndIndex = body.indexOf("/\"", messageUrlBeginIndex); + if (messageUrlBeginIndex >= 0) { + messageAttachmentPath = URIUtil.decode(body.substring(messageUrlBeginIndex, messageUrlEndIndex)); + } } - Message attachedMessage = getMessage(messageAttachmentPath); + Message attachedMessage = getMessage(messageAttachmentPath, decodedPath); attachedMessage.write(quotedOs); } else { @@ -1024,11 +1052,18 @@ public class ExchangeSession { // exclude external URLs if (attachmentHref.startsWith(messageUrl)) { String attachmentName = attachmentHref.substring(messageUrl.length() + 1); + // yet another case, attached messages ? + final String ANOTHER_MULTIPART_STRING = "1_multipart/2_"; + int anotherMultipartIndex = attachmentName.indexOf(ANOTHER_MULTIPART_STRING); + if (anotherMultipartIndex >= 0) { + attachmentName = attachmentName.substring(anotherMultipartIndex + ANOTHER_MULTIPART_STRING.length()); + } + int slashIndex = attachmentName.indexOf('/'); if (slashIndex >= 0) { attachmentName = attachmentName.substring(0, slashIndex); } - // attachmentName is now right for Exchange message, need to handle external MIME messages + // attachmentName is now right for Exchange messages, need to handle external MIME messages final String MULTIPART_STRING = "1_multipart_xF8FF_"; if (attachmentName.startsWith(MULTIPART_STRING)) { attachmentName = attachmentName.substring(MULTIPART_STRING.length()); @@ -1146,14 +1181,20 @@ public class ExchangeSession { public String boundary = null; public String name = null; + protected void writeLine(OutputStream os, String line) throws IOException { + if (os != null) { + os.write(line.getBytes()); + os.write('\r'); + os.write('\n'); + } + } + public void processHeaders(BufferedReader reader, OutputStream os) throws IOException { String line; line = reader.readLine(); while (line != null && line.length() > 0) { - os.write(line.getBytes()); - os.write('\r'); - os.write('\n'); + writeLine(os, line); String lineToCompare = line.toLowerCase(); @@ -1165,9 +1206,7 @@ public class ExchangeSession { line = reader.readLine(); header.append(line); - os.write(line.getBytes()); - os.write('\r'); - os.write('\n'); + writeLine(os, line); } // decode header with accented file name (URL encoded) int encodedIndex = header.indexOf("*="); @@ -1217,8 +1256,20 @@ public class ExchangeSession { } line = reader.readLine(); } - os.write('\r'); - os.write('\n'); + writeLine(os, ""); + + // strip header content for attached messages + if ("message/rfc822".equals(contentType)) { + MimeHeader internalMimeHeader = new MimeHeader(); + internalMimeHeader.processHeaders(reader, null); + if (internalMimeHeader.boundary != null) { + String endBoundary = internalMimeHeader.boundary + "--"; + line = reader.readLine(); + while (line != null && !endBoundary.equals(line)) { + line = reader.readLine(); + } + } + } } } }