1
0
mirror of https://github.com/moparisthebest/davmail synced 2025-01-05 10:47:59 -05:00

EWS: refactor contactFind, use new Condition API

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1095 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-06-17 22:37:33 +00:00
parent df99136e61
commit 9a2149ef93
8 changed files with 207 additions and 97 deletions

View File

@ -22,6 +22,7 @@ import davmail.BundleMessage;
import davmail.Settings; import davmail.Settings;
import davmail.exception.DavMailAuthenticationException; import davmail.exception.DavMailAuthenticationException;
import davmail.exception.DavMailException; import davmail.exception.DavMailException;
import davmail.exchange.dav.Field;
import davmail.http.DavGatewayHttpClientFacade; import davmail.http.DavGatewayHttpClientFacade;
import davmail.http.DavGatewayOTPPrompt; import davmail.http.DavGatewayOTPPrompt;
import davmail.util.StringUtil; import davmail.util.StringUtil;
@ -600,7 +601,8 @@ public abstract class ExchangeSession {
public abstract MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException; public abstract MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException;
protected enum Operator { protected enum Operator {
Or, And, Not, IsEqualTo, Like, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse Or, And, Not, IsEqualTo, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse,
Like, StartsWith, Contains
} }
public abstract static class Condition { public abstract static class Condition {
@ -628,7 +630,7 @@ public abstract class ExchangeSession {
this.conditions = Arrays.asList(conditions); this.conditions = Arrays.asList(conditions);
} }
public void append(Condition condition) { public void add(Condition condition) {
if (condition != null) { if (condition != null) {
conditions.add(condition); conditions.add(condition);
} }
@ -673,6 +675,8 @@ public abstract class ExchangeSession {
public abstract Condition like(String attributeName, String value); public abstract Condition like(String attributeName, String value);
public abstract Condition startsWith(String attributeName, String value);
public abstract Condition isNull(String attributeName); public abstract Condition isNull(String attributeName);
public abstract Condition isTrue(String attributeName); public abstract Condition isTrue(String attributeName);
@ -2755,7 +2759,7 @@ public abstract class ExchangeSession {
* @throws IOException on error * @throws IOException on error
*/ */
public Map<String, Map<String, String>> contactFindByUid(String uid) throws IOException { public Map<String, Map<String, String>> contactFindByUid(String uid) throws IOException {
return contactFind(DAV_UID_FILTER + uid + '\''); return contactFind(equals("uid", uid));
} }
static final String DAV_UID_FILTER = "\"DAV:uid\"='"; static final String DAV_UID_FILTER = "\"DAV:uid\"='";
@ -2767,13 +2771,15 @@ public abstract class ExchangeSession {
* @return List of users * @return List of users
* @throws IOException on error * @throws IOException on error
*/ */
public Map<String, Map<String, String>> contactFind(String searchFilter) throws IOException { public Map<String, Map<String, String>> contactFind(Condition condition) throws IOException {
// uid value in search filter (hex value) // uid value in search filter (hex value)
String filterUid = null; String filterUid = null;
// base64 encoded uid value // base64 encoded uid value
String actualFilterUid = null; String actualFilterUid = null;
// TODO: move to LDAP code
// replace hex encoded uid with base64 uid // replace hex encoded uid with base64 uid
/*
if (searchFilter != null) { if (searchFilter != null) {
int uidStart = searchFilter.indexOf(DAV_UID_FILTER); int uidStart = searchFilter.indexOf(DAV_UID_FILTER);
if (uidStart >= 0) { if (uidStart >= 0) {
@ -2789,52 +2795,63 @@ public abstract class ExchangeSession {
} }
} }
} }
*/
List<String> attributes = new ArrayList<String>();
attributes.add("extensionattribute1");
attributes.add("extensionattribute2");
attributes.add("extensionattribute3");
attributes.add("extensionattribute4");
attributes.add("bday");
attributes.add("businesshomepage");
attributes.add("c");
attributes.add("cn");
attributes.add("co");
attributes.add("department");
attributes.add("email1");
attributes.add("email2");
attributes.add("facsimiletelephonenumber");
attributes.add("givenName");
attributes.add("homeCity");
attributes.add("homeCountry");
attributes.add("homePhone");
attributes.add("homePostalCode");
attributes.add("homeState");
attributes.add("homeStreet");
attributes.add("l");
attributes.add("manager");
attributes.add("mobile");
attributes.add("namesuffix");
attributes.add("nickname");
attributes.add("o");
attributes.add("pager");
attributes.add("personaltitle");
attributes.add("postalcode");
attributes.add("postofficebox");
attributes.add("profession");
attributes.add("roomnumber");
attributes.add("secretarycn");
attributes.add("sn");
attributes.add("spousecn");
attributes.add("st");
attributes.add("street");
attributes.add("telephoneNumber");
attributes.add("title");
attributes.add("textdescription");
StringBuilder searchRequest = new StringBuilder(); StringBuilder searchRequest = new StringBuilder();
searchRequest.append("Select \"DAV:uid\", " + searchRequest.append("SELECT ");
"\"http://schemas.microsoft.com/exchange/extensionattribute1\"," + if (attributes != null) {
"\"http://schemas.microsoft.com/exchange/extensionattribute2\"," + for (String attribute : attributes) {
"\"http://schemas.microsoft.com/exchange/extensionattribute3\"," + Field field = Field.get(attribute);
"\"http://schemas.microsoft.com/exchange/extensionattribute4\"," + searchRequest.append(',').append(Field.getRequestPropertyString(field.getAlias()));
"\"urn:schemas:contacts:bday\"," + }
"\"urn:schemas:contacts:businesshomepage\"," + }
"\"urn:schemas:contacts:c\"," + searchRequest.append(" FROM SCOPE('SHALLOW TRAVERSAL OF \"").append(contactsUrl).append("\"')")
"\"urn:schemas:contacts:cn\"," + .append(" WHERE \"DAV:contentclass\" = 'urn:content-classes:person'");
"\"urn:schemas:contacts:co\"," + if (condition != null) {
"\"urn:schemas:contacts:department\"," + searchRequest.append(" AND ");
"\"urn:schemas:contacts:email1\"," + condition.appendTo(searchRequest);
"\"urn:schemas:contacts:email2\"," +
"\"urn:schemas:contacts:facsimiletelephonenumber\"," +
"\"urn:schemas:contacts:givenName\"," +
"\"urn:schemas:contacts:homeCity\"," +
"\"urn:schemas:contacts:homeCountry\"," +
"\"urn:schemas:contacts:homePhone\"," +
"\"urn:schemas:contacts:homePostalCode\"," +
"\"urn:schemas:contacts:homeState\"," +
"\"urn:schemas:contacts:homeStreet\"," +
"\"urn:schemas:contacts:l\"," +
"\"urn:schemas:contacts:manager\"," +
"\"urn:schemas:contacts:mobile\"," +
"\"urn:schemas:contacts:namesuffix\"," +
"\"urn:schemas:contacts:nickname\"," +
"\"urn:schemas:contacts:o\"," +
"\"urn:schemas:contacts:pager\"," +
"\"urn:schemas:contacts:personaltitle\"," +
"\"urn:schemas:contacts:postalcode\"," +
"\"urn:schemas:contacts:postofficebox\"," +
"\"urn:schemas:contacts:profession\"," +
"\"urn:schemas:contacts:roomnumber\"," +
"\"urn:schemas:contacts:secretarycn\"," +
"\"urn:schemas:contacts:sn\"," +
"\"urn:schemas:contacts:spousecn\"," +
"\"urn:schemas:contacts:st\"," +
"\"urn:schemas:contacts:street\"," +
"\"urn:schemas:contacts:telephoneNumber\"," +
"\"urn:schemas:contacts:title\"," +
"\"urn:schemas:httpmail:textdescription\"")
.append(" FROM Scope('SHALLOW TRAVERSAL OF \"").append(contactsUrl).append("\"')\n")
.append(" WHERE \"DAV:contentclass\" = 'urn:content-classes:person' \n");
if (searchFilter != null && searchFilter.length() > 0) {
searchRequest.append(" AND ").append(searchFilter);
} }
LOGGER.debug("contactFind: " + searchRequest); LOGGER.debug("contactFind: " + searchRequest);
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod( MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
@ -2889,7 +2906,7 @@ public abstract class ExchangeSession {
} }
} }
LOGGER.debug("contactFind " + ((searchFilter == null) ? "" : searchFilter) + ": " + results.size() + " result(s)"); LOGGER.debug("contactFind " + searchRequest + ": " + results.size() + " result(s)");
return results; return results;
} }

View File

@ -307,7 +307,7 @@ public class DavExchangeSession extends ExchangeSession {
buffer.append('%'); buffer.append('%');
} }
buffer.append(value); buffer.append(value);
if (Operator.Like == operator) { if (Operator.Like == operator || Operator.StartsWith == operator) {
buffer.append('%'); buffer.append('%');
} }
if (!isIntValue) { if (!isIntValue) {
@ -404,6 +404,11 @@ public class DavExchangeSession extends ExchangeSession {
return new AttributeCondition(attributeName, Operator.Like, value); return new AttributeCondition(attributeName, Operator.Like, value);
} }
@Override
public Condition startsWith(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.StartsWith, value);
}
@Override @Override
public Condition isNull(String attributeName) { public Condition isNull(String attributeName) {
return new MonoCondition(attributeName, Operator.IsNull); return new MonoCondition(attributeName, Operator.IsNull);

View File

@ -30,6 +30,11 @@ public abstract class AttributeOption extends Option {
super(name, value); super(name, value);
} }
public void appendTo(StringBuilder buffer) {
buffer.append(' ').append(name).append("=\"").append(value).append('"');
}
/** /**
* @inheritDoc * @inheritDoc
*/ */

View File

@ -0,0 +1,38 @@
/*
* 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.ews;
/**
* Contains comparison mode.
*/
public class ContainmentComparison extends AttributeOption {
private ContainmentComparison(String value) {
super("ContainmentComparison", value);
}
public static final ContainmentComparison Exact = new ContainmentComparison("Exact");
public static final ContainmentComparison IgnoreCase = new ContainmentComparison("IgnoreCase");
public static final ContainmentComparison IgnoreNonSpacingCharacters = new ContainmentComparison("IgnoreNonSpacingCharacters");
public static final ContainmentComparison Loose = new ContainmentComparison("Loose");
public static final ContainmentComparison IgnoreCaseAndNonSpacingCharacters = new ContainmentComparison("IgnoreCaseAndNonSpacingCharacters");
public static final ContainmentComparison LooseAndIgnoreCase = new ContainmentComparison("LooseAndIgnoreCase");
public static final ContainmentComparison LooseAndIgnoreNonSpace = new ContainmentComparison("LooseAndIgnoreNonSpace");
public static final ContainmentComparison LooseAndIgnoreCaseAndIgnoreNonSpace = new ContainmentComparison("LooseAndIgnoreCaseAndIgnoreNonSpace");
}

View File

@ -0,0 +1,41 @@
/*
* 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.ews;
/**
* Contains search mode.
*/
public class ContainmentMode extends AttributeOption {
private ContainmentMode(String value) {
super("ContainmentMode", value);
}
/**
* Full String.
*/
public static final ContainmentMode FullString = new ContainmentMode("FullString");
/**
* Starts with.
*/
public static final ContainmentMode Prefixed = new ContainmentMode("Prefixed");
public static final ContainmentMode Substring = new ContainmentMode("Substring");
public static final ContainmentMode PrefixOnWords = new ContainmentMode("PrefixOnWords");
public static final ContainmentMode ExactPhrase = new ContainmentMode("ExactPhrase");
}

View File

@ -164,10 +164,20 @@ public class EwsExchangeSession extends ExchangeSession {
} }
protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression { protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression {
protected ContainmentMode containmentMode;
protected ContainmentComparison containmentComparison;
protected AttributeCondition(String attributeName, Operator operator, String value) { protected AttributeCondition(String attributeName, Operator operator, String value) {
super(attributeName, operator, value); super(attributeName, operator, value);
} }
protected AttributeCondition(String attributeName, Operator operator, String value,
ContainmentMode containmentMode, ContainmentComparison containmentComparison) {
super(attributeName, operator, value);
this.containmentMode = containmentMode;
this.containmentComparison = containmentComparison;
}
protected FieldURI getFieldURI(String attributeName) { protected FieldURI getFieldURI(String attributeName) {
FieldURI fieldURI = attributeMap.get(attributeName); FieldURI fieldURI = attributeMap.get(attributeName);
if (fieldURI == null) { if (fieldURI == null) {
@ -178,7 +188,14 @@ public class EwsExchangeSession extends ExchangeSession {
@Override @Override
public void appendTo(StringBuilder buffer) { public void appendTo(StringBuilder buffer) {
buffer.append("<t:").append(operator.toString()).append('>'); buffer.append("<t:").append(operator.toString());
if (containmentMode != null) {
containmentMode.appendTo(buffer);
}
if (containmentComparison != null) {
containmentComparison.appendTo(buffer);
}
buffer.append('>');
getFieldURI(attributeName).appendTo(buffer); getFieldURI(attributeName).appendTo(buffer);
buffer.append("<t:FieldURIOrConstant><t:Constant Value=\""); buffer.append("<t:FieldURIOrConstant><t:Constant Value=\"");
@ -264,7 +281,12 @@ public class EwsExchangeSession extends ExchangeSession {
@Override @Override
public Condition like(String attributeName, String value) { public Condition like(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.Like, value); return new AttributeCondition(attributeName, Operator.Contains, value, ContainmentMode.Substring, ContainmentComparison.IgnoreCase);
}
@Override
public Condition startsWith(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.Contains, value, ContainmentMode.Prefixed, ContainmentComparison.IgnoreCase);
} }
@Override @Override

View File

@ -739,7 +739,7 @@ public class ImapConnection extends AbstractConnection {
if (condition == null) { if (condition == null) {
condition = session.and(); condition = session.and();
} }
condition.append(buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1)))); condition.add(buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1))));
} else if ("OR".equals(token)) { } else if ("OR".equals(token)) {
condition = session.or(); condition = session.or();
} else if (token.startsWith("OR ")) { } else if (token.startsWith("OR ")) {
@ -748,7 +748,7 @@ public class ImapConnection extends AbstractConnection {
if (condition == null) { if (condition == null) {
condition = session.and(); condition = session.and();
} }
condition.append(appendSearchParam(tokens, token, conditions)); condition.add(appendSearchParam(tokens, token, conditions));
} }
} }
return condition; return condition;
@ -996,7 +996,7 @@ public class ImapConnection extends AbstractConnection {
innerTokens.nextToken(); innerTokens.nextToken();
while (innerTokens.hasMoreTokens()) { while (innerTokens.hasMoreTokens()) {
String innerToken = innerTokens.nextToken(); String innerToken = innerTokens.nextToken();
orCondition.append(appendSearchParam(innerTokens, innerToken, conditions)); orCondition.add(appendSearchParam(innerTokens, innerToken, conditions));
} }
return orCondition; return orCondition;
} }

View File

@ -573,10 +573,10 @@ public class LdapConnection extends AbstractConnection {
String attributeName = reqBer.parseStringWithTag(LDAP_FILTER_PRESENT, isLdapV3(), null).toLowerCase(); String attributeName = reqBer.parseStringWithTag(LDAP_FILTER_PRESENT, isLdapV3(), null).toLowerCase();
nestedFilter.add(new SimpleFilter(attributeName)); nestedFilter.add(new SimpleFilter(attributeName));
} else { } else {
int[] seqSize = new int[1]; int[] seqSize = new int[1];
int ldapFilterOperator = reqBer.parseSeq(seqSize); int ldapFilterOperator = reqBer.parseSeq(seqSize);
int subEnd = reqBer.getParsePosition() + seqSize[0]; int subEnd = reqBer.getParsePosition() + seqSize[0];
nestedFilter.add(parseNestedFilter(reqBer, ldapFilterOperator, subEnd)); nestedFilter.add(parseNestedFilter(reqBer, ldapFilterOperator, subEnd));
} }
} }
} else { } else {
@ -774,7 +774,7 @@ public class LdapConnection extends AbstractConnection {
} }
static interface LdapFilter { static interface LdapFilter {
String getContactSearchFilter(); ExchangeSession.Condition getContactSearchFilter();
Map<String, Map<String, String>> findInGAL(ExchangeSession session) throws IOException; Map<String, Map<String, String>> findInGAL(ExchangeSession session) throws IOException;
@ -785,7 +785,7 @@ public class LdapConnection extends AbstractConnection {
boolean isMatch(Map<String, String> person); boolean isMatch(Map<String, String> person);
} }
static class CompoundFilter implements LdapFilter { class CompoundFilter implements LdapFilter {
final Set<LdapFilter> criteria = new HashSet<LdapFilter>(); final Set<LdapFilter> criteria = new HashSet<LdapFilter>();
final int type; final int type;
@ -843,35 +843,21 @@ public class LdapConnection extends AbstractConnection {
* *
* @return contact search filter * @return contact search filter
*/ */
public String getContactSearchFilter() { public ExchangeSession.Condition getContactSearchFilter() {
StringBuilder buffer = new StringBuilder(); ExchangeSession.MultiCondition condition;
String op; String op;
if (type == LDAP_FILTER_OR) { if (type == LDAP_FILTER_OR) {
op = " OR "; condition = session.or();
} else { } else {
op = " AND "; condition = session.and();
} }
buffer.append('(');
for (LdapFilter child : criteria) { for (LdapFilter child : criteria) {
String childFilter = child.getContactSearchFilter(); condition.add(child.getContactSearchFilter());
if (childFilter != null) {
if (buffer.length() > 1) {
buffer.append(op);
}
buffer.append(childFilter);
}
} }
// empty filter return condition;
if (buffer.length() == 1) {
return null;
}
buffer.append(')');
return buffer.toString();
} }
/** /**
@ -956,7 +942,7 @@ public class LdapConnection extends AbstractConnection {
} }
} }
static class SimpleFilter implements LdapFilter { class SimpleFilter implements LdapFilter {
static final String STAR = "*"; static final String STAR = "*";
final String attributeName; final String attributeName;
final String value; final String value;
@ -1026,32 +1012,28 @@ public class LdapConnection extends AbstractConnection {
return buffer.toString(); return buffer.toString();
} }
public String getContactSearchFilter() { public ExchangeSession.Condition getContactSearchFilter() {
StringBuilder buffer;
String contactAttributeName = getContactAttributeName(); String contactAttributeName = getContactAttributeName();
if (canIgnore || (contactAttributeName == null)) { if (canIgnore || (contactAttributeName == null)) {
return null; return null;
} }
buffer = new StringBuilder(); ExchangeSession.Condition condition;
buffer.append('"').append(contactAttributeName).append('"');
if (operator == LDAP_FILTER_EQUALITY) { if (operator == LDAP_FILTER_EQUALITY) {
buffer.append("='").append(value).append('\''); condition = session.equals(contactAttributeName, value);
} else if ("*".equals(value)) { } else if ("*".equals(value)) {
buffer.append(" is not null"); condition = session.not(session.isNull(contactAttributeName));
} else { } else {
buffer.append(" LIKE '"); // 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) {
buffer.append('%'); condition = session.like(contactAttributeName, value);
} else {
condition = session.startsWith(contactAttributeName, value);
} }
buffer.append(value.replaceAll("'", "''"));
// endsWith not supported by exchange, always append %
buffer.append('%');
buffer.append('\'');
} }
return buffer.toString(); return condition;
} }
public boolean isMatch(Map<String, String> person) { public boolean isMatch(Map<String, String> person) {
@ -1230,7 +1212,7 @@ public class LdapConnection extends AbstractConnection {
} }
} else { } else {
// append personal contacts first // append personal contacts first
String filter = ldapFilter.getContactSearchFilter(); ExchangeSession.Condition filter = ldapFilter.getContactSearchFilter();
// 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