LDAP: move galLookup to DavExchangeSession

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1333 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-08-09 22:19:15 +00:00
parent 905870f7da
commit 3d98a34328
6 changed files with 119 additions and 98 deletions

View File

@ -118,7 +118,6 @@ public abstract class ExchangeSession {
private final String userName; private final String userName;
private boolean disableGalLookup;
protected static final String YYYY_MM_DD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss"; protected static final String YYYY_MM_DD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";
private static final String YYYYMMDD_T_HHMMSS_Z = "yyyyMMdd'T'HHmmss'Z'"; private static final String YYYYMMDD_T_HHMMSS_Z = "yyyyMMdd'T'HHmmss'Z'";
protected static final String YYYY_MM_DD_T_HHMMSS_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'"; protected static final String YYYY_MM_DD_T_HHMMSS_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'";
@ -2816,7 +2815,7 @@ public abstract class ExchangeSession {
return results; return results;
} }
public abstract Map<String, Contact> galFind(Condition condition) throws IOException; public abstract Map<String, Contact> galFind(Condition condition, Set<String> returningAttributes, int sizeLimit) throws IOException;
/** /**
* Full Contact attribute list * Full Contact attribute list
@ -2890,39 +2889,6 @@ public abstract class ExchangeSession {
CONTACT_ATTRIBUTES.add("fburl"); CONTACT_ATTRIBUTES.add("fburl");
} }
/**
* Get extended address book information for person with gallookup.
* Does not work with Exchange 2007
*
* @param person person attributes map
*/
public void galLookup(Map<String, String> person) {
if (!disableGalLookup) {
GetMethod getMethod = null;
try {
getMethod = new GetMethod(URIUtil.encodePathQuery(getCmdBasePath() + "?Cmd=gallookup&ADDR=" + person.get("EM")));
DavGatewayHttpClientFacade.executeGetMethod(httpClient, getMethod, true);
Map<String, Map<String, String>> results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "person", "alias");
// add detailed information
if (!results.isEmpty()) {
Map<String, String> fullperson = results.get(person.get("AN").toLowerCase());
if (fullperson != null) {
for (Map.Entry<String, String> entry : fullperson.entrySet()) {
person.put(entry.getKey(), entry.getValue());
}
}
}
} catch (IOException e) {
LOGGER.warn("Unable to gallookup person: " + person + ", disable GalLookup");
disableGalLookup = true;
} finally {
if (getMethod != null) {
getMethod.releaseConnection();
}
}
}
}
/** /**
* Get freebusy data string from Exchange. * Get freebusy data string from Exchange.
* *

View File

@ -198,6 +198,20 @@ public class DavExchangeSession extends ExchangeSession {
GALFIND_CRITERIA_MAP.put("apple-group-realname", "DP"); GALFIND_CRITERIA_MAP.put("apple-group-realname", "DP");
} }
static final HashSet<String> GALLOOKUP_ATTRIBUTES = new HashSet<String>();
static {
GALLOOKUP_ATTRIBUTES.add("givenname");
GALLOOKUP_ATTRIBUTES.add("initials");
GALLOOKUP_ATTRIBUTES.add("sn");
GALLOOKUP_ATTRIBUTES.add("street");
GALLOOKUP_ATTRIBUTES.add("st");
GALLOOKUP_ATTRIBUTES.add("postalcode");
GALLOOKUP_ATTRIBUTES.add("c");
GALLOOKUP_ATTRIBUTES.add("departement");
GALLOOKUP_ATTRIBUTES.add("mobile");
}
/** /**
* Exchange to LDAP attribute map * Exchange to LDAP attribute map
*/ */
@ -225,7 +239,7 @@ public class DavExchangeSession extends ExchangeSession {
} }
@Override @Override
public Map<String, ExchangeSession.Contact> galFind(Condition condition) throws IOException { public Map<String, ExchangeSession.Contact> galFind(Condition condition, Set<String> returningAttributes, int sizeLimit) throws IOException {
Map<String, ExchangeSession.Contact> contacts = new HashMap<String, ExchangeSession.Contact>(); Map<String, ExchangeSession.Contact> contacts = new HashMap<String, ExchangeSession.Contact>();
if (condition instanceof MultiCondition) { if (condition instanceof MultiCondition) {
// TODO // TODO
@ -242,9 +256,15 @@ public class DavExchangeSession extends ExchangeSession {
getMethod.releaseConnection(); getMethod.releaseConnection();
} }
LOGGER.debug("galfind " + searchAttribute + '=' + searchValue + ": " + results.size() + " result(s)"); LOGGER.debug("galfind " + searchAttribute + '=' + searchValue + ": " + results.size() + " result(s)");
for (Map<String, String> result:results.values()) { for (Map<String, String> result : results.values()) {
Contact contact = buildGalfindContact(result); Contact contact = new Contact();
contact.setName(result.get("AN"));
contact.put("uid", result.get("AN"));
buildGalfindContact(contact, result);
if (condition.isMatch(contact)) { if (condition.isMatch(contact)) {
if (needGalLookup(returningAttributes)) {
galLookup(contact);
}
contacts.put(contact.getName().toLowerCase(), contact); contacts.put(contact.getName().toLowerCase(), contact);
} }
} }
@ -254,17 +274,63 @@ public class DavExchangeSession extends ExchangeSession {
return contacts; return contacts;
} }
protected Contact buildGalfindContact(Map<String, String> response) { protected boolean needGalLookup(Set<String> returningAttributes) {
Contact contact = new Contact(); // iCal search, do not call gallookup
contact.setName(response.get("AN")); if (returningAttributes.contains("apple-serviceslocator")) {
contact.put("uid", response.get("AN")); return false;
// return all attributes => call gallookup
} else if (returningAttributes.isEmpty()) {
return true;
}
for (String attributeName : GALLOOKUP_ATTRIBUTES) {
if (returningAttributes.contains(attributeName)) {
return true;
}
}
return false;
}
private boolean disableGalLookup;
/**
* Get extended address book information for person with gallookup.
* Does not work with Exchange 2007
*
* @param contact galfind contact
*/
public void galLookup(Contact contact) {
if (!disableGalLookup) {
GetMethod getMethod = null;
try {
getMethod = new GetMethod(URIUtil.encodePathQuery(getCmdBasePath() + "?Cmd=gallookup&ADDR=" + contact.get("smtpemail1")));
DavGatewayHttpClientFacade.executeGetMethod(httpClient, getMethod, true);
Map<String, Map<String, String>> results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "person", "alias");
// add detailed information
if (!results.isEmpty()) {
Map<String, String> personGalLookupDetails = results.get(contact.get("uid").toLowerCase());
if (personGalLookupDetails != null) {
buildGalfindContact(contact, personGalLookupDetails);
}
}
} catch (IOException e) {
LOGGER.warn("Unable to gallookup person: " + contact + ", disable GalLookup");
disableGalLookup = true;
} finally {
if (getMethod != null) {
getMethod.releaseConnection();
}
}
}
}
protected void buildGalfindContact(Contact contact, Map<String, String> response) {
for (Map.Entry<String, String> entry : GALFIND_ATTRIBUTE_MAP.entrySet()) { for (Map.Entry<String, String> entry : GALFIND_ATTRIBUTE_MAP.entrySet()) {
String attributeValue = response.get(entry.getValue()); String attributeValue = response.get(entry.getValue());
if (attributeValue != null) { if (attributeValue != null) {
contact.put(entry.getKey(), attributeValue); contact.put(entry.getKey(), attributeValue);
} }
} }
return contact;
} }
@Override @Override
@ -528,6 +594,15 @@ public class DavExchangeSession extends ExchangeSession {
} }
if ("urlcompname".equals(field.alias)) { if ("urlcompname".equals(field.alias)) {
buffer.append(StringUtil.encodeUrlcompname(value)); buffer.append(StringUtil.encodeUrlcompname(value));
} else if (field.isIntValue()) {
// check value
try {
Integer.parseInt(value);
buffer.append(value);
} catch (NumberFormatException e) {
// invalid value, replace with 0
buffer.append('0');
}
} else { } else {
buffer.append(value); buffer.append(value);
} }

View File

@ -1280,17 +1280,17 @@ public class EwsExchangeSession extends ExchangeSession {
return contact; return contact;
} }
public Map<String, ExchangeSession.Contact> galFind(Condition condition) throws IOException { public Map<String, ExchangeSession.Contact> galFind(Condition condition, Set<String> returningAttributes, int sizeLimit) throws IOException {
Map<String, ExchangeSession.Contact> contacts = new HashMap<String, ExchangeSession.Contact>(); Map<String, ExchangeSession.Contact> contacts = new HashMap<String, ExchangeSession.Contact>();
if (condition instanceof MultiCondition) { if (condition instanceof MultiCondition) {
List<Condition> conditions = ((MultiCondition) condition).getConditions(); List<Condition> conditions = ((MultiCondition) condition).getConditions();
Operator operator = ((MultiCondition) condition).getOperator(); Operator operator = ((MultiCondition) condition).getOperator();
if (operator == Operator.Or) { if (operator == Operator.Or) {
for (Condition innerCondition : conditions) { for (Condition innerCondition : conditions) {
contacts.putAll(galFind(innerCondition)); contacts.putAll(galFind(innerCondition, returningAttributes, sizeLimit));
} }
} else if (operator == Operator.And && !conditions.isEmpty()) { } else if (operator == Operator.And && !conditions.isEmpty()) {
Map<String, ExchangeSession.Contact> innerContacts = galFind(conditions.get(0)); Map<String, ExchangeSession.Contact> innerContacts = galFind(conditions.get(0), returningAttributes, sizeLimit);
for (ExchangeSession.Contact contact : innerContacts.values()) { for (ExchangeSession.Contact contact : innerContacts.values()) {
if (condition.isMatch(contact)) { if (condition.isMatch(contact)) {
contacts.put(contact.getName().toLowerCase(), contact); contacts.put(contact.getName().toLowerCase(), contact);

View File

@ -186,20 +186,6 @@ public class LdapConnection extends AbstractConnection {
STATIC_ATTRIBUTE_MAP.put("apple-serviceslocator", COMPUTER_GUID + ':' + VIRTUALHOST_GUID + ":calendar"); STATIC_ATTRIBUTE_MAP.put("apple-serviceslocator", COMPUTER_GUID + ':' + VIRTUALHOST_GUID + ":calendar");
} }
static final HashSet<String> EXTENDED_ATTRIBUTES = new HashSet<String>();
static {
EXTENDED_ATTRIBUTES.add("givenname");
EXTENDED_ATTRIBUTES.add("initials");
EXTENDED_ATTRIBUTES.add("sn");
EXTENDED_ATTRIBUTES.add("street");
EXTENDED_ATTRIBUTES.add("st");
EXTENDED_ATTRIBUTES.add("postalcode");
EXTENDED_ATTRIBUTES.add("c");
EXTENDED_ATTRIBUTES.add("departement");
EXTENDED_ATTRIBUTES.add("mobile");
}
/** /**
* LDAP to Exchange Criteria Map * LDAP to Exchange Criteria Map
*/ */
@ -846,7 +832,7 @@ public class LdapConnection extends AbstractConnection {
static interface LdapFilter { static interface LdapFilter {
ExchangeSession.Condition getContactSearchFilter(); ExchangeSession.Condition getContactSearchFilter();
Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session) throws IOException; Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException;
void add(LdapFilter filter); void add(LdapFilter filter);
@ -973,11 +959,11 @@ public class LdapConnection extends AbstractConnection {
* @return persons map * @return persons map
* @throws IOException on error * @throws IOException on error
*/ */
public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session) throws IOException { public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException {
Map<String, ExchangeSession.Contact> persons = null; Map<String, ExchangeSession.Contact> persons = null;
for (LdapFilter child : criteria) { for (LdapFilter child : criteria) {
Map<String, ExchangeSession.Contact> childFind = child.findInGAL(session); Map<String, ExchangeSession.Contact> childFind = child.findInGAL(session, returningAttributes, sizeLimit);
if (childFind != null) { if (childFind != null) {
if (persons == null) { if (persons == null) {
@ -1091,15 +1077,7 @@ public class LdapConnection extends AbstractConnection {
ExchangeSession.Condition condition = null; ExchangeSession.Condition condition = null;
if (operator == LDAP_FILTER_EQUALITY) { if (operator == LDAP_FILTER_EQUALITY) {
try { condition = session.isEqualTo(contactAttributeName, value);
// check imapUid value
if ("imapUid".equals(contactAttributeName)) {
Integer.parseInt(value);
}
condition = session.isEqualTo(contactAttributeName, value);
} catch (NumberFormatException e) {
// ignore condition
}
} else if ("*".equals(value)) { } else if ("*".equals(value)) {
condition = session.not(session.isNull(contactAttributeName)); condition = session.not(session.isNull(contactAttributeName));
// do not allow substring search on integer field imapUid // do not allow substring search on integer field imapUid
@ -1120,7 +1098,7 @@ public class LdapConnection extends AbstractConnection {
return true; return true;
} }
String personAttributeValue = person.get(getGalFindAttributeName()); String personAttributeValue = person.get(attributeName);
if (personAttributeValue == null) { if (personAttributeValue == null) {
// No value to allow for filter match // No value to allow for filter match
@ -1139,7 +1117,7 @@ public class LdapConnection extends AbstractConnection {
return false; return false;
} }
public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session) throws IOException { public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException {
if (canIgnore) { if (canIgnore) {
return null; return null;
} }
@ -1148,7 +1126,7 @@ public class LdapConnection extends AbstractConnection {
if (galFindAttributeName != null) { if (galFindAttributeName != null) {
// quick fix for cn=* filter // quick fix for cn=* filter
Map<String, ExchangeSession.Contact> galPersons = session.galFind(session.startsWith(attributeName, "*".equals(value) ? "A" : value)); Map<String, ExchangeSession.Contact> galPersons = session.galFind(session.startsWith(attributeName, "*".equals(value) ? "A" : value), returningAttributes, sizeLimit);
if (operator == LDAP_FILTER_EQUALITY) { if (operator == LDAP_FILTER_EQUALITY) {
// Make sure only exact matches are returned // Make sure only exact matches are returned
@ -1278,7 +1256,7 @@ public class LdapConnection extends AbstractConnection {
// then in GAL // then in GAL
if (persons == null || persons.isEmpty()) { if (persons == null || persons.isEmpty()) {
persons = session.galFind(session.isEqualTo("uid", uid)); persons = session.galFind(session.isEqualTo("uid", uid), returningAttributes, sizeLimit);
ExchangeSession.Contact person = persons.get(uid.toLowerCase()); ExchangeSession.Contact person = persons.get(uid.toLowerCase());
// filter out non exact results // filter out non exact results
@ -1315,7 +1293,7 @@ public class LdapConnection extends AbstractConnection {
// full search // full search
for (char c = 'A'; c < 'Z'; c++) { for (char c = 'A'; c < 'Z'; c++) {
if (!abandon && persons.size() < sizeLimit) { if (!abandon && persons.size() < sizeLimit) {
for (ExchangeSession.Contact person : session.galFind(session.startsWith("uid", String.valueOf(c))).values()) { for (ExchangeSession.Contact person : session.galFind(session.startsWith("uid", String.valueOf(c)), returningAttributes, sizeLimit).values()) {
persons.put(person.get("uid"), person); persons.put(person.get("uid"), person);
if (persons.size() == sizeLimit) { if (persons.size() == sizeLimit) {
break; break;
@ -1341,7 +1319,7 @@ public class LdapConnection extends AbstractConnection {
} }
} }
if (!abandon && persons.size() < sizeLimit) { if (!abandon && persons.size() < sizeLimit) {
for (ExchangeSession.Contact person : ldapFilter.findInGAL(session).values()) { for (ExchangeSession.Contact person : ldapFilter.findInGAL(session, returningAttributes, sizeLimit).values()) {
if (persons.size() == sizeLimit) { if (persons.size() == sizeLimit) {
break; break;
} }
@ -1446,19 +1424,6 @@ public class LdapConnection extends AbstractConnection {
boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.isEmpty(); boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.isEmpty();
boolean iCalSearch = returningAttributes.contains("apple-serviceslocator"); boolean iCalSearch = returningAttributes.contains("apple-serviceslocator");
boolean returnAllAttributes = returningAttributes.isEmpty(); boolean returnAllAttributes = returningAttributes.isEmpty();
boolean needDetails = returnAllAttributes;
if (!needDetails) {
for (String attributeName : EXTENDED_ATTRIBUTES) {
if (returningAttributes.contains(attributeName)) {
needDetails = true;
break;
}
}
}
// iCal search, do not lookup details
if (iCalSearch) {
needDetails = false;
}
for (ExchangeSession.Contact person : persons.values()) { for (ExchangeSession.Contact person : persons.values()) {
if (abandon) { if (abandon) {

View File

@ -48,20 +48,20 @@ public class TestEwsExchangeSession extends AbstractExchangeSessionTestCase {
public void testGalFind() throws IOException { public void testGalFind() throws IOException {
// find a set of contacts // find a set of contacts
Map<String, ExchangeSession.Contact> contacts = ewsSession.galFind(ewsSession.startsWith("cn", "a")); Map<String, ExchangeSession.Contact> contacts = ewsSession.galFind(ewsSession.startsWith("cn", "a"), null, 100);
for (ExchangeSession.Contact contact : contacts.values()) { for (ExchangeSession.Contact contact : contacts.values()) {
System.out.println(contact); System.out.println(contact);
} }
if (!contacts.isEmpty()) { if (!contacts.isEmpty()) {
ExchangeSession.Contact testContact = contacts.values().iterator().next(); ExchangeSession.Contact testContact = contacts.values().iterator().next();
contacts = ewsSession.galFind(ewsSession.isEqualTo("cn", testContact.get("cn"))); contacts = ewsSession.galFind(ewsSession.isEqualTo("cn", testContact.get("cn")), null, 100);
assertEquals(1, contacts.size()); assertEquals(1, contacts.size());
contacts = ewsSession.galFind(ewsSession.isEqualTo("email1", testContact.get("email1"))); contacts = ewsSession.galFind(ewsSession.isEqualTo("email1", testContact.get("email1")), null, 100);
assertEquals(1, contacts.size()); assertEquals(1, contacts.size());
contacts = ewsSession.galFind(ewsSession.startsWith("email1", testContact.get("email1"))); contacts = ewsSession.galFind(ewsSession.startsWith("email1", testContact.get("email1")), null, 100);
assertEquals(1, contacts.size()); assertEquals(1, contacts.size());
contacts = ewsSession.galFind(ewsSession.and(ewsSession.isEqualTo("cn", testContact.get("cn")), contacts = ewsSession.galFind(ewsSession.and(ewsSession.isEqualTo("cn", testContact.get("cn")),
ewsSession.startsWith("email1", testContact.get("email1")))); ewsSession.startsWith("email1", testContact.get("email1"))), null, 100);
assertEquals(1, contacts.size()); assertEquals(1, contacts.size());
} }
} }

View File

@ -18,13 +18,15 @@
*/ */
package davmail.ldap; package davmail.ldap;
import davmail.AbstractDavMailTestCase;
import davmail.DavGateway; import davmail.DavGateway;
import davmail.Settings; import davmail.Settings;
import davmail.exchange.AbstractExchangeSessionTestCase;
import davmail.exchange.ExchangeSessionFactory; import davmail.exchange.ExchangeSessionFactory;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult; import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.InitialLdapContext;
@ -34,7 +36,7 @@ import java.util.Hashtable;
/** /**
* Test LDAP. * Test LDAP.
*/ */
public class TestLdap extends AbstractDavMailTestCase { public class TestLdap extends AbstractExchangeSessionTestCase {
InitialLdapContext ldapContext; InitialLdapContext ldapContext;
@Override @Override
@ -83,4 +85,17 @@ public class TestLdap extends AbstractDavMailTestCase {
searchControls.setReturningAttributes(new String[]{"custom1", "mozillausehtmlmail", "postalcode", "custom2", "custom3", "custom4", "street", "surname", "telephonenumber", "mozillahomelocalityname", "orgunit", "mozillaworkstreet2", "xmozillanickname", "mozillahomestreet", "description", "cellphone", "homeurl", "mozillahomepostalcode", "departmentnumber", "postofficebox", "st", "objectclass", "sn", "ou", "fax", "mozillahomeurl", "mozillahomecountryname", "streetaddress", "cn", "company", "mozillaworkurl", "mobile", "region", "birthmonth", "birthday", "labeleduri", "carphone", "department", "xmozillausehtmlmail", "givenname", "nsaimid", "workurl", "facsimiletelephonenumber", "mozillanickname", "title", "nscpaimscreenname", "xmozillasecondemail", "mozillacustom3", "countryname", "mozillacustom4", "mozillacustom1", "mozillacustom2", "homephone", "mozillasecondemail", "pager", "zip", "mail", "c", "mozillahomestate", "o", "l", "birthyear", "modifytimestamp", "locality", "commonname", "notes", "pagerphone", "mozillahomestreet2"}); searchControls.setReturningAttributes(new String[]{"custom1", "mozillausehtmlmail", "postalcode", "custom2", "custom3", "custom4", "street", "surname", "telephonenumber", "mozillahomelocalityname", "orgunit", "mozillaworkstreet2", "xmozillanickname", "mozillahomestreet", "description", "cellphone", "homeurl", "mozillahomepostalcode", "departmentnumber", "postofficebox", "st", "objectclass", "sn", "ou", "fax", "mozillahomeurl", "mozillahomecountryname", "streetaddress", "cn", "company", "mozillaworkurl", "mobile", "region", "birthmonth", "birthday", "labeleduri", "carphone", "department", "xmozillausehtmlmail", "givenname", "nsaimid", "workurl", "facsimiletelephonenumber", "mozillanickname", "title", "nscpaimscreenname", "xmozillasecondemail", "mozillacustom3", "countryname", "mozillacustom4", "mozillacustom1", "mozillacustom2", "homephone", "mozillasecondemail", "pager", "zip", "mail", "c", "mozillahomestate", "o", "l", "birthyear", "modifytimestamp", "locality", "commonname", "notes", "pagerphone", "mozillahomestreet2"});
NamingEnumeration<SearchResult> searchResults = ldapContext.search("ou=people", "(objectclass=*)", searchControls); NamingEnumeration<SearchResult> searchResults = ldapContext.search("ou=people", "(objectclass=*)", searchControls);
} }
public void testGalfind() throws NamingException {
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
// searchControls.setReturningAttributes(new String[]{"uid"});
NamingEnumeration<SearchResult> searchResults = ldapContext.search("ou=people", "(uid="+session.getAlias()+ ')', searchControls);
assertTrue(searchResults.hasMore());
SearchResult searchResult = searchResults.next();
Attributes attributes = searchResult.getAttributes();
Attribute attribute = attributes.get("uid");
assertEquals(session.getAlias(), attribute.get());
assertNotNull(attributes.get("givenName"));
}
} }