From 9a2149ef93734f302cdf0c43bfbeb7a4107599a4 Mon Sep 17 00:00:00 2001 From: mguessan Date: Thu, 17 Jun 2010 22:37:33 +0000 Subject: [PATCH] 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 --- .../davmail/exchange/ExchangeSession.java | 117 ++++++++++-------- .../exchange/dav/DavExchangeSession.java | 7 +- .../davmail/exchange/ews/AttributeOption.java | 5 + .../exchange/ews/ContainmentComparison.java | 38 ++++++ .../davmail/exchange/ews/ContainmentMode.java | 41 ++++++ .../exchange/ews/EwsExchangeSession.java | 26 +++- src/java/davmail/imap/ImapConnection.java | 6 +- src/java/davmail/ldap/LdapConnection.java | 64 ++++------ 8 files changed, 207 insertions(+), 97 deletions(-) create mode 100644 src/java/davmail/exchange/ews/ContainmentComparison.java create mode 100644 src/java/davmail/exchange/ews/ContainmentMode.java diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 031f2474..d0575696 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -22,6 +22,7 @@ import davmail.BundleMessage; import davmail.Settings; import davmail.exception.DavMailAuthenticationException; import davmail.exception.DavMailException; +import davmail.exchange.dav.Field; import davmail.http.DavGatewayHttpClientFacade; import davmail.http.DavGatewayOTPPrompt; import davmail.util.StringUtil; @@ -600,7 +601,8 @@ public abstract class ExchangeSession { public abstract MessageList searchMessages(String folderName, List attributes, Condition condition) throws IOException; 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 { @@ -628,7 +630,7 @@ public abstract class ExchangeSession { this.conditions = Arrays.asList(conditions); } - public void append(Condition condition) { + public void add(Condition condition) { if (condition != null) { conditions.add(condition); } @@ -673,6 +675,8 @@ public abstract class ExchangeSession { 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 isTrue(String attributeName); @@ -2755,7 +2759,7 @@ public abstract class ExchangeSession { * @throws IOException on error */ public Map> contactFindByUid(String uid) throws IOException { - return contactFind(DAV_UID_FILTER + uid + '\''); + return contactFind(equals("uid", uid)); } static final String DAV_UID_FILTER = "\"DAV:uid\"='"; @@ -2767,13 +2771,15 @@ public abstract class ExchangeSession { * @return List of users * @throws IOException on error */ - public Map> contactFind(String searchFilter) throws IOException { + public Map> 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) { @@ -2789,52 +2795,63 @@ public abstract class ExchangeSession { } } } + */ + + List attributes = new ArrayList(); + 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(); - searchRequest.append("Select \"DAV:uid\", " + - "\"http://schemas.microsoft.com/exchange/extensionattribute1\"," + - "\"http://schemas.microsoft.com/exchange/extensionattribute2\"," + - "\"http://schemas.microsoft.com/exchange/extensionattribute3\"," + - "\"http://schemas.microsoft.com/exchange/extensionattribute4\"," + - "\"urn:schemas:contacts:bday\"," + - "\"urn:schemas:contacts:businesshomepage\"," + - "\"urn:schemas:contacts:c\"," + - "\"urn:schemas:contacts:cn\"," + - "\"urn:schemas:contacts:co\"," + - "\"urn:schemas:contacts:department\"," + - "\"urn:schemas:contacts:email1\"," + - "\"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); + searchRequest.append("SELECT "); + if (attributes != null) { + for (String attribute : attributes) { + Field field = Field.get(attribute); + searchRequest.append(',').append(Field.getRequestPropertyString(field.getAlias())); + } + } + searchRequest.append(" FROM SCOPE('SHALLOW TRAVERSAL OF \"").append(contactsUrl).append("\"')") + .append(" WHERE \"DAV:contentclass\" = 'urn:content-classes:person'"); + if (condition != null) { + searchRequest.append(" AND "); + condition.appendTo(searchRequest); } LOGGER.debug("contactFind: " + searchRequest); 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; } diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 52b740c6..b5cfb2a1 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -307,7 +307,7 @@ public class DavExchangeSession extends ExchangeSession { buffer.append('%'); } buffer.append(value); - if (Operator.Like == operator) { + if (Operator.Like == operator || Operator.StartsWith == operator) { buffer.append('%'); } if (!isIntValue) { @@ -404,6 +404,11 @@ public class DavExchangeSession extends ExchangeSession { return new AttributeCondition(attributeName, Operator.Like, value); } + @Override + public Condition startsWith(String attributeName, String value) { + return new AttributeCondition(attributeName, Operator.StartsWith, value); + } + @Override public Condition isNull(String attributeName) { return new MonoCondition(attributeName, Operator.IsNull); diff --git a/src/java/davmail/exchange/ews/AttributeOption.java b/src/java/davmail/exchange/ews/AttributeOption.java index 7aeb1fa4..e0efaeb2 100644 --- a/src/java/davmail/exchange/ews/AttributeOption.java +++ b/src/java/davmail/exchange/ews/AttributeOption.java @@ -30,6 +30,11 @@ public abstract class AttributeOption extends Option { super(name, value); } + public void appendTo(StringBuilder buffer) { + buffer.append(' ').append(name).append("=\"").append(value).append('"'); + } + + /** * @inheritDoc */ diff --git a/src/java/davmail/exchange/ews/ContainmentComparison.java b/src/java/davmail/exchange/ews/ContainmentComparison.java new file mode 100644 index 00000000..e6b641d0 --- /dev/null +++ b/src/java/davmail/exchange/ews/ContainmentComparison.java @@ -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"); + +} diff --git a/src/java/davmail/exchange/ews/ContainmentMode.java b/src/java/davmail/exchange/ews/ContainmentMode.java new file mode 100644 index 00000000..18dd8191 --- /dev/null +++ b/src/java/davmail/exchange/ews/ContainmentMode.java @@ -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"); +} diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index 0c102c94..a77ca077 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -164,10 +164,20 @@ public class EwsExchangeSession extends ExchangeSession { } protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression { + protected ContainmentMode containmentMode; + protected ContainmentComparison containmentComparison; + protected AttributeCondition(String attributeName, Operator operator, String 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) { FieldURI fieldURI = attributeMap.get(attributeName); if (fieldURI == null) { @@ -178,7 +188,14 @@ public class EwsExchangeSession extends ExchangeSession { @Override public void appendTo(StringBuilder buffer) { - buffer.append("'); + buffer.append("'); getFieldURI(attributeName).appendTo(buffer); buffer.append("> findInGAL(ExchangeSession session) throws IOException; @@ -785,7 +785,7 @@ public class LdapConnection extends AbstractConnection { boolean isMatch(Map person); } - static class CompoundFilter implements LdapFilter { + class CompoundFilter implements LdapFilter { final Set criteria = new HashSet(); final int type; @@ -843,35 +843,21 @@ public class LdapConnection extends AbstractConnection { * * @return contact search filter */ - public String getContactSearchFilter() { - StringBuilder buffer = new StringBuilder(); + public ExchangeSession.Condition getContactSearchFilter() { + ExchangeSession.MultiCondition condition; String op; if (type == LDAP_FILTER_OR) { - op = " OR "; + condition = session.or(); } else { - op = " AND "; + condition = session.and(); } - buffer.append('('); for (LdapFilter child : criteria) { - String childFilter = child.getContactSearchFilter(); - - if (childFilter != null) { - if (buffer.length() > 1) { - buffer.append(op); - } - buffer.append(childFilter); - } + condition.add(child.getContactSearchFilter()); } - // empty filter - if (buffer.length() == 1) { - return null; - } - - buffer.append(')'); - return buffer.toString(); + return condition; } /** @@ -956,7 +942,7 @@ public class LdapConnection extends AbstractConnection { } } - static class SimpleFilter implements LdapFilter { + class SimpleFilter implements LdapFilter { static final String STAR = "*"; final String attributeName; final String value; @@ -1026,32 +1012,28 @@ public class LdapConnection extends AbstractConnection { return buffer.toString(); } - public String getContactSearchFilter() { - StringBuilder buffer; + public ExchangeSession.Condition getContactSearchFilter() { String contactAttributeName = getContactAttributeName(); if (canIgnore || (contactAttributeName == null)) { return null; } - buffer = new StringBuilder(); - buffer.append('"').append(contactAttributeName).append('"'); + ExchangeSession.Condition condition; if (operator == LDAP_FILTER_EQUALITY) { - buffer.append("='").append(value).append('\''); + condition = session.equals(contactAttributeName, value); } else if ("*".equals(value)) { - buffer.append(" is not null"); + condition = session.not(session.isNull(contactAttributeName)); } else { - buffer.append(" LIKE '"); + // endsWith not supported by exchange, convert to contains 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 person) { @@ -1230,7 +1212,7 @@ public class LdapConnection extends AbstractConnection { } } else { // append personal contacts first - String filter = ldapFilter.getContactSearchFilter(); + ExchangeSession.Condition filter = ldapFilter.getContactSearchFilter(); // if ldapfilter is not a full search and filter is null, // ignored all attribute filters => return empty results