mirror of
https://github.com/moparisthebest/davmail
synced 2025-01-07 03:38:05 -05:00
LDAP: run searches in separate threads to implement ABANDON, will make searches faster with some clients (Evolution and OSX address book)
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@799 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
3688d11ac7
commit
51c843de93
@ -332,6 +332,11 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
*/
|
*/
|
||||||
int ldapVersion = LDAP_VERSION3;
|
int ldapVersion = LDAP_VERSION3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search threads map
|
||||||
|
*/
|
||||||
|
protected final HashMap<Integer, SearchThread> searchThreadMap = new HashMap<Integer, SearchThread>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the streams and start the thread.
|
* Initialize the streams and start the thread.
|
||||||
*
|
*
|
||||||
@ -502,129 +507,30 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
reqBer.parseBoolean();
|
reqBer.parseBoolean();
|
||||||
LdapFilter ldapFilter = parseFilter(reqBer);
|
LdapFilter ldapFilter = parseFilter(reqBer);
|
||||||
Set<String> returningAttributes = parseReturningAttributes(reqBer);
|
Set<String> returningAttributes = parseReturningAttributes(reqBer);
|
||||||
|
// launch search in a separate thread
|
||||||
int size = 0;
|
SearchThread searchThread = new SearchThread(getName(), currentMessageId, dn, scope, sizeLimit, timelimit, ldapFilter, returningAttributes);
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH", currentMessageId, dn, scope, sizeLimit, timelimit, ldapFilter.toString(), returningAttributes));
|
synchronized (searchThreadMap) {
|
||||||
|
searchThreadMap.put(currentMessageId, searchThread);
|
||||||
if (scope == SCOPE_BASE_OBJECT) {
|
|
||||||
if ("".equals(dn)) {
|
|
||||||
size = 1;
|
|
||||||
sendRootDSE(currentMessageId);
|
|
||||||
} else if (BASE_CONTEXT.equals(dn)) {
|
|
||||||
size = 1;
|
|
||||||
// root
|
|
||||||
sendBaseContext(currentMessageId);
|
|
||||||
} else if (dn.startsWith("uid=") && dn.indexOf(',') > 0) {
|
|
||||||
if (session != null) {
|
|
||||||
// single user request
|
|
||||||
String uid = dn.substring("uid=".length(), dn.indexOf(','));
|
|
||||||
// first search in contact
|
|
||||||
Map<String, Map<String, String>> persons = session.contactFindByUid(uid);
|
|
||||||
|
|
||||||
// then in GAL
|
|
||||||
if (persons.isEmpty()) {
|
|
||||||
persons = session.galFind("AN", uid);
|
|
||||||
|
|
||||||
Map<String, String> person = persons.get(uid.toLowerCase());
|
|
||||||
// filter out non exact results
|
|
||||||
if (persons.size() > 1 || person == null) {
|
|
||||||
persons = new HashMap<String, Map<String, String>>();
|
|
||||||
if (person != null) {
|
|
||||||
persons.put(uid.toLowerCase(), person);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size = persons.size();
|
|
||||||
sendPersons(currentMessageId, dn.substring(dn.indexOf(',')), persons, returningAttributes);
|
|
||||||
} else {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
|
||||||
}
|
|
||||||
} else if (COMPUTER_CONTEXT.equals(dn)) {
|
|
||||||
size = 1;
|
|
||||||
// computer context for iCal
|
|
||||||
sendComputerContext(currentMessageId, returningAttributes);
|
|
||||||
} else if ((BASE_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT.equalsIgnoreCase(dn))) {
|
|
||||||
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("uid"), person);
|
|
||||||
if (persons.size() == sizeLimit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// full search
|
|
||||||
for (char c = 'A'; c < 'Z'; c++) {
|
|
||||||
if (persons.size() < sizeLimit) {
|
|
||||||
for (Map<String, String> person : session.galFind("AN", String.valueOf(c)).values()) {
|
|
||||||
persons.put(person.get("AN"), person);
|
|
||||||
if (persons.size() == sizeLimit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (persons.size() == sizeLimit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// append personal contacts first
|
|
||||||
String filter = ldapFilter.getContactSearchFilter();
|
|
||||||
|
|
||||||
// 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<String, String> person : session.contactFind(filter).values()) {
|
|
||||||
persons.put(person.get("uid"), person);
|
|
||||||
|
|
||||||
if (persons.size() == sizeLimit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map<String, String> person : ldapFilter.findInGAL(session).values()) {
|
|
||||||
if (persons.size() == sizeLimit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
persons.put(person.get("AN"), person);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size = persons.size();
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_FOUND_RESULTS", currentMessageId, size));
|
|
||||||
sendPersons(currentMessageId, ", " + dn, persons, returningAttributes);
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_END", currentMessageId));
|
|
||||||
} else {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
|
||||||
}
|
|
||||||
} else if (dn != null && dn.length() > 0) {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
|
||||||
}
|
}
|
||||||
|
searchThread.start();
|
||||||
|
|
||||||
// iCal: do not send LDAP_SIZE_LIMIT_EXCEEDED on apple-computer search by cn with sizelimit 1
|
|
||||||
if (size > 1 && size == sizeLimit) {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SIZE_LIMIT_EXCEEDED", currentMessageId));
|
|
||||||
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SIZE_LIMIT_EXCEEDED, "");
|
|
||||||
} else {
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SUCCESS", currentMessageId));
|
|
||||||
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SUCCESS, "");
|
|
||||||
}
|
|
||||||
} else if (requestOperation == LDAP_REQ_ABANDON) {
|
} else if (requestOperation == LDAP_REQ_ABANDON) {
|
||||||
int canceledMessageId = 0;
|
int abandonMessageId = 0;
|
||||||
try {
|
try {
|
||||||
canceledMessageId = (Integer) PARSE_INT_WITH_TAG_METHOD.invoke(reqBer, LDAP_REQ_ABANDON);
|
abandonMessageId = (Integer) PARSE_INT_WITH_TAG_METHOD.invoke(reqBer, LDAP_REQ_ABANDON);
|
||||||
|
synchronized (searchThreadMap) {
|
||||||
|
SearchThread searchThread = searchThreadMap.get(abandonMessageId);
|
||||||
|
if (searchThread != null) {
|
||||||
|
searchThread.abandon();
|
||||||
|
searchThreadMap.remove(currentMessageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
DavGatewayTray.error(e);
|
DavGatewayTray.error(e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
DavGatewayTray.error(e);
|
DavGatewayTray.error(e);
|
||||||
}
|
}
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_ABANDON_SEARCH", currentMessageId, canceledMessageId));
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_ABANDON_SEARCH", currentMessageId, abandonMessageId));
|
||||||
} else {
|
} else {
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_UNSUPPORTED_OPERATION", requestOperation));
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_UNSUPPORTED_OPERATION", requestOperation));
|
||||||
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_OTHER, "Unsupported operation");
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_OTHER, "Unsupported operation");
|
||||||
@ -726,121 +632,6 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
return returningAttributes;
|
return returningAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert to LDAP attributes and send entry
|
|
||||||
*
|
|
||||||
* @param currentMessageId current Message Id
|
|
||||||
* @param baseContext request base context (BASE_CONTEXT or OD_BASE_CONTEXT)
|
|
||||||
* @param persons persons Map
|
|
||||||
* @param returningAttributes returning attributes
|
|
||||||
* @throws IOException on error
|
|
||||||
*/
|
|
||||||
protected void sendPersons(int currentMessageId, String baseContext, Map<String, Map<String, String>> persons, Set<String> returningAttributes) throws IOException {
|
|
||||||
boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.isEmpty();
|
|
||||||
boolean iCalSearch = returningAttributes.contains("apple-serviceslocator");
|
|
||||||
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 (Map<String, String> person : persons.values()) {
|
|
||||||
returningAttributes.add("uid");
|
|
||||||
|
|
||||||
Map<String, Object> ldapPerson = new HashMap<String, Object>();
|
|
||||||
|
|
||||||
// convert GAL entries
|
|
||||||
if (person.get("AN") != null) {
|
|
||||||
// add detailed information, only for GAL entries
|
|
||||||
if (needDetails) {
|
|
||||||
session.galLookup(person);
|
|
||||||
}
|
|
||||||
// Process all attributes that are mapped from exchange
|
|
||||||
for (Map.Entry<String, String> entry : ATTRIBUTE_MAP.entrySet()) {
|
|
||||||
String ldapAttribute = entry.getKey();
|
|
||||||
String exchangeAttribute = entry.getValue();
|
|
||||||
String value = person.get(exchangeAttribute);
|
|
||||||
// contactFind return ldap attributes directly
|
|
||||||
if (value == null) {
|
|
||||||
value = person.get(ldapAttribute);
|
|
||||||
}
|
|
||||||
if (value != null
|
|
||||||
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
|
||||||
ldapPerson.put(ldapAttribute, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// iCal fix to suit both iCal 3 and 4: move cn to sn, remove cn
|
|
||||||
if (iCalSearch && ldapPerson.get("cn") != null && returningAttributes.contains("sn")) {
|
|
||||||
ldapPerson.put("sn", ldapPerson.get("cn"));
|
|
||||||
ldapPerson.remove("cn");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// convert Contact entries
|
|
||||||
for (Map.Entry<String, String> entry : person.entrySet()) {
|
|
||||||
String contactAttribute = entry.getKey();
|
|
||||||
// get converted attribute name
|
|
||||||
String ldapAttribute = CONTACT_ATTRIBUTE_MAP.get(contactAttribute);
|
|
||||||
// no conversion, use exchange attribute name
|
|
||||||
if (ldapAttribute == null) {
|
|
||||||
ldapAttribute = contactAttribute;
|
|
||||||
}
|
|
||||||
String value = entry.getValue();
|
|
||||||
if (value != null
|
|
||||||
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
|
||||||
ldapPerson.put(ldapAttribute, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process all attributes which have static mappings
|
|
||||||
for (Map.Entry<String, String> entry : STATIC_ATTRIBUTE_MAP.entrySet()) {
|
|
||||||
String ldapAttribute = entry.getKey();
|
|
||||||
String value = entry.getValue();
|
|
||||||
|
|
||||||
if (value != null
|
|
||||||
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
|
||||||
ldapPerson.put(ldapAttribute, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needObjectClasses) {
|
|
||||||
ldapPerson.put("objectClass", PERSON_OBJECT_CLASSES);
|
|
||||||
}
|
|
||||||
|
|
||||||
// iCal: copy email to apple-generateduid, encode @
|
|
||||||
if (returnAllAttributes || returningAttributes.contains("apple-generateduid")) {
|
|
||||||
String mail = (String) ldapPerson.get("mail");
|
|
||||||
if (mail != null) {
|
|
||||||
ldapPerson.put("apple-generateduid", mail.replaceAll("@", "__AT__"));
|
|
||||||
} else {
|
|
||||||
// failover, should not happen
|
|
||||||
ldapPerson.put("apple-generateduid", ldapPerson.get("uid"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iCal: replace current user alias with login name
|
|
||||||
if (session.getAlias().equals(ldapPerson.get("uid"))) {
|
|
||||||
if (returningAttributes.contains("uidnumber")) {
|
|
||||||
ldapPerson.put("uidnumber", userName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SEND_PERSON", currentMessageId, ldapPerson.get("uid"), baseContext, ldapPerson));
|
|
||||||
sendEntry(currentMessageId, "uid=" + ldapPerson.get("uid") + baseContext, ldapPerson);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Root DSE
|
* Send Root DSE
|
||||||
*
|
*
|
||||||
@ -917,33 +708,36 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void sendEntry(int currentMessageId, String dn, Map<String, Object> attributes) throws IOException {
|
protected void sendEntry(int currentMessageId, String dn, Map<String, Object> attributes) throws IOException {
|
||||||
responseBer.reset();
|
// synchronize on responseBer
|
||||||
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
synchronized (responseBer) {
|
||||||
responseBer.encodeInt(currentMessageId);
|
responseBer.reset();
|
||||||
responseBer.beginSeq(LDAP_REP_SEARCH);
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
||||||
responseBer.encodeString(dn, isLdapV3());
|
responseBer.encodeInt(currentMessageId);
|
||||||
responseBer.beginSeq(LBER_SEQUENCE);
|
responseBer.beginSeq(LDAP_REP_SEARCH);
|
||||||
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
|
responseBer.encodeString(dn, isLdapV3());
|
||||||
responseBer.beginSeq(LBER_SEQUENCE);
|
responseBer.beginSeq(LBER_SEQUENCE);
|
||||||
responseBer.encodeString(entry.getKey(), isLdapV3());
|
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
|
||||||
responseBer.beginSeq(LBER_SET);
|
responseBer.beginSeq(LBER_SEQUENCE);
|
||||||
Object values = entry.getValue();
|
responseBer.encodeString(entry.getKey(), isLdapV3());
|
||||||
if (values instanceof String) {
|
responseBer.beginSeq(LBER_SET);
|
||||||
responseBer.encodeString((String) values, isLdapV3());
|
Object values = entry.getValue();
|
||||||
} else if (values instanceof List) {
|
if (values instanceof String) {
|
||||||
for (Object value : (List) values) {
|
responseBer.encodeString((String) values, isLdapV3());
|
||||||
responseBer.encodeString((String) value, isLdapV3());
|
} else if (values instanceof List) {
|
||||||
|
for (Object value : (List) values) {
|
||||||
|
responseBer.encodeString((String) value, isLdapV3());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new DavMailException("EXCEPTION_UNSUPPORTED_VALUE", values);
|
||||||
}
|
}
|
||||||
} else {
|
responseBer.endSeq();
|
||||||
throw new DavMailException("EXCEPTION_UNSUPPORTED_VALUE", values);
|
responseBer.endSeq();
|
||||||
}
|
}
|
||||||
responseBer.endSeq();
|
responseBer.endSeq();
|
||||||
responseBer.endSeq();
|
responseBer.endSeq();
|
||||||
|
responseBer.endSeq();
|
||||||
|
sendResponse();
|
||||||
}
|
}
|
||||||
responseBer.endSeq();
|
|
||||||
responseBer.endSeq();
|
|
||||||
responseBer.endSeq();
|
|
||||||
sendResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendErr(int currentMessageId, int responseOperation, Exception e) throws IOException {
|
protected void sendErr(int currentMessageId, int responseOperation, Exception e) throws IOException {
|
||||||
@ -1329,4 +1123,282 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class SearchThread extends Thread {
|
||||||
|
private final int currentMessageId;
|
||||||
|
private final String dn;
|
||||||
|
private final int scope;
|
||||||
|
private final int sizeLimit;
|
||||||
|
private final int timelimit;
|
||||||
|
private final LdapFilter ldapFilter;
|
||||||
|
private final Set<String> returningAttributes;
|
||||||
|
private boolean abandon;
|
||||||
|
|
||||||
|
protected SearchThread(String threadName, int currentMessageId, String dn, int scope, int sizeLimit, int timelimit, LdapFilter ldapFilter, Set<String> returningAttributes) {
|
||||||
|
super(threadName + "-Search-" + currentMessageId);
|
||||||
|
this.currentMessageId = currentMessageId;
|
||||||
|
this.dn = dn;
|
||||||
|
this.scope = scope;
|
||||||
|
this.sizeLimit = sizeLimit;
|
||||||
|
this.timelimit = timelimit;
|
||||||
|
this.ldapFilter = ldapFilter;
|
||||||
|
this.returningAttributes = returningAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abandon search.
|
||||||
|
*/
|
||||||
|
protected void abandon() {
|
||||||
|
abandon = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
int size = 0;
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH", currentMessageId, dn, scope, sizeLimit, timelimit, ldapFilter.toString(), returningAttributes));
|
||||||
|
|
||||||
|
if (scope == SCOPE_BASE_OBJECT) {
|
||||||
|
if ("".equals(dn)) {
|
||||||
|
size = 1;
|
||||||
|
sendRootDSE(currentMessageId);
|
||||||
|
} else if (BASE_CONTEXT.equals(dn)) {
|
||||||
|
size = 1;
|
||||||
|
// root
|
||||||
|
sendBaseContext(currentMessageId);
|
||||||
|
} else if (dn.startsWith("uid=") && dn.indexOf(',') > 0) {
|
||||||
|
if (session != null) {
|
||||||
|
// single user request
|
||||||
|
String uid = dn.substring("uid=".length(), dn.indexOf(','));
|
||||||
|
// first search in contact
|
||||||
|
Map<String, Map<String, String>> persons = session.contactFindByUid(uid);
|
||||||
|
|
||||||
|
// then in GAL
|
||||||
|
if (persons.isEmpty()) {
|
||||||
|
persons = session.galFind("AN", uid);
|
||||||
|
|
||||||
|
Map<String, String> person = persons.get(uid.toLowerCase());
|
||||||
|
// filter out non exact results
|
||||||
|
if (persons.size() > 1 || person == null) {
|
||||||
|
persons = new HashMap<String, Map<String, String>>();
|
||||||
|
if (person != null) {
|
||||||
|
persons.put(uid.toLowerCase(), person);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size = persons.size();
|
||||||
|
sendPersons(currentMessageId, dn.substring(dn.indexOf(',')), persons, returningAttributes);
|
||||||
|
} else {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
||||||
|
}
|
||||||
|
} else if (COMPUTER_CONTEXT.equals(dn)) {
|
||||||
|
size = 1;
|
||||||
|
// computer context for iCal
|
||||||
|
sendComputerContext(currentMessageId, returningAttributes);
|
||||||
|
} else if ((BASE_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT.equalsIgnoreCase(dn))) {
|
||||||
|
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("uid"), person);
|
||||||
|
if (persons.size() == sizeLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// full search
|
||||||
|
for (char c = 'A'; c < 'Z'; c++) {
|
||||||
|
if (!abandon && persons.size() < sizeLimit) {
|
||||||
|
for (Map<String, String> person : session.galFind("AN", String.valueOf(c)).values()) {
|
||||||
|
persons.put(person.get("AN"), person);
|
||||||
|
if (persons.size() == sizeLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (persons.size() == sizeLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// append personal contacts first
|
||||||
|
String filter = ldapFilter.getContactSearchFilter();
|
||||||
|
|
||||||
|
// 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<String, String> person : session.contactFind(filter).values()) {
|
||||||
|
persons.put(person.get("uid"), person);
|
||||||
|
|
||||||
|
if (persons.size() == sizeLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!abandon && persons.size() < sizeLimit) {
|
||||||
|
for (Map<String, String> person : ldapFilter.findInGAL(session).values()) {
|
||||||
|
if (persons.size() == sizeLimit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
persons.put(person.get("AN"), person);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size = persons.size();
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_FOUND_RESULTS", currentMessageId, size));
|
||||||
|
sendPersons(currentMessageId, ", " + dn, persons, returningAttributes);
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_END", currentMessageId));
|
||||||
|
} else {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
||||||
|
}
|
||||||
|
} else if (dn != null && dn.length() > 0) {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// iCal: do not send LDAP_SIZE_LIMIT_EXCEEDED on apple-computer search by cn with sizelimit 1
|
||||||
|
if (size > 1 && size == sizeLimit) {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SIZE_LIMIT_EXCEEDED", currentMessageId));
|
||||||
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SIZE_LIMIT_EXCEEDED, "");
|
||||||
|
} else {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SUCCESS", currentMessageId));
|
||||||
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SUCCESS, "");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
DavGatewayTray.log(e);
|
||||||
|
try {
|
||||||
|
sendErr(currentMessageId, LDAP_REP_RESULT, e);
|
||||||
|
} catch (IOException e2) {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
synchronized (searchThreadMap) {
|
||||||
|
searchThreadMap.remove(currentMessageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to LDAP attributes and send entry
|
||||||
|
*
|
||||||
|
* @param currentMessageId current Message Id
|
||||||
|
* @param baseContext request base context (BASE_CONTEXT or OD_BASE_CONTEXT)
|
||||||
|
* @param persons persons Map
|
||||||
|
* @param returningAttributes returning attributes
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
protected void sendPersons(int currentMessageId, String baseContext, Map<String, Map<String, String>> persons, Set<String> returningAttributes) throws IOException {
|
||||||
|
boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.isEmpty();
|
||||||
|
boolean iCalSearch = returningAttributes.contains("apple-serviceslocator");
|
||||||
|
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 (Map<String, String> person : persons.values()) {
|
||||||
|
if (abandon) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
returningAttributes.add("uid");
|
||||||
|
|
||||||
|
Map<String, Object> ldapPerson = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
// convert GAL entries
|
||||||
|
if (person.get("AN") != null) {
|
||||||
|
// add detailed information, only for GAL entries
|
||||||
|
if (needDetails) {
|
||||||
|
session.galLookup(person);
|
||||||
|
}
|
||||||
|
// Process all attributes that are mapped from exchange
|
||||||
|
for (Map.Entry<String, String> entry : ATTRIBUTE_MAP.entrySet()) {
|
||||||
|
String ldapAttribute = entry.getKey();
|
||||||
|
String exchangeAttribute = entry.getValue();
|
||||||
|
String value = person.get(exchangeAttribute);
|
||||||
|
// contactFind return ldap attributes directly
|
||||||
|
if (value == null) {
|
||||||
|
value = person.get(ldapAttribute);
|
||||||
|
}
|
||||||
|
if (value != null
|
||||||
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
||||||
|
ldapPerson.put(ldapAttribute, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// iCal fix to suit both iCal 3 and 4: move cn to sn, remove cn
|
||||||
|
if (iCalSearch && ldapPerson.get("cn") != null && returningAttributes.contains("sn")) {
|
||||||
|
ldapPerson.put("sn", ldapPerson.get("cn"));
|
||||||
|
ldapPerson.remove("cn");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// convert Contact entries
|
||||||
|
for (Map.Entry<String, String> entry : person.entrySet()) {
|
||||||
|
String contactAttribute = entry.getKey();
|
||||||
|
// get converted attribute name
|
||||||
|
String ldapAttribute = CONTACT_ATTRIBUTE_MAP.get(contactAttribute);
|
||||||
|
// no conversion, use exchange attribute name
|
||||||
|
if (ldapAttribute == null) {
|
||||||
|
ldapAttribute = contactAttribute;
|
||||||
|
}
|
||||||
|
String value = entry.getValue();
|
||||||
|
if (value != null
|
||||||
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
||||||
|
ldapPerson.put(ldapAttribute, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all attributes which have static mappings
|
||||||
|
for (Map.Entry<String, String> entry : STATIC_ATTRIBUTE_MAP.entrySet()) {
|
||||||
|
String ldapAttribute = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
|
||||||
|
if (value != null
|
||||||
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
||||||
|
ldapPerson.put(ldapAttribute, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needObjectClasses) {
|
||||||
|
ldapPerson.put("objectClass", PERSON_OBJECT_CLASSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iCal: copy email to apple-generateduid, encode @
|
||||||
|
if (returnAllAttributes || returningAttributes.contains("apple-generateduid")) {
|
||||||
|
String mail = (String) ldapPerson.get("mail");
|
||||||
|
if (mail != null) {
|
||||||
|
ldapPerson.put("apple-generateduid", mail.replaceAll("@", "__AT__"));
|
||||||
|
} else {
|
||||||
|
// failover, should not happen
|
||||||
|
ldapPerson.put("apple-generateduid", ldapPerson.get("uid"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// iCal: replace current user alias with login name
|
||||||
|
if (session.getAlias().equals(ldapPerson.get("uid"))) {
|
||||||
|
if (returningAttributes.contains("uidnumber")) {
|
||||||
|
ldapPerson.put("uidnumber", userName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SEND_PERSON", currentMessageId, ldapPerson.get("uid"), baseContext, ldapPerson));
|
||||||
|
sendEntry(currentMessageId, "uid=" + ldapPerson.get("uid") + baseContext, ldapPerson);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user