mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-13 19:22:22 -05:00
LDAP: experimental contact search support
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@682 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
692d9faab8
commit
689727a389
@ -85,6 +85,7 @@ public class ExchangeSession {
|
||||
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("sendmsg", URN_SCHEMAS_HTTPMAIL));
|
||||
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("drafts", URN_SCHEMAS_HTTPMAIL));
|
||||
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("calendar", URN_SCHEMAS_HTTPMAIL));
|
||||
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("contacts", URN_SCHEMAS_HTTPMAIL));
|
||||
}
|
||||
|
||||
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet();
|
||||
@ -126,6 +127,7 @@ public class ExchangeSession {
|
||||
private String sendmsgUrl;
|
||||
private String draftsUrl;
|
||||
private String calendarUrl;
|
||||
private String contactsUrl;
|
||||
|
||||
/**
|
||||
* Base user mailboxes path (used to select folder)
|
||||
@ -511,12 +513,14 @@ public class ExchangeSession {
|
||||
sendmsgUrl = getURIPropertyIfExists(properties, "sendmsg", URN_SCHEMAS_HTTPMAIL);
|
||||
draftsUrl = getURIPropertyIfExists(properties, "drafts", URN_SCHEMAS_HTTPMAIL);
|
||||
calendarUrl = getURIPropertyIfExists(properties, "calendar", URN_SCHEMAS_HTTPMAIL);
|
||||
contactsUrl = getURIPropertyIfExists(properties, "contacts", URN_SCHEMAS_HTTPMAIL);
|
||||
LOGGER.debug("Inbox URL : " + inboxUrl +
|
||||
" Trash URL : " + deleteditemsUrl +
|
||||
" Sent URL : " + sentitemsUrl +
|
||||
" Send URL : " + sendmsgUrl +
|
||||
" Drafts URL : " + draftsUrl +
|
||||
" Calendar URL : " + calendarUrl
|
||||
" Calendar URL : " + calendarUrl +
|
||||
" Contacts URL : " + contactsUrl
|
||||
);
|
||||
}
|
||||
|
||||
@ -1983,7 +1987,7 @@ public class ExchangeSession {
|
||||
writer.write(participants.organizer);
|
||||
writer.write("\r\n");
|
||||
}
|
||||
// if not organizer, set REPLYTIME to force Outlook in attendee mode
|
||||
// if not organizer, set REPLYTIME to force Outlook in attendee mode
|
||||
if (participants.organizer != null && !email.equalsIgnoreCase(participants.organizer)) {
|
||||
if (icsBody.indexOf("METHOD:") < 0) {
|
||||
icsBody = icsBody.replaceAll("BEGIN:VCALENDAR", "BEGIN:VCALENDAR\r\nMETHOD:REQUEST");
|
||||
@ -2394,6 +2398,112 @@ public class ExchangeSession {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search users in contacts folder
|
||||
*
|
||||
* @param searchFilter search filter
|
||||
* @return List of users
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public Map<String, Map<String, String>> contactFind(String searchFilter) throws IOException {
|
||||
StringBuilder searchRequest = new StringBuilder();
|
||||
searchRequest.append("Select \"DAV:uid\", \"urn:schemas:contacts:email1\", \"urn:schemas:contacts:cn\"," +
|
||||
" \"urn:schemas:contacts:givenName\",\"urn:schemas:contacts:sn\", \"urn:schemas:contacts:title\"," +
|
||||
"\"urn:schemas:contacts:o\", \"urn:schemas:contacts:location\", \"urn:schemas:contacts:department\"," +
|
||||
"\"urn:schemas:contacts:telephoneNumber\", \"urn:schemas:contacts:initials\", \"urn:schemas:contacts:street\"," +
|
||||
"\"urn:schemas:contacts:st\", \"urn:schemas:contacts:c\", \"urn:schemas:contacts:mobile\"")
|
||||
.append(" FROM Scope('SHALLOW TRAVERSAL OF \"").append(contactsUrl).append("\"')\n");
|
||||
if (searchFilter != null) {
|
||||
searchRequest.append(" WHERE ").append(searchFilter);
|
||||
}
|
||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
|
||||
httpClient, URIUtil.encodePath(contactsUrl), searchRequest.toString());
|
||||
|
||||
Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();
|
||||
Map<String, String> item;
|
||||
for (MultiStatusResponse response : responses) {
|
||||
DavPropertySet properties = response.getProperties(HttpStatus.SC_OK);
|
||||
// TODO: improve mapping
|
||||
String uid = getPropertyIfExists(properties, "uid", Namespace.getNamespace("DAV:"));
|
||||
String email1 = getPropertyIfExists(properties, "email1", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
// patch email
|
||||
if (email1 != null && email1.startsWith("\"")) {
|
||||
int endIndex = email1.indexOf('\"', 1);
|
||||
if (endIndex > 0) {
|
||||
email1 = email1.substring(1, endIndex);
|
||||
}
|
||||
}
|
||||
String cn = getPropertyIfExists(properties, "cn", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String givenName = getPropertyIfExists(properties, "givenName", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String sn = getPropertyIfExists(properties, "sn", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String title = getPropertyIfExists(properties, "title", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String company = getPropertyIfExists(properties, "o", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String location = getPropertyIfExists(properties, "location", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String department = getPropertyIfExists(properties, "department", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
|
||||
String telephoneNumber = getPropertyIfExists(properties, "telephoneNumber", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String initials = getPropertyIfExists(properties, "initials", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String street = getPropertyIfExists(properties, "street", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String st = getPropertyIfExists(properties, "st", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String postalcode = getPropertyIfExists(properties, "postalcode", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String c = getPropertyIfExists(properties, "c", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
String mobile = getPropertyIfExists(properties, "mobile", Namespace.getNamespace("urn:schemas:contacts:"));
|
||||
item = new HashMap<String, String>();
|
||||
if (uid != null) {
|
||||
item.put("AN", uid);
|
||||
}
|
||||
if (email1 != null) {
|
||||
item.put("EM", email1);
|
||||
}
|
||||
if (cn != null) {
|
||||
item.put("DN", cn);
|
||||
}
|
||||
if (givenName != null) {
|
||||
item.put("first", givenName);
|
||||
}
|
||||
if (sn != null) {
|
||||
item.put("last", sn);
|
||||
}
|
||||
if (title != null) {
|
||||
item.put("TL", title);
|
||||
}
|
||||
if (company != null) {
|
||||
item.put("CP", company);
|
||||
}
|
||||
if (location != null) {
|
||||
item.put("OFFICE", location);
|
||||
}
|
||||
if (department != null) {
|
||||
item.put("department", department);
|
||||
}
|
||||
if (telephoneNumber != null) {
|
||||
item.put("PH", telephoneNumber);
|
||||
}
|
||||
if (initials != null) {
|
||||
item.put("initials", initials);
|
||||
}
|
||||
if (street != null) {
|
||||
item.put("street", street);
|
||||
}
|
||||
if (st != null) {
|
||||
item.put("state", st);
|
||||
}
|
||||
if (postalcode != null) {
|
||||
item.put("zip", postalcode);
|
||||
}
|
||||
if (c != null) {
|
||||
item.put("country", c);
|
||||
}
|
||||
if (mobile != null) {
|
||||
item.put("mobile", mobile);
|
||||
}
|
||||
results.put(uid, item);
|
||||
}
|
||||
|
||||
LOGGER.debug("contactFind " + searchFilter + ": " + results.size() + " result(s)");
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extended address book information for person with gallookup.
|
||||
* Does not work with Exchange 2007
|
||||
@ -2413,8 +2523,10 @@ public class ExchangeSession {
|
||||
// add detailed information
|
||||
if (!results.isEmpty()) {
|
||||
Map<String, String> fullperson = results.get(person.get("AN").toLowerCase());
|
||||
for (Map.Entry<String, String> entry : fullperson.entrySet()) {
|
||||
person.put(entry.getKey(), entry.getValue());
|
||||
if (fullperson != null) {
|
||||
for (Map.Entry<String, String> entry : fullperson.entrySet()) {
|
||||
person.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
||||
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
||||
* Copyright (C) 2009 Mickael Guessant
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@ -201,8 +201,25 @@ public class LdapConnection extends AbstractConnection {
|
||||
CRITERIA_MAP.put("apple-group-realname", "DP");
|
||||
}
|
||||
|
||||
static final HashMap<String, String> CONTACT_MAP = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
CONTACT_MAP.put("uid", "DAV:uid");
|
||||
CONTACT_MAP.put("mail", "urn:schemas:contacts:email1");
|
||||
CONTACT_MAP.put("displayname", "urn:schemas:contacts:cn");
|
||||
CONTACT_MAP.put("cn", "urn:schemas:contacts:cn");
|
||||
CONTACT_MAP.put("givenname", "urn:schemas:contacts:givenName");
|
||||
CONTACT_MAP.put("sn", "urn:schemas:contacts:sn");
|
||||
CONTACT_MAP.put("title", "urn:schemas:contacts:title");
|
||||
CONTACT_MAP.put("company", "urn:schemas:contacts:company");
|
||||
CONTACT_MAP.put("o", "urn:schemas:contacts:company");
|
||||
CONTACT_MAP.put("l", "urn:schemas:contacts:location");
|
||||
CONTACT_MAP.put("department", "urn:schemas:contacts:department");
|
||||
CONTACT_MAP.put("apple-group-realname", "urn:schemas:contacts:department");
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP to Exchange Criteria Map
|
||||
* LDAP filter attributes ignore map
|
||||
*/
|
||||
static final HashSet<String> IGNORE_MAP = new HashSet<String>();
|
||||
|
||||
@ -504,6 +521,13 @@ public class LdapConnection extends AbstractConnection {
|
||||
if (session != null) {
|
||||
Map<String, Map<String, String>> persons = new HashMap<String, Map<String, String>>();
|
||||
if (ldapFilter.isFullSearch()) {
|
||||
// append personal contacts first
|
||||
for (Map<String, String> person : session.contactFind(null).values()) {
|
||||
persons.put(person.get("AN"), person);
|
||||
if (persons.size() == sizeLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// full search
|
||||
for (char c = 'A'; c < 'Z'; c++) {
|
||||
if (persons.size() < sizeLimit) {
|
||||
@ -519,12 +543,20 @@ public class LdapConnection extends AbstractConnection {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Map.Entry<String, SimpleFilter> entry : ldapFilter.getOrFilterEntrySet()) {
|
||||
// append personal contacts first
|
||||
for (Map<String, String> person : session.contactFind(ldapFilter.getContactSearchFilter()).values()) {
|
||||
persons.put(person.get("AN"), person);
|
||||
if (persons.size() == sizeLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (SimpleFilter simpleFilter : ldapFilter.getOrFilterSet()) {
|
||||
if (persons.size() < sizeLimit) {
|
||||
for (Map<String, String> person : session.galFind(entry.getKey(), entry.getValue().value).values()) {
|
||||
if ((entry.getValue().operator == LDAP_FILTER_SUBSTRINGS)
|
||||
|| (entry.getValue().operator == LDAP_FILTER_EQUALITY &&
|
||||
entry.getValue().value.equalsIgnoreCase(person.get(entry.getKey())))) {
|
||||
for (Map<String, String> person : session.galFind(simpleFilter.getGalFindAttributeName(), simpleFilter.value).values()) {
|
||||
if ((simpleFilter.operator == LDAP_FILTER_SUBSTRINGS)
|
||||
// exclude non exact match on exact search
|
||||
|| (simpleFilter.operator == LDAP_FILTER_EQUALITY &&
|
||||
simpleFilter.value.equalsIgnoreCase(person.get(simpleFilter.getGalFindAttributeName())))) {
|
||||
persons.put(person.get("AN"), person);
|
||||
}
|
||||
if (persons.size() == sizeLimit) {
|
||||
@ -584,7 +616,7 @@ public class LdapConnection extends AbstractConnection {
|
||||
LdapFilter ldapFilter = new LdapFilter();
|
||||
if (reqBer.peekByte() == LDAP_FILTER_PRESENT) {
|
||||
String attributeName = reqBer.parseStringWithTag(LDAP_FILTER_PRESENT, isLdapV3(), null).toLowerCase();
|
||||
ldapFilter.addFilter(attributeName, new SimpleFilter());
|
||||
ldapFilter.addFilter(new SimpleFilter(attributeName));
|
||||
if (!"objectclass".equals(attributeName)) {
|
||||
DavGatewayTray.warn(new BundleMessage("LOG_LDAP_UNSUPPORTED_FILTER", ldapFilter.toString()));
|
||||
}
|
||||
@ -654,7 +686,7 @@ public class LdapConnection extends AbstractConnection {
|
||||
}
|
||||
}
|
||||
|
||||
ldapFilter.addFilter(attributeName, new SimpleFilter(sValue, ldapFilterOperator));
|
||||
ldapFilter.addFilter(new SimpleFilter(attributeName, sValue, ldapFilterOperator));
|
||||
}
|
||||
|
||||
protected Set<String> parseReturningAttributes(BerDecoder reqBer) throws IOException {
|
||||
@ -885,28 +917,53 @@ public class LdapConnection extends AbstractConnection {
|
||||
final StringBuilder filterString = new StringBuilder();
|
||||
int ldapFilterType;
|
||||
boolean isFullSearch = true;
|
||||
final Map<String, SimpleFilter> orCriteria = new HashMap<String, SimpleFilter>();
|
||||
final Map<String, SimpleFilter> andCriteria = new HashMap<String, SimpleFilter>();
|
||||
final Set<SimpleFilter> orCriteria = new HashSet<SimpleFilter>();
|
||||
final Set<SimpleFilter> andCriteria = new HashSet<SimpleFilter>();
|
||||
|
||||
public void addFilter(String attributeName, SimpleFilter simpleFilter) {
|
||||
filterString.append('(').append(attributeName).append('=').append(simpleFilter.toString()).append(')');
|
||||
public void addFilter(SimpleFilter simpleFilter) {
|
||||
filterString.append('(').append(simpleFilter.toString()).append(')');
|
||||
String attributeName = simpleFilter.getAttributeName();
|
||||
|
||||
String exchangeAttributeName = CRITERIA_MAP.get(attributeName);
|
||||
if (exchangeAttributeName != null) {
|
||||
// known search attribute
|
||||
if (CRITERIA_MAP.get(attributeName) != null) {
|
||||
isFullSearch = false;
|
||||
if (ldapFilterType == 0 || ldapFilterType == LDAP_FILTER_OR) {
|
||||
orCriteria.put(exchangeAttributeName, simpleFilter);
|
||||
orCriteria.add(simpleFilter);
|
||||
} else if (ldapFilterType == LDAP_FILTER_AND) {
|
||||
andCriteria.put(exchangeAttributeName, simpleFilter);
|
||||
andCriteria.add(simpleFilter);
|
||||
}
|
||||
// full search (objectclass=*) filter
|
||||
} else if ("objectclass".equals(attributeName) && SimpleFilter.STAR.equals(simpleFilter.value)) {
|
||||
isFullSearch = true;
|
||||
// ignore search attribute
|
||||
} else if (IGNORE_MAP.contains(attributeName)) {
|
||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_IGNORE_FILTER_ATTRIBUTE", attributeName, simpleFilter.value));
|
||||
// unknown search attribute: warn
|
||||
} else {
|
||||
if (IGNORE_MAP.contains(attributeName)) {
|
||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_IGNORE_FILTER_ATTRIBUTE", attributeName, simpleFilter.value));
|
||||
} else {
|
||||
DavGatewayTray.warn(new BundleMessage("LOG_LDAP_UNSUPPORTED_FILTER_ATTRIBUTE", attributeName, simpleFilter.value));
|
||||
DavGatewayTray.warn(new BundleMessage("LOG_LDAP_UNSUPPORTED_FILTER_ATTRIBUTE", attributeName, simpleFilter.value));
|
||||
}
|
||||
}
|
||||
|
||||
public String getContactSearchFilter() {
|
||||
if (orCriteria.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (SimpleFilter simpleFilter : orCriteria) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(" OR ");
|
||||
}
|
||||
buffer.append('"').append(simpleFilter.getContactAttributeName()).append('"');
|
||||
if (simpleFilter.operator == LDAP_FILTER_EQUALITY) {
|
||||
buffer.append(" = '").append(simpleFilter.value).append('\'');
|
||||
} else if ("mail".equals(simpleFilter.getAttributeName())) {
|
||||
buffer.append(" LIKE '\"").append(simpleFilter.value).append("%'");
|
||||
} else {
|
||||
buffer.append(" LIKE '").append(simpleFilter.value).append("%'");
|
||||
}
|
||||
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,35 +991,54 @@ public class LdapConnection extends AbstractConnection {
|
||||
return isFullSearch;
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, SimpleFilter>> getOrFilterEntrySet() {
|
||||
return orCriteria.entrySet();
|
||||
public Set<SimpleFilter> getOrFilterSet() {
|
||||
return orCriteria;
|
||||
}
|
||||
}
|
||||
|
||||
static class SimpleFilter {
|
||||
static final String STAR = "*";
|
||||
final String attributeName;
|
||||
final String value;
|
||||
final int operator;
|
||||
|
||||
SimpleFilter() {
|
||||
SimpleFilter(String attributeName) {
|
||||
this.attributeName = attributeName;
|
||||
this.value = SimpleFilter.STAR;
|
||||
this.operator = LDAP_FILTER_SUBSTRINGS;
|
||||
}
|
||||
|
||||
SimpleFilter(String value, int ldapFilterOperator) {
|
||||
SimpleFilter(String attributeName, String value, int ldapFilterOperator) {
|
||||
this.attributeName = attributeName;
|
||||
this.value = value;
|
||||
this.operator = ldapFilterOperator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(attributeName);
|
||||
buffer.append('=');
|
||||
if (SimpleFilter.STAR.equals(value)) {
|
||||
return SimpleFilter.STAR;
|
||||
buffer.append(SimpleFilter.STAR);
|
||||
} else if (operator == LDAP_FILTER_SUBSTRINGS) {
|
||||
return SimpleFilter.STAR + value + SimpleFilter.STAR;
|
||||
buffer.append(SimpleFilter.STAR).append(value).append(SimpleFilter.STAR);
|
||||
} else {
|
||||
return value;
|
||||
buffer.append(value);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
public String getGalFindAttributeName() {
|
||||
return CRITERIA_MAP.get(attributeName);
|
||||
}
|
||||
|
||||
public String getContactAttributeName() {
|
||||
return CONTACT_MAP.get(attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user