diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 5f70dc4d..4e2bdf65 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -556,6 +556,7 @@ public abstract class ExchangeSession { static { IMAP_MESSAGE_ATTRIBUTES.add("permanenturl"); + IMAP_MESSAGE_ATTRIBUTES.add("urlcompname"); IMAP_MESSAGE_ATTRIBUTES.add("uid"); IMAP_MESSAGE_ATTRIBUTES.add("messageSize"); IMAP_MESSAGE_ATTRIBUTES.add("imapUid"); diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 4f5c7443..f88bd418 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -433,7 +433,11 @@ public class DavExchangeSession extends ExchangeSession { if (Operator.Like == operator) { buffer.append('%'); } - buffer.append(value); + if ("urlcompname".equals(field.alias)) { + buffer.append(StringUtil.encodeUrlcompname(value)); + } else { + buffer.append(value); + } if (Operator.Like == operator || Operator.StartsWith == operator) { buffer.append('%'); } @@ -1202,22 +1206,7 @@ public class DavExchangeSession extends ExchangeSession { @Override public Item getItem(String folderPath, String itemName) throws IOException { String itemPath = getFolderPath(folderPath) + '/' + convertItemNameToEML(itemName); - Item item; - try { - item = getItem(itemPath); - } catch (HttpNotFoundException hnfe) { - // failover for Exchange 2007 plus encoding issue - String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''"); - LOGGER.debug("Item not found at " + itemPath + ", search by displayname: '" + decodedEventName + '\''); - ExchangeSession.MessageList messages = searchMessages(folderPath, equals("displayname", decodedEventName)); - if (!messages.isEmpty()) { - item = getItem(messages.get(0).getPermanentUrl()); - } else { - throw hnfe; - } - } - - return item; + return getItem(itemPath); } @Override diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index 68f7c8ca..f1434005 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -174,7 +174,7 @@ public class EwsExchangeSession extends ExchangeSession { fieldUpdates.add(Field.createFieldUpdate("messageFlags", "0")); } } - fieldUpdates.add(Field.createFieldUpdate("urlcompname", messageName)); + fieldUpdates.add(Field.createFieldUpdate("urlcompname", StringUtil.encodeUrlcompname(messageName))); item.setFieldUpdates(fieldUpdates); CreateItemMethod createItemMethod = new CreateItemMethod(MessageDisposition.SaveOnly, getFolderId(folderPath), item); executeMethod(createItemMethod); @@ -363,10 +363,16 @@ public class EwsExchangeSession extends ExchangeSession { containmentComparison.appendTo(buffer); } buffer.append('>'); - getFieldURI(attributeName).appendTo(buffer); + FieldURI fieldURI = getFieldURI(attributeName); + fieldURI.appendTo(buffer); buffer.append(""); buffer.append("'); @@ -577,7 +583,7 @@ public class EwsExchangeSession extends ExchangeSession { * @inheritDoc */ @Override - public int createFolder(String folderPath, String folderClass, Map properties) throws IOException { + public int createFolder(String folderPath, String folderClass, Map properties) throws IOException { FolderPath path = new FolderPath(folderPath); EWSMethod.Item folder = new EWSMethod.Item(); folder.type = "Folder"; @@ -1112,7 +1118,7 @@ public class EwsExchangeSession extends ExchangeSession { parentFolderId, FOLDER_PROPERTIES, new TwoOperandExpression(TwoOperandExpression.Operator.IsEqualTo, - Field.get("urlcompname"), StringUtil.urlEncodeAmpersand(folderName)) + Field.get("urlcompname"), folderName) ); executeMethod(findFolderMethod); EWSMethod.Item item = findFolderMethod.getResponseItem(); diff --git a/src/java/davmail/exchange/ews/TwoOperandExpression.java b/src/java/davmail/exchange/ews/TwoOperandExpression.java index a6863b3f..5b44555c 100644 --- a/src/java/davmail/exchange/ews/TwoOperandExpression.java +++ b/src/java/davmail/exchange/ews/TwoOperandExpression.java @@ -51,7 +51,12 @@ public class TwoOperandExpression implements SearchExpression { fieldURI.appendTo(buffer); buffer.append(""); buffer.append("'); diff --git a/src/java/davmail/util/StringUtil.java b/src/java/davmail/util/StringUtil.java index b5bcac59..b5046ea3 100644 --- a/src/java/davmail/util/StringUtil.java +++ b/src/java/davmail/util/StringUtil.java @@ -135,6 +135,18 @@ public final class StringUtil { private static final Pattern ENCODED_LT_PATTERN = Pattern.compile("<"); private static final Pattern ENCODED_GT_PATTERN = Pattern.compile(">"); + private static final Pattern F8FF_PATTERN = Pattern.compile("_xF8FF_"); + private static final Pattern PLUS_PATTERN = Pattern.compile("\\+"); + + + public static String urlEncodeSlash(String name) { + String result = name; + if (name.indexOf("_xF8FF_") >= 0) { + result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char)0xF8FF)); + } + return result; + } + /** * Encode & to %26 for urlcompname. * @@ -232,4 +244,17 @@ public final class StringUtil { return base64Value; } + public static String encodeUrlcompname(String value) { + String result = value; + if (result.indexOf("_xF8FF_") >= 0) { + result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char)0xF8FF)); + } + if (result.indexOf('&') >= 0) { + result = AMP_PATTERN.matcher(result).replaceAll("%26"); + } + if (result.indexOf('+') >= 0) { + result = PLUS_PATTERN.matcher(result).replaceAll("%2B"); + } + return result; + } } diff --git a/src/test/davmail/exchange/TestExchangeSessionMessage.java b/src/test/davmail/exchange/TestExchangeSessionMessage.java index c8a16c9f..c4ab16b4 100644 --- a/src/test/davmail/exchange/TestExchangeSessionMessage.java +++ b/src/test/davmail/exchange/TestExchangeSessionMessage.java @@ -96,6 +96,46 @@ public class TestExchangeSessionMessage extends AbstractExchangeSessionTestCase assertEquals(0, messageList.size()); } + public void testSpecialMessageCharacter() throws IOException, MessagingException { + session.deleteFolder("testfolder"); + session.createMessageFolder("testfolder"); + MimeMessage mimeMessage = createMimeMessage(); + messageName = "Special & accenté.EML"; + HashMap properties = new HashMap(); + properties.put("draft", "0"); + session.createMessage("testfolder", messageName, properties, getMimeBody(mimeMessage)); + ExchangeSession.MessageList messageList = session.searchMessages("testfolder", session.equals("urlcompname", messageName)); + assertNotNull(messageList); + assertEquals(1, messageList.size()); + } + + public void testSlashMessageName() throws IOException, MessagingException { + session.deleteFolder("testfolder"); + session.createMessageFolder("testfolder"); + MimeMessage mimeMessage = createMimeMessage(); + messageName = "test _xF8FF_ slash.EML"; + HashMap properties = new HashMap(); + properties.put("draft", "0"); + session.createMessage("testfolder", messageName, properties, getMimeBody(mimeMessage)); + ExchangeSession.MessageList messageList = session.searchMessages("testfolder", session.equals("urlcompname", messageName)); + assertNotNull(messageList); + assertEquals(1, messageList.size()); + } + + public void testPlusMessageName() throws IOException, MessagingException { + // fails on Exchange 2003 + session.deleteFolder("testfolder"); + session.createMessageFolder("testfolder"); + MimeMessage mimeMessage = createMimeMessage(); + messageName = "test + plus.EML"; + HashMap properties = new HashMap(); + properties.put("draft", "0"); + session.createMessage("testfolder", messageName, properties, getMimeBody(mimeMessage)); + ExchangeSession.MessageList messageList = session.searchMessages("testfolder", session.equals("urlcompname", messageName)); + assertNotNull(messageList); + assertEquals(1, messageList.size()); + } + /** * Cleanup */