diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 0e098213..14e7578e 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -89,7 +89,7 @@ public abstract class ExchangeSession { protected static final String PUBLIC_ROOT = "/public"; protected static final String CALENDAR = "calendar"; - protected static final String CONTACTS = "contacts"; + public static final String CONTACTS = "contacts"; protected static final String ADDRESSBOOK = "addressbook"; protected static final String INBOX = "INBOX"; protected static final String LOWER_CASE_INBOX = "inbox"; @@ -513,7 +513,7 @@ public abstract class ExchangeSession { */ protected abstract BufferedReader getContentReader(Message message) throws IOException; - protected static final List POP_MESSAGE_ATTRIBUTES = new ArrayList(); + protected static final Set POP_MESSAGE_ATTRIBUTES = new HashSet(); static { POP_MESSAGE_ATTRIBUTES.add("uid"); @@ -531,7 +531,7 @@ public abstract class ExchangeSession { return searchMessages(folderName, POP_MESSAGE_ATTRIBUTES, null); } - protected static final List IMAP_MESSAGE_ATTRIBUTES = new ArrayList(); + protected static final Set IMAP_MESSAGE_ATTRIBUTES = new HashSet(); static { IMAP_MESSAGE_ATTRIBUTES.add("uid"); @@ -546,7 +546,7 @@ public abstract class ExchangeSession { IMAP_MESSAGE_ATTRIBUTES.add("date"); } - protected static final List UID_MESSAGE_ATTRIBUTES = new ArrayList(); + protected static final Set UID_MESSAGE_ATTRIBUTES = new HashSet(); static { UID_MESSAGE_ATTRIBUTES.add("uid"); @@ -584,7 +584,7 @@ public abstract class ExchangeSession { * @return message list * @throws IOException on error */ - public abstract MessageList searchMessages(String folderName, List attributes, Condition condition) throws IOException; + public abstract MessageList searchMessages(String folderName, Set attributes, Condition condition) throws IOException; protected enum Operator { Or, And, Not, IsEqualTo, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse, @@ -1701,7 +1701,7 @@ public abstract class ExchangeSession { writer.appendProperty("FN", get("cn")); // RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes writer.appendProperty("N", get("sn"), get("givenName"), get("middlename"), get("personaltitle"), get("namesuffix")); - + writer.appendProperty("TEL;TYPE=cell", get("mobile")); writer.appendProperty("TEL;TYPE=work", get("telephoneNumber")); writer.appendProperty("TEL;TYPE=home", get("homePhone")); @@ -1741,13 +1741,12 @@ public abstract class ExchangeSession { writer.appendProperty("X-EVOLUTION-SPOUSE", get("spousecn")); - String lastModified = get("lastmodified"); if (lastModified != null) { try { writer.appendProperty("REV", getZuluDateFormat().format(getExchangeZuluDateFormatMillisecond().parse(lastModified))); } catch (ParseException e) { - LOGGER.warn("Invalid date: "+lastModified); + LOGGER.warn("Invalid date: " + lastModified); } } writer.endCard(); @@ -2392,7 +2391,7 @@ public abstract class ExchangeSession { } - public static final List ITEM_PROPERTIES = new ArrayList(); + public static final Set ITEM_PROPERTIES = new HashSet(); static { ITEM_PROPERTIES.add("etag"); @@ -2422,7 +2421,7 @@ public abstract class ExchangeSession { * @return list of contacts * @throws IOException on error */ - protected abstract List searchContacts(String folderPath, List attributes, Condition condition) throws IOException; + public abstract List searchContacts(String folderPath, Set attributes, Condition condition) throws IOException; /** * Search calendar messages in provided folder. @@ -2478,7 +2477,7 @@ public abstract class ExchangeSession { * @return list of calendar messages as Event objects * @throws IOException on error */ - protected abstract List searchEvents(String folderPath, List attributes, Condition condition) throws IOException; + protected abstract List searchEvents(String folderPath, Set attributes, Condition condition) throws IOException; /** * convert vcf extension to EML. @@ -2858,18 +2857,7 @@ public abstract class ExchangeSession { return results; } - /** - * Search users in contacts folder by uid. - * - * @param uid unique id - * @return List of users - * @throws IOException on error - */ - public Map> contactFindByUid(String uid) throws IOException { - return contactFind(equals("uid", uid)); - } - - protected static final List CONTACT_ATTRIBUTES = new ArrayList(); + public static final Set CONTACT_ATTRIBUTES = new HashSet(); static { CONTACT_ATTRIBUTES.add("uid"); @@ -2919,87 +2907,6 @@ public abstract class ExchangeSession { CONTACT_ATTRIBUTES.add("lastmodified"); } - - /** - * Search users in contacts folder - * - * @param condition search filter - * @return List of users - * @throws IOException on error - */ - public Map> contactFind(Condition condition) throws IOException { - // uid value in search filter (hex value) - String filterUid = null; - // base64 encoded uid value - String actualFilterUid = null; - - // TODO: move to LDAP code - // replace hex encoded uid with base64 uid - /* - if (searchFilter != null) { - int uidStart = searchFilter.indexOf(DAV_UID_FILTER); - if (uidStart >= 0) { - int uidEnd = searchFilter.indexOf('\'', uidStart + DAV_UID_FILTER.length()); - if (uidEnd >= 0) { - try { - filterUid = searchFilter.substring(uidStart + DAV_UID_FILTER.length(), uidEnd); - actualFilterUid = new String(Base64.encodeBase64(Hex.decodeHex(filterUid.toCharArray()))); - searchFilter = searchFilter.substring(0, uidStart + DAV_UID_FILTER.length()) + actualFilterUid + searchFilter.substring(uidEnd); - } catch (DecoderException e) { - // ignore, this is not an hex uid - } - } - } - } - */ - - Map> results = new HashMap>(); - - List contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("outlookmessageclass", "IPM.Contact"), condition)); - - Map item; - for (Contact contact : contacts) { - item = new HashMap(); - - for (Map.Entry contactEntry : contact.entrySet()) { - String propertyName = contactEntry.getKey(); - String propertyValue = contactEntry.getValue(); - if ("uid".equals(propertyName)) { - // TODO: move to LDAP ? - // uid is base64, reencode to hex - propertyValue = new String(Hex.encodeHex(Base64.decodeBase64(propertyValue.getBytes()))); - // if actualFilterUid is not null, exclude non exact match - if (actualFilterUid != null && !filterUid.equals(propertyValue)) { - propertyValue = null; - } - } else if ("bday".equals(propertyName)) { - SimpleDateFormat parser = getExchangeZuluDateFormatMillisecond(); - try { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(parser.parse(propertyValue)); - item.put("birthday", String.valueOf(calendar.get(Calendar.DAY_OF_MONTH))); - item.put("birthmonth", String.valueOf(calendar.get(Calendar.MONTH) + 1)); - item.put("birthyear", String.valueOf(calendar.get(Calendar.YEAR))); - propertyValue = null; - } catch (ParseException e) { - throw new IOException(e); - } - } else if ("textdescription".equals(propertyName) && " \n".equals(propertyValue)) { - propertyValue = null; - } - if (propertyValue != null && propertyValue.length() > 0) { - item.put(propertyName, propertyValue); - } - } - if (item.get("uid") != null) { - results.put(item.get("uid"), item); - } - } - - LOGGER.debug("contactFind: " + results.size() + " result(s)"); - return results; - } - /** * Get extended address book information for person with gallookup. * Does not work with Exchange 2007 diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 6e0ad5de..f2be0fc7 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -117,6 +117,10 @@ public class DavExchangeSession extends ExchangeSession { exchangeFolderPath = mailPath + draftsName + folderPath.substring(DRAFTS.length()); } else if (folderPath.startsWith(SENT)) { exchangeFolderPath = mailPath + sentitemsName + folderPath.substring(SENT.length()); + } else if (folderPath.startsWith(CONTACTS)) { + exchangeFolderPath = mailPath + contactsName + folderPath.substring(CONTACTS.length()); + } else if (folderPath.startsWith(CALENDAR)) { + exchangeFolderPath = mailPath + calendarName + folderPath.substring(CALENDAR.length()); } else if (folderPath.startsWith("public")) { exchangeFolderPath = publicFolderUrl + folderPath.substring("public".length()); @@ -415,9 +419,13 @@ public class DavExchangeSession extends ExchangeSession { } public void appendTo(StringBuilder buffer) { - buffer.append('"').append(Field.get(attributeName).getUri()).append('"'); + Field field = Field.get(attributeName); + buffer.append('"').append(field.getUri()).append('"'); buffer.append(operatorMap.get(operator)); - if (!isIntValue) { + //noinspection VariableNotUsedInsideIf + if (field.cast != null) { + buffer.append("CAST (\""); + } else if (!isIntValue) { buffer.append('\''); } if (Operator.Like == operator) { @@ -427,7 +435,9 @@ public class DavExchangeSession extends ExchangeSession { if (Operator.Like == operator || Operator.StartsWith == operator) { buffer.append('%'); } - if (!isIntValue) { + if (field.cast != null) { + buffer.append("\" as '").append(field.cast).append("')"); + } else if (!isIntValue) { buffer.append('\''); } } @@ -901,7 +911,7 @@ public class DavExchangeSession extends ExchangeSession { return folder; } - protected static final List FOLDER_PROPERTIES = new ArrayList(); + protected static final Set FOLDER_PROPERTIES = new HashSet(); static { FOLDER_PROPERTIES.add("displayname"); @@ -1082,7 +1092,7 @@ public class DavExchangeSession extends ExchangeSession { } @Override - public MessageList searchMessages(String folderPath, List attributes, Condition condition) throws IOException { + public MessageList searchMessages(String folderPath, Set attributes, Condition condition) throws IOException { MessageList messages = new MessageList(); MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow); @@ -1099,9 +1109,11 @@ public class DavExchangeSession extends ExchangeSession { * @inheritDoc */ @Override - protected List searchContacts(String folderPath, List attributes, Condition condition) throws IOException { + public List searchContacts(String folderPath, Set attributes, Condition condition) throws IOException { List contacts = new ArrayList(); - MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow); + MultiStatusResponse[] responses = searchItems(folderPath, attributes, + and(equals("outlookmessageclass", "IPM.Contact"), isFalse("isfolder"), isFalse("ishidden"), condition), + FolderQueryTraversal.Shallow); for (MultiStatusResponse response : responses) { contacts.add(new Contact(response)); } @@ -1109,7 +1121,7 @@ public class DavExchangeSession extends ExchangeSession { } @Override - protected List searchEvents(String folderPath, List attributes, Condition condition) throws IOException { + protected List searchEvents(String folderPath, Set attributes, Condition condition) throws IOException { List events = new ArrayList(); MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow); for (MultiStatusResponse response : responses) { @@ -1133,7 +1145,7 @@ public class DavExchangeSession extends ExchangeSession { return events; } - protected MultiStatusResponse[] searchItems(String folderPath, List attributes, Condition condition, FolderQueryTraversal folderQueryTraversal) throws IOException { + protected MultiStatusResponse[] searchItems(String folderPath, Set attributes, Condition condition, FolderQueryTraversal folderQueryTraversal) throws IOException { String folderUrl = getFolderPath(folderPath); StringBuilder searchRequest = new StringBuilder(); searchRequest.append("SELECT ") @@ -1237,7 +1249,7 @@ public class DavExchangeSession extends ExchangeSession { if ("urn:content-classes:person".equals(contentClass)) { // retrieve Contact properties // TODO: need to check list size - return searchContacts(itemPath.substring(0, itemPath.lastIndexOf('/')), CONTACT_ATTRIBUTES, equals("urlcompname", itemPath.substring(itemPath.lastIndexOf('/')+1))).get(0); + return searchContacts(itemPath.substring(0, itemPath.lastIndexOf('/')), CONTACT_ATTRIBUTES, equals("urlcompname", itemPath.substring(itemPath.lastIndexOf('/') + 1))).get(0); } else if ("urn:content-classes:appointment".equals(contentClass) || "urn:content-classes:calendarmessage".equals(contentClass)) { return new Event(responses[0]); @@ -1330,7 +1342,7 @@ public class DavExchangeSession extends ExchangeSession { String timezoneId = null; try { - ArrayList attributes = new ArrayList(); + Set attributes = new HashSet(); attributes.add("roamingdictionary"); MultiStatusResponse[] responses = searchItems("/users/" + getEmail() + "/NON_IPM_SUBTREE", attributes, equals("messageclass", "IPM.Configuration.OWA.UserOptions"), DavExchangeSession.FolderQueryTraversal.Deep); diff --git a/src/java/davmail/exchange/dav/Field.java b/src/java/davmail/exchange/dav/Field.java index d2621ad3..6e11e1d6 100644 --- a/src/java/davmail/exchange/dav/Field.java +++ b/src/java/davmail/exchange/dav/Field.java @@ -18,6 +18,7 @@ */ package davmail.exchange.dav; +import org.apache.commons.codec.binary.Hex; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.property.DefaultDavProperty; @@ -111,8 +112,10 @@ public class Field { createField(DAV, "isfolder"); + // item id, do not use DAV:uid, see http://support.microsoft.com/kb/320749 + createField("uid", 0x300b, PropertyType.Binary); // PR_SEARCH_KEY + // POP and IMAP message - createField(DAV, "uid"); createField("messageSize", 0x0e08, PropertyType.Long);//PR_MESSAGE_SIZE createField("imapUid", 0x0e23, PropertyType.Long);//PR_INTERNET_ARTICLE_NUMBER createField("junk", 0x1083, PropertyType.Long); @@ -238,9 +241,22 @@ public class Field { createField(DAV, "ishidden"); } + protected static String toHexString(int propertyTag) { + String hexValue = Integer.toHexString(propertyTag); + while (hexValue.length() < 4) { + hexValue = '0' + hexValue; + } + return hexValue; + } + protected static void createField(String alias, int propertyTag, PropertyType propertyType) { - String name = 'x' + Integer.toHexString(propertyTag) + propertyTypeMap.get(propertyType); - Field field = new Field(alias, SCHEMAS_MAPI_PROPTAG, name); + String name = 'x' + toHexString(propertyTag) + propertyTypeMap.get(propertyType); + Field field; + if (propertyType == PropertyType.Binary) { + field = new Field(alias, SCHEMAS_MAPI_PROPTAG, name, null, "bin.base64"); + } else { + field = new Field(alias, SCHEMAS_MAPI_PROPTAG, name); + } fieldMap.put(field.alias, field); } @@ -251,15 +267,15 @@ public class Field { name = String.valueOf(propertyTag); } else { // Common namespace expects hex names - name = "0x" + Integer.toHexString(propertyTag); + name = "0x" + toHexString(propertyTag); } Field field = new Field(alias, Namespace.getNamespace(SCHEMAS_MAPI_ID.getURI() + - '{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name, responseAlias); + '{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name, responseAlias, null); fieldMap.put(field.alias, field); } protected static void createField(String alias, DistinguishedPropertySetType propertySetType, int propertyTag, PropertyType propertyType) { - String name = "_x" + propertyTypeMap.get(propertyType) + "_x" + Integer.toHexString(propertyTag); + String name = "_x" + propertyTypeMap.get(propertyType) + "_x" + toHexString(propertyTag); Field field = new Field(alias, Namespace.getNamespace(SCHEMAS_MAPI_ID.getURI() + '{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name); @@ -281,6 +297,7 @@ public class Field { protected final String uri; protected final String requestPropertyString; protected final DavPropertyName responsePropertyName; + protected final String cast; public Field(Namespace namespace, String name) { @@ -288,10 +305,10 @@ public class Field { } public Field(String alias, Namespace namespace, String name) { - this(alias, namespace, name, null); + this(alias, namespace, name, null, null); } - public Field(String alias, Namespace namespace, String name, String responseAlias) { + public Field(String alias, Namespace namespace, String name, String responseAlias, String cast) { davPropertyName = DavPropertyName.create(name, namespace); this.alias = alias; this.uri = namespace.getURI() + name; @@ -302,6 +319,7 @@ public class Field { this.requestPropertyString = '"' + uri + "\" as " + responseAlias; this.responsePropertyName = DavPropertyName.create(responseAlias, EMPTY); } + this.cast = cast; } public String getUri() { diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index 700eaf1f..6e6c101c 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -138,7 +138,7 @@ public class EwsExchangeSession extends ExchangeSession { } @Override - public MessageList searchMessages(String folderName, List attributes, Condition condition) throws IOException { + public MessageList searchMessages(String folderName, Set attributes, Condition condition) throws IOException { throw new UnsupportedOperationException(); } @@ -468,12 +468,12 @@ public class EwsExchangeSession extends ExchangeSession { } @Override - protected List searchContacts(String folderName, List attributes, Condition condition) throws IOException { + public List searchContacts(String folderName, Set attributes, Condition condition) throws IOException { throw new UnsupportedOperationException(); } @Override - protected List searchEvents(String folderPath, List attributes, Condition condition) throws IOException { + protected List searchEvents(String folderPath, Set attributes, Condition condition) throws IOException { throw new UnsupportedOperationException(); } diff --git a/src/java/davmail/ldap/LdapConnection.java b/src/java/davmail/ldap/LdapConnection.java index ddd20e87..c2deb5f8 100644 --- a/src/java/davmail/ldap/LdapConnection.java +++ b/src/java/davmail/ldap/LdapConnection.java @@ -28,6 +28,10 @@ import davmail.exception.DavMailException; import davmail.exchange.ExchangeSession; import davmail.exchange.ExchangeSessionFactory; import davmail.ui.tray.DavGatewayTray; +import davmail.util.StringUtil; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -35,6 +39,8 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.*; /** @@ -1021,16 +1027,26 @@ public class LdapConnection extends AbstractConnection { ExchangeSession.Condition condition; + // convert hex uid to base64 + String actualValue = value; + if ("uid".equals(contactAttributeName)) { + try { + actualValue = StringUtil.hexToBase64(value); + } catch (DecoderException e) { + // ignore, this is not an hex uid + } + } + if (operator == LDAP_FILTER_EQUALITY) { - condition = session.equals(contactAttributeName, value); - } else if ("*".equals(value)) { + condition = session.equals(contactAttributeName, actualValue); + } else if ("*".equals(actualValue)) { condition = session.not(session.isNull(contactAttributeName)); } else { // endsWith not supported by exchange, convert to contains if (mode == LDAP_SUBSTRING_FINAL || mode == LDAP_SUBSTRING_ANY) { - condition = session.contains(contactAttributeName, value); + condition = session.contains(contactAttributeName, actualValue); } else { - condition = session.startsWith(contactAttributeName, value); + condition = session.startsWith(contactAttributeName, actualValue); } } return condition; @@ -1157,11 +1173,17 @@ public class LdapConnection extends AbstractConnection { if (session != null) { // single user request String uid = dn.substring("uid=".length(), dn.indexOf(',')); + Map> persons = null; + // first search in contact - Map> persons = session.contactFindByUid(uid); + try { + persons = contactFind(session.equals("uid", StringUtil.hexToBase64(uid)), returningAttributes); + } catch (DecoderException e) { + // ignore, this is not an hex uid + } // then in GAL - if (persons.isEmpty()) { + if (persons == null || persons.isEmpty()) { persons = session.galFind("AN", uid); Map person = persons.get(uid.toLowerCase()); @@ -1190,7 +1212,7 @@ public class LdapConnection extends AbstractConnection { Map> persons = new HashMap>(); if (ldapFilter.isFullSearch()) { // append personal contacts first - for (Map person : session.contactFind(null).values()) { + for (Map person : contactFind(null, returningAttributes).values()) { persons.put(person.get("uid"), person); if (persons.size() == sizeLimit) { break; @@ -1217,7 +1239,7 @@ public class LdapConnection extends AbstractConnection { // if ldapfilter is not a full search and filter is null, // ignored all attribute filters => return empty results if (ldapFilter.isFullSearch() || filter != null) { - for (Map person : session.contactFind(filter).values()) { + for (Map person : contactFind(filter, returningAttributes).values()) { persons.put(person.get("uid"), person); if (persons.size() == sizeLimit) { @@ -1273,6 +1295,63 @@ public class LdapConnection extends AbstractConnection { } + /** + * Search users in contacts folder + * + * @param condition search filter + * @return List of users + * @throws IOException on error + */ + public Map> contactFind(ExchangeSession.Condition condition, Set returningAttributes) throws IOException { + Set ldapReturningAttributes; + if (returningAttributes != null && !returningAttributes.isEmpty()) { + ldapReturningAttributes = new HashSet(); + // always return uid + ldapReturningAttributes.add("uid"); + for (String attribute : returningAttributes) { + if (!"objectclass".equals(attribute)) { + ldapReturningAttributes.add(attribute); + } + } + } else { + ldapReturningAttributes = ExchangeSession.CONTACT_ATTRIBUTES; + } + + Map> results = new HashMap>(); + + List contacts = session.searchContacts(ExchangeSession.CONTACTS, ldapReturningAttributes, condition); + + for (ExchangeSession.Contact contact : contacts) { + // TODO convert values + /* + if ("bday".equals(propertyName)) { + SimpleDateFormat parser = getExchangeZuluDateFormatMillisecond(); + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(parser.parse(propertyValue)); + item.put("birthday", String.valueOf(calendar.get(Calendar.DAY_OF_MONTH))); + item.put("birthmonth", String.valueOf(calendar.get(Calendar.MONTH) + 1)); + item.put("birthyear", String.valueOf(calendar.get(Calendar.YEAR))); + propertyValue = null; + } catch (ParseException e) { + throw new IOException(e); + } + } else if ("textdescription".equals(propertyName) && " \n".equals(propertyValue)) { + propertyValue = null; + } + */ + if (contact.get("uid") != null) { + // convert uid to hex + String hexUid = StringUtil.base64ToHex(contact.get("uid")); + contact.put("uid", hexUid); + results.put(hexUid, contact); + } + } + + return results; + } + + /** * Convert to LDAP attributes and send entry * diff --git a/src/java/davmail/util/StringUtil.java b/src/java/davmail/util/StringUtil.java index b898e39f..77c53166 100644 --- a/src/java/davmail/util/StringUtil.java +++ b/src/java/davmail/util/StringUtil.java @@ -18,6 +18,10 @@ */ package davmail.util; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + import java.util.Set; import java.util.regex.Pattern; @@ -169,4 +173,33 @@ public final class StringUtil { return result; } + /** + * Convert base64 value to hex. + * + * @param value base64 value + * @return hex value + */ + public static String base64ToHex(String value) { + String hexValue = null; + if (value != null) { + hexValue = new String(Hex.encodeHex(Base64.decodeBase64(value.getBytes()))); + } + return hexValue; + } + + /** + * Convert hex value to base64. + * + * @param value hex value + * @return base64 value + * @throws DecoderException on error + */ + public static String hexToBase64(String value) throws DecoderException { + String base64Value = null; + if (value != null) { + base64Value = new String(Base64.encodeBase64(Hex.decodeHex(value.toCharArray()))); + } + return base64Value; + } + } diff --git a/src/test/davmail/exchange/TestExchangeSessionContact.java b/src/test/davmail/exchange/TestExchangeSessionContact.java new file mode 100644 index 00000000..42335723 --- /dev/null +++ b/src/test/davmail/exchange/TestExchangeSessionContact.java @@ -0,0 +1,59 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange; + +import davmail.exchange.dav.DavExchangeSession; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Test ExchangeSession contact features. + */ +@SuppressWarnings({"UseOfSystemOutOrSystemErr"}) +public class TestExchangeSessionContact extends AbstractExchangeSessionTestCase { + + public void testSearchContacts() throws IOException { + List contacts = session.searchContacts(ExchangeSession.CONTACTS, ExchangeSession.CONTACT_ATTRIBUTES, null); + for (ExchangeSession.Contact contact : contacts) { + System.out.println(session.getItem(ExchangeSession.CONTACTS, contact.getName())); + } + } + + public void testSearchContactsUidOnly() throws IOException { + Set attributes = new HashSet(); + attributes.add("uid"); + List contacts = session.searchContacts(ExchangeSession.CONTACTS, attributes, null); + for (ExchangeSession.Contact contact : contacts) { + System.out.println(contact); + } + } + + public void testSearchContactsByUid() throws IOException { + Set attributes = new HashSet(); + attributes.add("uid"); + List contacts = session.searchContacts(ExchangeSession.CONTACTS, attributes, null); + for (ExchangeSession.Contact contact : contacts) { + System.out.println(session.searchContacts(ExchangeSession.CONTACTS, attributes, session.equals("uid", contact.get("uid")))); + } + } +} diff --git a/src/test/davmail/exchange/dav/TestDavExchangeSession.java b/src/test/davmail/exchange/dav/TestDavExchangeSession.java index 2cbdf90a..5b6b39bd 100644 --- a/src/test/davmail/exchange/dav/TestDavExchangeSession.java +++ b/src/test/davmail/exchange/dav/TestDavExchangeSession.java @@ -54,6 +54,9 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase { assertEquals(mailPath + davSession.sentitemsName, davSession.getFolderPath("Sent")); assertEquals(mailPath + davSession.draftsName, davSession.getFolderPath("Drafts")); + assertEquals(mailPath + davSession.contactsName, davSession.getFolderPath("contacts")); + assertEquals(mailPath + davSession.calendarName, davSession.getFolderPath("calendar")); + assertEquals(mailPath + davSession.inboxName + "/test", davSession.getFolderPath("INBOX/test")); assertEquals(mailPath + davSession.deleteditemsName + "/test", davSession.getFolderPath("Trash/test")); assertEquals(mailPath + davSession.sentitemsName + "/test", davSession.getFolderPath("Sent/test")); @@ -99,7 +102,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase { } public void testGetCategoryList() throws IOException { - ArrayList attributes = new ArrayList(); + Set attributes = new HashSet(); attributes.add("permanenturl"); attributes.add("roamingxmlstream"); MultiStatusResponse[] responses = davSession.searchItems("/users/" + davSession.getEmail() + "/calendar", attributes, davSession.and(davSession.isFalse("isfolder"), davSession.equals("messageclass", "IPM.Configuration.CategoryList")), DavExchangeSession.FolderQueryTraversal.Shallow); @@ -109,7 +112,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase { } public void testGetCalendarOptions() throws IOException { - ArrayList attributes = new ArrayList(); + Set attributes = new HashSet(); attributes.add("permanenturl"); attributes.add("roamingxmlstream"); MultiStatusResponse[] responses = davSession.searchItems("/users/" + davSession.getEmail() + "/calendar", attributes, davSession.and(davSession.isFalse("isfolder"), davSession.equals("messageclass", "IPM.Configuration.Calendar")), DavExchangeSession.FolderQueryTraversal.Shallow); @@ -119,7 +122,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase { } public void testAllHidden() throws IOException { - ArrayList attributes = new ArrayList(); + Set attributes = new HashSet(); attributes.add("messageclass"); attributes.add("permanenturl"); attributes.add("roamingxmlstream"); @@ -139,7 +142,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase { } public void testNonIpmSubtree() throws IOException { - ArrayList attributes = new ArrayList(); + Set attributes = new HashSet(); attributes.add("messageclass"); attributes.add("permanenturl"); attributes.add("roamingxmlstream");