1
0
mirror of https://github.com/moparisthebest/davmail synced 2024-12-15 04:02:21 -05:00

LDAP: use PR_SEARCH_KEY instead of DAV:uid as uid string

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1115 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-07-01 12:47:57 +00:00
parent 6d2fecdd9a
commit 3320d19494
8 changed files with 249 additions and 138 deletions

View File

@ -89,7 +89,7 @@ public abstract class ExchangeSession {
protected static final String PUBLIC_ROOT = "/public"; protected static final String PUBLIC_ROOT = "/public";
protected static final String CALENDAR = "calendar"; 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 ADDRESSBOOK = "addressbook";
protected static final String INBOX = "INBOX"; protected static final String INBOX = "INBOX";
protected static final String LOWER_CASE_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 abstract BufferedReader getContentReader(Message message) throws IOException;
protected static final List<String> POP_MESSAGE_ATTRIBUTES = new ArrayList<String>(); protected static final Set<String> POP_MESSAGE_ATTRIBUTES = new HashSet<String>();
static { static {
POP_MESSAGE_ATTRIBUTES.add("uid"); POP_MESSAGE_ATTRIBUTES.add("uid");
@ -531,7 +531,7 @@ public abstract class ExchangeSession {
return searchMessages(folderName, POP_MESSAGE_ATTRIBUTES, null); return searchMessages(folderName, POP_MESSAGE_ATTRIBUTES, null);
} }
protected static final List<String> IMAP_MESSAGE_ATTRIBUTES = new ArrayList<String>(); protected static final Set<String> IMAP_MESSAGE_ATTRIBUTES = new HashSet<String>();
static { static {
IMAP_MESSAGE_ATTRIBUTES.add("uid"); IMAP_MESSAGE_ATTRIBUTES.add("uid");
@ -546,7 +546,7 @@ public abstract class ExchangeSession {
IMAP_MESSAGE_ATTRIBUTES.add("date"); IMAP_MESSAGE_ATTRIBUTES.add("date");
} }
protected static final List<String> UID_MESSAGE_ATTRIBUTES = new ArrayList<String>(); protected static final Set<String> UID_MESSAGE_ATTRIBUTES = new HashSet<String>();
static { static {
UID_MESSAGE_ATTRIBUTES.add("uid"); UID_MESSAGE_ATTRIBUTES.add("uid");
@ -584,7 +584,7 @@ public abstract class ExchangeSession {
* @return message list * @return message list
* @throws IOException on error * @throws IOException on error
*/ */
public abstract MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException; public abstract MessageList searchMessages(String folderName, Set<String> attributes, Condition condition) throws IOException;
protected enum Operator { protected enum Operator {
Or, And, Not, IsEqualTo, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse, Or, And, Not, IsEqualTo, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse,
@ -1741,13 +1741,12 @@ public abstract class ExchangeSession {
writer.appendProperty("X-EVOLUTION-SPOUSE", get("spousecn")); writer.appendProperty("X-EVOLUTION-SPOUSE", get("spousecn"));
String lastModified = get("lastmodified"); String lastModified = get("lastmodified");
if (lastModified != null) { if (lastModified != null) {
try { try {
writer.appendProperty("REV", getZuluDateFormat().format(getExchangeZuluDateFormatMillisecond().parse(lastModified))); writer.appendProperty("REV", getZuluDateFormat().format(getExchangeZuluDateFormatMillisecond().parse(lastModified)));
} catch (ParseException e) { } catch (ParseException e) {
LOGGER.warn("Invalid date: "+lastModified); LOGGER.warn("Invalid date: " + lastModified);
} }
} }
writer.endCard(); writer.endCard();
@ -2392,7 +2391,7 @@ public abstract class ExchangeSession {
} }
public static final List<String> ITEM_PROPERTIES = new ArrayList<String>(); public static final Set<String> ITEM_PROPERTIES = new HashSet<String>();
static { static {
ITEM_PROPERTIES.add("etag"); ITEM_PROPERTIES.add("etag");
@ -2422,7 +2421,7 @@ public abstract class ExchangeSession {
* @return list of contacts * @return list of contacts
* @throws IOException on error * @throws IOException on error
*/ */
protected abstract List<Contact> searchContacts(String folderPath, List<String> attributes, Condition condition) throws IOException; public abstract List<Contact> searchContacts(String folderPath, Set<String> attributes, Condition condition) throws IOException;
/** /**
* Search calendar messages in provided folder. * Search calendar messages in provided folder.
@ -2478,7 +2477,7 @@ public abstract class ExchangeSession {
* @return list of calendar messages as Event objects * @return list of calendar messages as Event objects
* @throws IOException on error * @throws IOException on error
*/ */
protected abstract List<Event> searchEvents(String folderPath, List<String> attributes, Condition condition) throws IOException; protected abstract List<Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException;
/** /**
* convert vcf extension to EML. * convert vcf extension to EML.
@ -2858,18 +2857,7 @@ public abstract class ExchangeSession {
return results; return results;
} }
/** public static final Set<String> CONTACT_ATTRIBUTES = new HashSet<String>();
* Search users in contacts folder by uid.
*
* @param uid unique id
* @return List of users
* @throws IOException on error
*/
public Map<String, Map<String, String>> contactFindByUid(String uid) throws IOException {
return contactFind(equals("uid", uid));
}
protected static final List<String> CONTACT_ATTRIBUTES = new ArrayList<String>();
static { static {
CONTACT_ATTRIBUTES.add("uid"); CONTACT_ATTRIBUTES.add("uid");
@ -2919,87 +2907,6 @@ public abstract class ExchangeSession {
CONTACT_ATTRIBUTES.add("lastmodified"); CONTACT_ATTRIBUTES.add("lastmodified");
} }
/**
* Search users in contacts folder
*
* @param condition search filter
* @return List of users
* @throws IOException on error
*/
public Map<String, Map<String, String>> 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<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();
List<Contact> contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("outlookmessageclass", "IPM.Contact"), condition));
Map<String, String> item;
for (Contact contact : contacts) {
item = new HashMap<String, String>();
for (Map.Entry<String, String> 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. * Get extended address book information for person with gallookup.
* Does not work with Exchange 2007 * Does not work with Exchange 2007

View File

@ -117,6 +117,10 @@ public class DavExchangeSession extends ExchangeSession {
exchangeFolderPath = mailPath + draftsName + folderPath.substring(DRAFTS.length()); exchangeFolderPath = mailPath + draftsName + folderPath.substring(DRAFTS.length());
} else if (folderPath.startsWith(SENT)) { } else if (folderPath.startsWith(SENT)) {
exchangeFolderPath = mailPath + sentitemsName + folderPath.substring(SENT.length()); 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")) { } else if (folderPath.startsWith("public")) {
exchangeFolderPath = publicFolderUrl + folderPath.substring("public".length()); exchangeFolderPath = publicFolderUrl + folderPath.substring("public".length());
@ -415,9 +419,13 @@ public class DavExchangeSession extends ExchangeSession {
} }
public void appendTo(StringBuilder buffer) { 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)); buffer.append(operatorMap.get(operator));
if (!isIntValue) { //noinspection VariableNotUsedInsideIf
if (field.cast != null) {
buffer.append("CAST (\"");
} else if (!isIntValue) {
buffer.append('\''); buffer.append('\'');
} }
if (Operator.Like == operator) { if (Operator.Like == operator) {
@ -427,7 +435,9 @@ public class DavExchangeSession extends ExchangeSession {
if (Operator.Like == operator || Operator.StartsWith == operator) { if (Operator.Like == operator || Operator.StartsWith == operator) {
buffer.append('%'); buffer.append('%');
} }
if (!isIntValue) { if (field.cast != null) {
buffer.append("\" as '").append(field.cast).append("')");
} else if (!isIntValue) {
buffer.append('\''); buffer.append('\'');
} }
} }
@ -901,7 +911,7 @@ public class DavExchangeSession extends ExchangeSession {
return folder; return folder;
} }
protected static final List<String> FOLDER_PROPERTIES = new ArrayList<String>(); protected static final Set<String> FOLDER_PROPERTIES = new HashSet<String>();
static { static {
FOLDER_PROPERTIES.add("displayname"); FOLDER_PROPERTIES.add("displayname");
@ -1082,7 +1092,7 @@ public class DavExchangeSession extends ExchangeSession {
} }
@Override @Override
public MessageList searchMessages(String folderPath, List<String> attributes, Condition condition) throws IOException { public MessageList searchMessages(String folderPath, Set<String> attributes, Condition condition) throws IOException {
MessageList messages = new MessageList(); MessageList messages = new MessageList();
MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow); MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow);
@ -1099,9 +1109,11 @@ public class DavExchangeSession extends ExchangeSession {
* @inheritDoc * @inheritDoc
*/ */
@Override @Override
protected List<ExchangeSession.Contact> searchContacts(String folderPath, List<String> attributes, Condition condition) throws IOException { public List<ExchangeSession.Contact> searchContacts(String folderPath, Set<String> attributes, Condition condition) throws IOException {
List<ExchangeSession.Contact> contacts = new ArrayList<ExchangeSession.Contact>(); List<ExchangeSession.Contact> contacts = new ArrayList<ExchangeSession.Contact>();
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) { for (MultiStatusResponse response : responses) {
contacts.add(new Contact(response)); contacts.add(new Contact(response));
} }
@ -1109,7 +1121,7 @@ public class DavExchangeSession extends ExchangeSession {
} }
@Override @Override
protected List<ExchangeSession.Event> searchEvents(String folderPath, List<String> attributes, Condition condition) throws IOException { protected List<ExchangeSession.Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
List<ExchangeSession.Event> events = new ArrayList<ExchangeSession.Event>(); List<ExchangeSession.Event> events = new ArrayList<ExchangeSession.Event>();
MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow); MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow);
for (MultiStatusResponse response : responses) { for (MultiStatusResponse response : responses) {
@ -1133,7 +1145,7 @@ public class DavExchangeSession extends ExchangeSession {
return events; return events;
} }
protected MultiStatusResponse[] searchItems(String folderPath, List<String> attributes, Condition condition, FolderQueryTraversal folderQueryTraversal) throws IOException { protected MultiStatusResponse[] searchItems(String folderPath, Set<String> attributes, Condition condition, FolderQueryTraversal folderQueryTraversal) throws IOException {
String folderUrl = getFolderPath(folderPath); String folderUrl = getFolderPath(folderPath);
StringBuilder searchRequest = new StringBuilder(); StringBuilder searchRequest = new StringBuilder();
searchRequest.append("SELECT ") searchRequest.append("SELECT ")
@ -1237,7 +1249,7 @@ public class DavExchangeSession extends ExchangeSession {
if ("urn:content-classes:person".equals(contentClass)) { if ("urn:content-classes:person".equals(contentClass)) {
// retrieve Contact properties // retrieve Contact properties
// TODO: need to check list size // 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) } else if ("urn:content-classes:appointment".equals(contentClass)
|| "urn:content-classes:calendarmessage".equals(contentClass)) { || "urn:content-classes:calendarmessage".equals(contentClass)) {
return new Event(responses[0]); return new Event(responses[0]);
@ -1330,7 +1342,7 @@ public class DavExchangeSession extends ExchangeSession {
String timezoneId = null; String timezoneId = null;
try { try {
ArrayList<String> attributes = new ArrayList<String>(); Set<String> attributes = new HashSet<String>();
attributes.add("roamingdictionary"); attributes.add("roamingdictionary");
MultiStatusResponse[] responses = searchItems("/users/" + getEmail() + "/NON_IPM_SUBTREE", attributes, equals("messageclass", "IPM.Configuration.OWA.UserOptions"), DavExchangeSession.FolderQueryTraversal.Deep); MultiStatusResponse[] responses = searchItems("/users/" + getEmail() + "/NON_IPM_SUBTREE", attributes, equals("messageclass", "IPM.Configuration.OWA.UserOptions"), DavExchangeSession.FolderQueryTraversal.Deep);

View File

@ -18,6 +18,7 @@
*/ */
package davmail.exchange.dav; package davmail.exchange.dav;
import org.apache.commons.codec.binary.Hex;
import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty; import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
@ -111,8 +112,10 @@ public class Field {
createField(DAV, "isfolder"); 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 // POP and IMAP message
createField(DAV, "uid");
createField("messageSize", 0x0e08, PropertyType.Long);//PR_MESSAGE_SIZE createField("messageSize", 0x0e08, PropertyType.Long);//PR_MESSAGE_SIZE
createField("imapUid", 0x0e23, PropertyType.Long);//PR_INTERNET_ARTICLE_NUMBER createField("imapUid", 0x0e23, PropertyType.Long);//PR_INTERNET_ARTICLE_NUMBER
createField("junk", 0x1083, PropertyType.Long); createField("junk", 0x1083, PropertyType.Long);
@ -238,9 +241,22 @@ public class Field {
createField(DAV, "ishidden"); 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) { protected static void createField(String alias, int propertyTag, PropertyType propertyType) {
String name = 'x' + Integer.toHexString(propertyTag) + propertyTypeMap.get(propertyType); String name = 'x' + toHexString(propertyTag) + propertyTypeMap.get(propertyType);
Field field = new Field(alias, SCHEMAS_MAPI_PROPTAG, name); 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); fieldMap.put(field.alias, field);
} }
@ -251,15 +267,15 @@ public class Field {
name = String.valueOf(propertyTag); name = String.valueOf(propertyTag);
} else { } else {
// Common namespace expects hex names // 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() + 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); fieldMap.put(field.alias, field);
} }
protected static void createField(String alias, DistinguishedPropertySetType propertySetType, int propertyTag, PropertyType propertyType) { 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() + Field field = new Field(alias, Namespace.getNamespace(SCHEMAS_MAPI_ID.getURI() +
'{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name); '{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name);
@ -281,6 +297,7 @@ public class Field {
protected final String uri; protected final String uri;
protected final String requestPropertyString; protected final String requestPropertyString;
protected final DavPropertyName responsePropertyName; protected final DavPropertyName responsePropertyName;
protected final String cast;
public Field(Namespace namespace, String name) { public Field(Namespace namespace, String name) {
@ -288,10 +305,10 @@ public class Field {
} }
public Field(String alias, Namespace namespace, String name) { 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); davPropertyName = DavPropertyName.create(name, namespace);
this.alias = alias; this.alias = alias;
this.uri = namespace.getURI() + name; this.uri = namespace.getURI() + name;
@ -302,6 +319,7 @@ public class Field {
this.requestPropertyString = '"' + uri + "\" as " + responseAlias; this.requestPropertyString = '"' + uri + "\" as " + responseAlias;
this.responsePropertyName = DavPropertyName.create(responseAlias, EMPTY); this.responsePropertyName = DavPropertyName.create(responseAlias, EMPTY);
} }
this.cast = cast;
} }
public String getUri() { public String getUri() {

View File

@ -138,7 +138,7 @@ public class EwsExchangeSession extends ExchangeSession {
} }
@Override @Override
public MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException { public MessageList searchMessages(String folderName, Set<String> attributes, Condition condition) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -468,12 +468,12 @@ public class EwsExchangeSession extends ExchangeSession {
} }
@Override @Override
protected List<Contact> searchContacts(String folderName, List<String> attributes, Condition condition) throws IOException { public List<Contact> searchContacts(String folderName, Set<String> attributes, Condition condition) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
protected List<Event> searchEvents(String folderPath, List<String> attributes, Condition condition) throws IOException { protected List<Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -28,6 +28,10 @@ import davmail.exception.DavMailException;
import davmail.exchange.ExchangeSession; import davmail.exchange.ExchangeSession;
import davmail.exchange.ExchangeSessionFactory; import davmail.exchange.ExchangeSessionFactory;
import davmail.ui.tray.DavGatewayTray; 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.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -35,6 +39,8 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.*; import java.net.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
/** /**
@ -1021,16 +1027,26 @@ public class LdapConnection extends AbstractConnection {
ExchangeSession.Condition condition; 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) { if (operator == LDAP_FILTER_EQUALITY) {
condition = session.equals(contactAttributeName, value); condition = session.equals(contactAttributeName, actualValue);
} else if ("*".equals(value)) { } else if ("*".equals(actualValue)) {
condition = session.not(session.isNull(contactAttributeName)); condition = session.not(session.isNull(contactAttributeName));
} else { } else {
// endsWith not supported by exchange, convert to contains // endsWith not supported by exchange, convert to contains
if (mode == LDAP_SUBSTRING_FINAL || mode == LDAP_SUBSTRING_ANY) { if (mode == LDAP_SUBSTRING_FINAL || mode == LDAP_SUBSTRING_ANY) {
condition = session.contains(contactAttributeName, value); condition = session.contains(contactAttributeName, actualValue);
} else { } else {
condition = session.startsWith(contactAttributeName, value); condition = session.startsWith(contactAttributeName, actualValue);
} }
} }
return condition; return condition;
@ -1157,11 +1173,17 @@ public class LdapConnection extends AbstractConnection {
if (session != null) { if (session != null) {
// single user request // single user request
String uid = dn.substring("uid=".length(), dn.indexOf(',')); String uid = dn.substring("uid=".length(), dn.indexOf(','));
Map<String, Map<String, String>> persons = null;
// first search in contact // first search in contact
Map<String, Map<String, String>> 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 // then in GAL
if (persons.isEmpty()) { if (persons == null || persons.isEmpty()) {
persons = session.galFind("AN", uid); persons = session.galFind("AN", uid);
Map<String, String> person = persons.get(uid.toLowerCase()); Map<String, String> person = persons.get(uid.toLowerCase());
@ -1190,7 +1212,7 @@ public class LdapConnection extends AbstractConnection {
Map<String, Map<String, String>> persons = new HashMap<String, Map<String, String>>(); Map<String, Map<String, String>> persons = new HashMap<String, Map<String, String>>();
if (ldapFilter.isFullSearch()) { if (ldapFilter.isFullSearch()) {
// append personal contacts first // append personal contacts first
for (Map<String, String> person : session.contactFind(null).values()) { for (Map<String, String> person : contactFind(null, returningAttributes).values()) {
persons.put(person.get("uid"), person); persons.put(person.get("uid"), person);
if (persons.size() == sizeLimit) { if (persons.size() == sizeLimit) {
break; break;
@ -1217,7 +1239,7 @@ public class LdapConnection extends AbstractConnection {
// if ldapfilter is not a full search and filter is null, // if ldapfilter is not a full search and filter is null,
// ignored all attribute filters => return empty results // ignored all attribute filters => return empty results
if (ldapFilter.isFullSearch() || filter != null) { if (ldapFilter.isFullSearch() || filter != null) {
for (Map<String, String> person : session.contactFind(filter).values()) { for (Map<String, String> person : contactFind(filter, returningAttributes).values()) {
persons.put(person.get("uid"), person); persons.put(person.get("uid"), person);
if (persons.size() == sizeLimit) { 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<String, Map<String, String>> contactFind(ExchangeSession.Condition condition, Set<String> returningAttributes) throws IOException {
Set<String> ldapReturningAttributes;
if (returningAttributes != null && !returningAttributes.isEmpty()) {
ldapReturningAttributes = new HashSet<String>();
// always return uid
ldapReturningAttributes.add("uid");
for (String attribute : returningAttributes) {
if (!"objectclass".equals(attribute)) {
ldapReturningAttributes.add(attribute);
}
}
} else {
ldapReturningAttributes = ExchangeSession.CONTACT_ATTRIBUTES;
}
Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();
List<ExchangeSession.Contact> 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 * Convert to LDAP attributes and send entry
* *

View File

@ -18,6 +18,10 @@
*/ */
package davmail.util; 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.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -169,4 +173,33 @@ public final class StringUtil {
return result; 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;
}
} }

View File

@ -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<ExchangeSession.Contact> 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<String> attributes = new HashSet<String>();
attributes.add("uid");
List<ExchangeSession.Contact> contacts = session.searchContacts(ExchangeSession.CONTACTS, attributes, null);
for (ExchangeSession.Contact contact : contacts) {
System.out.println(contact);
}
}
public void testSearchContactsByUid() throws IOException {
Set<String> attributes = new HashSet<String>();
attributes.add("uid");
List<ExchangeSession.Contact> 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"))));
}
}
}

View File

@ -54,6 +54,9 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase {
assertEquals(mailPath + davSession.sentitemsName, davSession.getFolderPath("Sent")); assertEquals(mailPath + davSession.sentitemsName, davSession.getFolderPath("Sent"));
assertEquals(mailPath + davSession.draftsName, davSession.getFolderPath("Drafts")); 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.inboxName + "/test", davSession.getFolderPath("INBOX/test"));
assertEquals(mailPath + davSession.deleteditemsName + "/test", davSession.getFolderPath("Trash/test")); assertEquals(mailPath + davSession.deleteditemsName + "/test", davSession.getFolderPath("Trash/test"));
assertEquals(mailPath + davSession.sentitemsName + "/test", davSession.getFolderPath("Sent/test")); assertEquals(mailPath + davSession.sentitemsName + "/test", davSession.getFolderPath("Sent/test"));
@ -99,7 +102,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase {
} }
public void testGetCategoryList() throws IOException { public void testGetCategoryList() throws IOException {
ArrayList<String> attributes = new ArrayList<String>(); Set<String> attributes = new HashSet<String>();
attributes.add("permanenturl"); attributes.add("permanenturl");
attributes.add("roamingxmlstream"); 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); 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 { public void testGetCalendarOptions() throws IOException {
ArrayList<String> attributes = new ArrayList<String>(); Set<String> attributes = new HashSet<String>();
attributes.add("permanenturl"); attributes.add("permanenturl");
attributes.add("roamingxmlstream"); 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); 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 { public void testAllHidden() throws IOException {
ArrayList<String> attributes = new ArrayList<String>(); Set<String> attributes = new HashSet<String>();
attributes.add("messageclass"); attributes.add("messageclass");
attributes.add("permanenturl"); attributes.add("permanenturl");
attributes.add("roamingxmlstream"); attributes.add("roamingxmlstream");
@ -139,7 +142,7 @@ public class TestDavExchangeSession extends AbstractExchangeSessionTestCase {
} }
public void testNonIpmSubtree() throws IOException { public void testNonIpmSubtree() throws IOException {
ArrayList<String> attributes = new ArrayList<String>(); Set<String> attributes = new HashSet<String>();
attributes.add("messageclass"); attributes.add("messageclass");
attributes.add("permanenturl"); attributes.add("permanenturl");
attributes.add("roamingxmlstream"); attributes.add("roamingxmlstream");