1
0
mirror of https://github.com/moparisthebest/davmail synced 2024-12-13 11:12:22 -05:00

EWS: refactor IMAP search, use Conditions classes instead of string search filder

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1083 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-06-08 22:21:53 +00:00
parent aab4709956
commit 51936619e3
6 changed files with 644 additions and 237 deletions

View File

@ -109,6 +109,7 @@ public abstract class ExchangeSession {
protected static final String UNSENT = "Unsent Messages"; protected static final String UNSENT = "Unsent Messages";
protected static final Namespace EMPTY = Namespace.getNamespace("");
protected static final Namespace DAV = Namespace.getNamespace("DAV:"); protected static final Namespace DAV = Namespace.getNamespace("DAV:");
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:"); protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/"); protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/");
@ -124,18 +125,6 @@ public abstract class ExchangeSession {
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("contentclass", DAV)); EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("contentclass", DAV));
} }
protected static final DavPropertyNameSet WELL_KNOWN_FOLDERS = new DavPropertyNameSet();
static {
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("inbox", URN_SCHEMAS_HTTPMAIL));
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("deleteditems", URN_SCHEMAS_HTTPMAIL));
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("sentitems", URN_SCHEMAS_HTTPMAIL));
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));
WELL_KNOWN_FOLDERS.add(DavPropertyName.create("outbox", URN_SCHEMAS_HTTPMAIL));
}
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet(); protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet();
@ -605,15 +594,6 @@ public abstract class ExchangeSession {
} }
} }
protected String getURIPropertyIfExists(DavPropertySet properties, String name, Namespace namespace) throws URIException {
DavProperty property = properties.get(name, namespace);
if (property == null) {
return null;
} else {
return URIUtil.decode((String) property.getValue());
}
}
/** /**
* Create message in specified folder. * Create message in specified folder.
* Will overwrite an existing message with same subject in the same folder * Will overwrite an existing message with same subject in the same folder
@ -672,40 +652,6 @@ public abstract class ExchangeSession {
} }
} }
protected Message buildMessage(MultiStatusResponse responseEntity) throws URIException {
Message message = new Message();
message.messageUrl = URIUtil.decode(responseEntity.getHref());
DavPropertySet properties = responseEntity.getProperties(HttpStatus.SC_OK);
message.permanentUrl = getPropertyIfExists(properties, "permanenturl", SCHEMAS_EXCHANGE);
message.size = getIntPropertyIfExists(properties, "x0e080003", SCHEMAS_MAPI_PROPTAG);
message.uid = getPropertyIfExists(properties, "uid", DAV);
message.imapUid = getLongPropertyIfExists(properties, "x0e230003", SCHEMAS_MAPI_PROPTAG);
message.read = "1".equals(getPropertyIfExists(properties, "read", URN_SCHEMAS_HTTPMAIL));
message.junk = "1".equals(getPropertyIfExists(properties, "x10830003", SCHEMAS_MAPI_PROPTAG));
message.flagged = "2".equals(getPropertyIfExists(properties, "x10900003", SCHEMAS_MAPI_PROPTAG));
message.draft = "9".equals(getPropertyIfExists(properties, "x0E070003", SCHEMAS_MAPI_PROPTAG));
String x10810003 = getPropertyIfExists(properties, "x10810003", SCHEMAS_MAPI_PROPTAG);
message.answered = "102".equals(x10810003) || "103".equals(x10810003);
message.forwarded = "104".equals(x10810003);
message.date = getPropertyIfExists(properties, "date", Namespace.getNamespace("urn:schemas:mailheader:"));
message.deleted = "1".equals(getPropertyIfExists(properties, "deleted", Namespace.getNamespace("")));
if (LOGGER.isDebugEnabled()) {
StringBuilder buffer = new StringBuilder();
buffer.append("Message");
if (message.imapUid != 0) {
buffer.append(" IMAP uid: ").append(message.imapUid);
}
if (message.uid != null) {
buffer.append(" uid: ").append(message.uid);
}
buffer.append(" href: ").append(responseEntity.getHref()).append(" permanenturl:").append(message.permanentUrl);
LOGGER.debug(buffer.toString());
}
return message;
}
protected List<DavProperty> buildProperties(Map<String, String> properties) { protected List<DavProperty> buildProperties(Map<String, String> properties) {
ArrayList<DavProperty> list = new ArrayList<DavProperty>(); ArrayList<DavProperty> list = new ArrayList<DavProperty>();
for (Map.Entry<String, String> entry : properties.entrySet()) { for (Map.Entry<String, String> entry : properties.entrySet()) {
@ -763,6 +709,13 @@ public abstract class ExchangeSession {
} }
} }
protected static final List<String> POP_MESSAGE_ATTRIBUTES = new ArrayList<String>();
static {
POP_MESSAGE_ATTRIBUTES.add("uid");
POP_MESSAGE_ATTRIBUTES.add("messageSize");
}
/** /**
* Return folder message list with id and size only (for POP3 listener). * Return folder message list with id and size only (for POP3 listener).
* *
@ -771,24 +724,46 @@ public abstract class ExchangeSession {
* @throws IOException on error * @throws IOException on error
*/ */
public MessageList getAllMessageUidAndSize(String folderName) throws IOException { public MessageList getAllMessageUidAndSize(String folderName) throws IOException {
return searchMessages(folderName, "\"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"", ""); return searchMessages(folderName, POP_MESSAGE_ATTRIBUTES, null);
}
protected static final List<String> IMAP_MESSAGE_ATTRIBUTES = new ArrayList<String>();
static {
IMAP_MESSAGE_ATTRIBUTES.add("uid");
IMAP_MESSAGE_ATTRIBUTES.add("messageSize");
IMAP_MESSAGE_ATTRIBUTES.add("imapUid");
IMAP_MESSAGE_ATTRIBUTES.add("junk");
IMAP_MESSAGE_ATTRIBUTES.add("flagStatus");
IMAP_MESSAGE_ATTRIBUTES.add("messageFlags");
IMAP_MESSAGE_ATTRIBUTES.add("lastVerbExecuted");
IMAP_MESSAGE_ATTRIBUTES.add("read");
IMAP_MESSAGE_ATTRIBUTES.add("deleted");
IMAP_MESSAGE_ATTRIBUTES.add("date");
}
/**
* Get all folder messages.
*
* @param folderName Exchange folder name
* @param condition search filter
* @return message list
* @throws IOException on error
*/
public MessageList searchMessages(String folderName) throws IOException {
return searchMessages(folderName, IMAP_MESSAGE_ATTRIBUTES, null);
} }
/** /**
* Search folder for messages matching conditions, with attributes needed by IMAP listener. * Search folder for messages matching conditions, with attributes needed by IMAP listener.
* *
* @param folderName Exchange folder name * @param folderName Exchange folder name
* @param conditions conditions string in Exchange SQL syntax * @param condition search filter
* @return message list * @return message list
* @throws IOException on error * @throws IOException on error
*/ */
public MessageList searchMessages(String folderName, String conditions) throws IOException { public MessageList searchMessages(String folderName, Condition condition) throws IOException {
return searchMessages(folderName, "\"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"" + return searchMessages(folderName, IMAP_MESSAGE_ATTRIBUTES, condition);
" ,\"http://schemas.microsoft.com/mapi/proptag/x0e230003\"" +
" ,\"http://schemas.microsoft.com/mapi/proptag/x10830003\", \"http://schemas.microsoft.com/mapi/proptag/x10900003\"" +
" ,\"http://schemas.microsoft.com/mapi/proptag/x0E070003\", \"http://schemas.microsoft.com/mapi/proptag/x10810003\"" +
" , \"urn:schemas:httpmail:read\" " +
" ,\"http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/0x8570\" as deleted, \"urn:schemas:mailheader:date\"", conditions);
} }
/** /**
@ -800,41 +775,17 @@ public abstract class ExchangeSession {
* @return message list * @return message list
* @throws IOException on error * @throws IOException on error
*/ */
public MessageList searchMessages(String folderName, String attributes, String conditions) throws IOException { public abstract MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException;
String folderUrl = getFolderPath(folderName);
MessageList messages = new MessageList();
StringBuilder searchRequest = new StringBuilder();
searchRequest.append("Select \"http://schemas.microsoft.com/exchange/permanenturl\"");
if (attributes != null && attributes.length() > 0) {
searchRequest.append(',').append(attributes);
}
searchRequest.append(" FROM Scope('SHALLOW TRAVERSAL OF \"").append(folderUrl).append("\"')\n")
.append(" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = False\n");
if (conditions != null) {
searchRequest.append(conditions);
}
searchRequest.append(" ORDER BY \"urn:schemas:httpmail:date\" ASC");
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
httpClient, URIUtil.encodePath(folderUrl), searchRequest.toString());
for (MultiStatusResponse response : responses) {
Message message = buildMessage(response);
message.messageList = messages;
messages.add(message);
}
Collections.sort(messages);
return messages;
}
protected enum Operator { protected enum Operator {
Or, And, Not, IsEqualTo Or, And, Not, IsEqualTo, Like, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse
} }
protected abstract static class Condition { public abstract static class Condition {
public abstract void appendTo(StringBuilder buffer); public abstract void appendTo(StringBuilder buffer);
} }
protected abstract static class AttributeCondition extends Condition { public abstract static class AttributeCondition extends Condition {
protected String attributeName; protected String attributeName;
protected Operator operator; protected Operator operator;
protected String value; protected String value;
@ -846,17 +797,23 @@ public abstract class ExchangeSession {
} }
} }
protected abstract static class MultiCondition extends Condition { public abstract static class MultiCondition extends Condition {
protected Operator operator; protected Operator operator;
protected Condition[] conditions; protected List<Condition> conditions;
protected MultiCondition(Operator operator, Condition... conditions) { protected MultiCondition(Operator operator, Condition... conditions) {
this.operator = operator; this.operator = operator;
this.conditions = conditions; this.conditions = Arrays.asList(conditions);
}
public void append(Condition condition) {
if (condition != null) {
conditions.add(condition);
}
} }
} }
protected abstract static class NotCondition extends Condition { public abstract static class NotCondition extends Condition {
protected Condition condition; protected Condition condition;
protected NotCondition(Condition condition) { protected NotCondition(Condition condition) {
@ -864,24 +821,40 @@ public abstract class ExchangeSession {
} }
} }
protected abstract static class IsNullCondition extends Condition { public abstract static class MonoCondition extends Condition {
protected String attributeName; protected String attributeName;
protected Operator operator;
protected IsNullCondition(String attributeName) { protected MonoCondition(String attributeName, Operator operator) {
this.attributeName = attributeName; this.attributeName = attributeName;
this.operator = operator;
} }
} }
public abstract Condition and(Condition... condition); public abstract MultiCondition and(Condition... condition);
public abstract Condition or(Condition... condition); public abstract MultiCondition or(Condition... condition);
public abstract Condition not(Condition condition); public abstract Condition not(Condition condition);
public abstract Condition equals(String attributeName, String value); public abstract Condition equals(String attributeName, String value);
public abstract Condition headerEquals(String headerName, String value);
public abstract Condition gte(String attributeName, String value);
public abstract Condition gt(String attributeName, String value);
public abstract Condition lt(String attributeName, String value);
public abstract Condition like(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 isFalse(String attributeName);
/** /**
* Search mail and generic folders under given folder. * Search mail and generic folders under given folder.
* Exclude calendar and contacts folders * Exclude calendar and contacts folders
@ -1288,19 +1261,19 @@ public abstract class ExchangeSession {
* @throws IOException on error * @throws IOException on error
*/ */
public void loadMessages() throws IOException { public void loadMessages() throws IOException {
messages = ExchangeSession.this.searchMessages(folderPath, ""); messages = ExchangeSession.this.searchMessages(folderPath, null);
fixUids(messages); fixUids(messages);
} }
/** /**
* Search messages in folder matching query. * Search messages in folder matching query.
* *
* @param query search query * @param condition search query
* @return message list * @return message list
* @throws IOException on error * @throws IOException on error
*/ */
public MessageList searchMessages(String query) throws IOException { public MessageList searchMessages(Condition condition) throws IOException {
MessageList localMessages = ExchangeSession.this.searchMessages(folderName, query); MessageList localMessages = ExchangeSession.this.searchMessages(folderName, condition);
fixUids(localMessages); fixUids(localMessages);
return localMessages; return localMessages;
} }
@ -1406,23 +1379,23 @@ public abstract class ExchangeSession {
/** /**
* enclosing message list * enclosing message list
*/ */
protected MessageList messageList; public MessageList messageList;
/** /**
* Message url. * Message url.
*/ */
protected String messageUrl; public String messageUrl;
/** /**
* Message permanent url (does not change on message move). * Message permanent url (does not change on message move).
*/ */
protected String permanentUrl; public String permanentUrl;
/** /**
* Message uid. * Message uid.
*/ */
protected String uid; public String uid;
/** /**
* Message IMAP uid, unique in folder (x0e230003). * Message IMAP uid, unique in folder (x0e230003).
*/ */
protected long imapUid; public long imapUid;
/** /**
* MAPI message size. * MAPI message size.
*/ */
@ -3011,7 +2984,7 @@ public abstract class ExchangeSession {
// failover for Exchange 2007 plus encoding issue // failover for Exchange 2007 plus encoding issue
String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''"); String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
LOGGER.debug("Item not found at " + itemPath + ", search by displayname: '" + decodedEventName + '\''); LOGGER.debug("Item not found at " + itemPath + ", search by displayname: '" + decodedEventName + '\'');
ExchangeSession.MessageList messages = searchMessages(folderPath, " AND \"DAV:displayname\"='" + decodedEventName + '\''); ExchangeSession.MessageList messages = searchMessages(folderPath, equals("displayname", decodedEventName));
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
item = getItem(messages.get(0).getPermanentUrl()); item = getItem(messages.get(0).getPermanentUrl());
} else { } else {

View File

@ -18,10 +18,12 @@
*/ */
package davmail.exchange.dav; package davmail.exchange.dav;
import davmail.BundleMessage;
import davmail.exception.DavMailAuthenticationException; import davmail.exception.DavMailAuthenticationException;
import davmail.exception.DavMailException; import davmail.exception.DavMailException;
import davmail.exchange.ExchangeSession; import davmail.exchange.ExchangeSession;
import davmail.http.DavGatewayHttpClientFacade; import davmail.http.DavGatewayHttpClientFacade;
import davmail.ui.tray.DavGatewayTray;
import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URI;
@ -29,6 +31,9 @@ import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil; import org.apache.commons.httpclient.util.URIUtil;
import org.apache.jackrabbit.webdav.MultiStatusResponse; import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavPropertySet; import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.xml.Namespace; import org.apache.jackrabbit.webdav.xml.Namespace;
@ -36,16 +41,25 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* Webdav Exchange adapter. * Webdav Exchange adapter.
* Compatible with Exchange 2003 and 2007 with webdav available. * Compatible with Exchange 2003 and 2007 with webdav available.
*/ */
public class DavExchangeSession extends ExchangeSession { public class DavExchangeSession extends ExchangeSession {
protected static final DavPropertyNameSet WELL_KNOWN_FOLDERS = new DavPropertyNameSet();
static {
WELL_KNOWN_FOLDERS.add(Field.get("inbox").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("deleteditems").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("sentitems").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("sendmsg").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("drafts").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("calendar").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("contacts").davPropertyName);
WELL_KNOWN_FOLDERS.add(Field.get("outbox").davPropertyName);
}
/** /**
* @inheritDoc * @inheritDoc
@ -109,6 +123,15 @@ public class DavExchangeSession extends ExchangeSession {
} }
} }
protected String getURIPropertyIfExists(DavPropertySet properties, String alias) throws URIException {
DavProperty property = properties.get(Field.get(alias).davPropertyName);
if (property == null) {
return null;
} else {
return URIUtil.decode((String) property.getValue());
}
}
protected void getWellKnownFolders() throws DavMailException { protected void getWellKnownFolders() throws DavMailException {
// Retrieve well known URLs // Retrieve well known URLs
MultiStatusResponse[] responses; MultiStatusResponse[] responses;
@ -119,14 +142,14 @@ public class DavExchangeSession extends ExchangeSession {
throw new DavMailException("EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER", mailPath); throw new DavMailException("EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER", mailPath);
} }
DavPropertySet properties = responses[0].getProperties(HttpStatus.SC_OK); DavPropertySet properties = responses[0].getProperties(HttpStatus.SC_OK);
inboxUrl = getURIPropertyIfExists(properties, "inbox", URN_SCHEMAS_HTTPMAIL); inboxUrl = getURIPropertyIfExists(properties, "inbox");
deleteditemsUrl = getURIPropertyIfExists(properties, "deleteditems", URN_SCHEMAS_HTTPMAIL); deleteditemsUrl = getURIPropertyIfExists(properties, "deleteditems");
sentitemsUrl = getURIPropertyIfExists(properties, "sentitems", URN_SCHEMAS_HTTPMAIL); sentitemsUrl = getURIPropertyIfExists(properties, "sentitems");
sendmsgUrl = getURIPropertyIfExists(properties, "sendmsg", URN_SCHEMAS_HTTPMAIL); sendmsgUrl = getURIPropertyIfExists(properties, "sendmsg");
draftsUrl = getURIPropertyIfExists(properties, "drafts", URN_SCHEMAS_HTTPMAIL); draftsUrl = getURIPropertyIfExists(properties, "drafts");
calendarUrl = getURIPropertyIfExists(properties, "calendar", URN_SCHEMAS_HTTPMAIL); calendarUrl = getURIPropertyIfExists(properties, "calendar");
contactsUrl = getURIPropertyIfExists(properties, "contacts", URN_SCHEMAS_HTTPMAIL); contactsUrl = getURIPropertyIfExists(properties, "contacts");
outboxUrl = getURIPropertyIfExists(properties, "outbox", URN_SCHEMAS_HTTPMAIL); outboxUrl = getURIPropertyIfExists(properties, "outbox");
// junk folder not available over webdav // junk folder not available over webdav
// default public folder path // default public folder path
@ -208,17 +231,16 @@ public class DavExchangeSession extends ExchangeSession {
} }
} }
static final Map<String, String> attributeMap = new HashMap<String, String>();
static {
attributeMap.put("folderclass", "http://schemas.microsoft.com/exchange/outlookfolderclass");
attributeMap.put("contentclass", "DAV:contentclass");
}
static final Map<Operator, String> operatorMap = new HashMap<Operator, String>(); static final Map<Operator, String> operatorMap = new HashMap<Operator, String>();
static { static {
operatorMap.put(Operator.IsEqualTo, "="); operatorMap.put(Operator.IsEqualTo, " = ");
operatorMap.put(Operator.IsGreaterThanOrEqualTo, " >= ");
operatorMap.put(Operator.IsGreaterThan, " > ");
operatorMap.put(Operator.IsLessThan, " < ");
operatorMap.put(Operator.Like, " like ");
operatorMap.put(Operator.IsNull, " is null");
} }
protected static class AttributeCondition extends ExchangeSession.AttributeCondition { protected static class AttributeCondition extends ExchangeSession.AttributeCondition {
@ -228,47 +250,116 @@ public class DavExchangeSession extends ExchangeSession {
@Override @Override
public void appendTo(StringBuilder buffer) { public void appendTo(StringBuilder buffer) {
buffer.append('"').append(attributeMap.get(attributeName)).append('"'); buffer.append('"').append(Field.get(attributeName).getUri()).append('"');
buffer.append(operatorMap.get(operator)); buffer.append(operatorMap.get(operator));
buffer.append('\'').append(value).append('\''); buffer.append('\'');
if (Operator.Like == operator) {
buffer.append('%');
}
buffer.append(value);
if (Operator.Like == operator) {
buffer.append('%');
}
buffer.append('\'');
} }
} }
protected static class IsNullCondition extends ExchangeSession.IsNullCondition { protected static class HeaderCondition extends AttributeCondition {
protected IsNullCondition(String attributeName) {
super(attributeName); protected HeaderCondition(String attributeName, Operator operator, String value) {
super(attributeName, operator, value);
} }
@Override @Override
public void appendTo(StringBuilder buffer) { public void appendTo(StringBuilder buffer) {
buffer.append('"').append(attributeMap.get(attributeName)).append('"'); buffer.append('"').append(Field.getHeader(attributeName)).append('"');
buffer.append(" is null"); buffer.append(operatorMap.get(operator));
buffer.append('\'');
if (Operator.Like == operator) {
buffer.append('%');
}
buffer.append(value);
if (Operator.Like == operator) {
buffer.append('%');
}
buffer.append('\'');
}
}
protected static class MonoCondition extends ExchangeSession.MonoCondition {
protected MonoCondition(String attributeName, Operator operator) {
super(attributeName, operator);
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append('"').append(Field.get(attributeName).getUri()).append('"');
buffer.append(operatorMap.get(operator));
} }
} }
@Override @Override
public Condition and(Condition... condition) { public MultiCondition and(Condition... condition) {
return new MultiCondition(Operator.And, condition); return new MultiCondition(Operator.And, condition);
} }
@Override @Override
public Condition or(Condition... condition) { public MultiCondition or(Condition... condition) {
return new MultiCondition(Operator.Or, condition); return new MultiCondition(Operator.Or, condition);
} }
@Override @Override
public Condition not(Condition condition) { public Condition not(Condition condition) {
if (condition == null) {
return null;
} else {
return new NotCondition(condition); return new NotCondition(condition);
} }
}
@Override @Override
public Condition equals(String attributeName, String value) { public Condition equals(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsEqualTo, value); return new AttributeCondition(attributeName, Operator.IsEqualTo, value);
} }
@Override
public Condition headerEquals(String headerName, String value) {
return new HeaderCondition(headerName, Operator.IsEqualTo, value);
}
@Override
public Condition gte(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsGreaterThanOrEqualTo, value);
}
@Override
public Condition lt(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsLessThan, value);
}
@Override
public Condition gt(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsGreaterThan, value);
}
@Override
public Condition like(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.Like, value);
}
@Override @Override
public Condition isNull(String attributeName) { public Condition isNull(String attributeName) {
return new IsNullCondition(attributeName); return new MonoCondition(attributeName, Operator.IsNull);
}
@Override
public Condition isTrue(String attributeName) {
return new MonoCondition(attributeName, Operator.IsTrue);
}
@Override
public Condition isFalse(String attributeName) {
return new MonoCondition(attributeName, Operator.IsFalse);
} }
@ -364,4 +455,98 @@ public class DavExchangeSession extends ExchangeSession {
return folders; return folders;
} }
protected String getPropertyIfExists(DavPropertySet properties, String name) {
DavProperty property = properties.get(name, EMPTY);
if (property == null) {
return null;
} else {
return (String) property.getValue();
}
}
protected int getIntPropertyIfExists(DavPropertySet properties, String name) {
DavProperty property = properties.get(name, EMPTY);
if (property == null) {
return 0;
} else {
return Integer.parseInt((String) property.getValue());
}
}
protected long getLongPropertyIfExists(DavPropertySet properties, String name) {
DavProperty property = properties.get(name, EMPTY);
if (property == null) {
return 0;
} else {
return Long.parseLong((String) property.getValue());
}
}
protected Message buildMessage(MultiStatusResponse responseEntity) throws URIException {
Message message = new Message();
message.messageUrl = URIUtil.decode(responseEntity.getHref());
DavPropertySet properties = responseEntity.getProperties(HttpStatus.SC_OK);
message.permanentUrl = getPropertyIfExists(properties, "permanenturl");
message.size = getIntPropertyIfExists(properties, "messageSize");
message.uid = getPropertyIfExists(properties, "uid");
message.imapUid = getLongPropertyIfExists(properties, "imapUid");
message.read = "1".equals(getPropertyIfExists(properties, "read"));
message.junk = "1".equals(getPropertyIfExists(properties, "junk"));
message.flagged = "2".equals(getPropertyIfExists(properties, "flagStatus"));
message.draft = "9".equals(getPropertyIfExists(properties, "messageFlags"));
String lastVerbExecuted = getPropertyIfExists(properties, "lastVerbExecuted");
message.answered = "102".equals(lastVerbExecuted) || "103".equals(lastVerbExecuted);
message.forwarded = "104".equals(lastVerbExecuted);
message.date = getPropertyIfExists(properties, "date");
message.deleted = "1".equals(getPropertyIfExists(properties, "deleted"));
if (LOGGER.isDebugEnabled()) {
StringBuilder buffer = new StringBuilder();
buffer.append("Message");
if (message.imapUid != 0) {
buffer.append(" IMAP uid: ").append(message.imapUid);
}
if (message.uid != null) {
buffer.append(" uid: ").append(message.uid);
}
buffer.append(" href: ").append(responseEntity.getHref()).append(" permanenturl:").append(message.permanentUrl);
LOGGER.debug(buffer.toString());
}
return message;
}
@Override
public MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException {
String folderUrl = getFolderPath(folderName);
MessageList messages = new MessageList();
StringBuilder searchRequest = new StringBuilder();
searchRequest.append("Select \"http://schemas.microsoft.com/exchange/permanenturl\" as permanenturl");
if (attributes != null) {
for (String attribute : attributes) {
Field field = Field.get(attribute);
searchRequest.append(",\"").append(field.getUri()).append("\" as ").append(field.getAlias());
}
}
searchRequest.append(" FROM Scope('SHALLOW TRAVERSAL OF \"").append(folderUrl).append("\"')\n")
.append(" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = False\n");
if (condition != null) {
searchRequest.append(" AND ");
condition.appendTo(searchRequest);
}
// TODO order by ImapUid
//searchRequest.append(" ORDER BY \"urn:schemas:httpmail:date\" ASC");
DavGatewayTray.debug(new BundleMessage("LOG_SEARCH_QUERY", searchRequest));
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
httpClient, URIUtil.encodePath(folderUrl), searchRequest.toString());
for (MultiStatusResponse response : responses) {
Message message = buildMessage(response);
message.messageList = messages;
messages.add(message);
}
Collections.sort(messages);
return messages;
}
} }

View File

@ -0,0 +1,188 @@
/*
* 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.dav;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.xml.Namespace;
import java.util.HashMap;
import java.util.Map;
/**
* WebDav Field
*/
public class Field {
protected static final Namespace DAV = Namespace.getNamespace("DAV:");
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
protected static final Namespace URN_SCHEMAS_MAILHEADER = Namespace.getNamespace("urn:schemas:mailheader:");
protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/");
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace.getNamespace("http://schemas.microsoft.com/mapi/proptag/");
protected static final Namespace SCHEMAS_MAPI_ID = Namespace.getNamespace("http://schemas.microsoft.com/mapi/id/");
protected static final Namespace SCHEMAS_MAPI_STRING = Namespace.getNamespace("http://schemas.microsoft.com/mapi/string/");
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
protected static enum PropertyType {
ApplicationTime, ApplicationTimeArray, Binary, BinaryArray, Boolean, CLSID, CLSIDArray, Currency, CurrencyArray,
Double, DoubleArray, Error, Float, FloatArray, Integer, IntegerArray, Long, LongArray, Null, Object,
ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray
}
protected static final Map<PropertyType, String> propertyTypeMap = new HashMap<PropertyType, String>();
static {
propertyTypeMap.put(PropertyType.Integer, "0003");
propertyTypeMap.put(PropertyType.Boolean, "000b");
propertyTypeMap.put(PropertyType.SystemTime, "0040");
propertyTypeMap.put(PropertyType.String, "001f");
}
protected static enum DistinguishedPropertySetType {
Meeting, Appointment, Common, PublicStrings, Address, InternetHeaders, CalendarAssistant, UnifiedMessaging, Task
}
protected static final Map<DistinguishedPropertySetType, String> distinguishedPropertySetMap = new HashMap<DistinguishedPropertySetType, String>();
static {
distinguishedPropertySetMap.put(DistinguishedPropertySetType.Meeting, "6ed8da90-450b-101b-98da-00aa003f1305");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.Appointment, "00062002-0000-0000-c000-000000000046");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.Common, "00062008-0000-0000-c000-000000000046");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.PublicStrings, "00020329-0000-0000-c000-000000000046");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.Address, "00062004-0000-0000-c000-000000000046");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.InternetHeaders, "00020386-0000-0000-c000-000000000046");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.UnifiedMessaging, "4442858e-a9e3-4e80-b900-317a210cc15b");
distinguishedPropertySetMap.put(DistinguishedPropertySetType.Task, "00062003-0000-0000-c000-000000000046");
}
protected static final Map<String, Field> fieldMap = new HashMap<String, Field>();
static {
// well known folders
createField(URN_SCHEMAS_HTTPMAIL, "inbox");
createField(URN_SCHEMAS_HTTPMAIL, "deleteditems");
createField(URN_SCHEMAS_HTTPMAIL, "sentitems");
createField(URN_SCHEMAS_HTTPMAIL, "sendmsg");
createField(URN_SCHEMAS_HTTPMAIL, "drafts");
createField(URN_SCHEMAS_HTTPMAIL, "calendar");
createField(URN_SCHEMAS_HTTPMAIL, "contacts");
createField(URN_SCHEMAS_HTTPMAIL, "outbox");
// folder
createField("folderclass", SCHEMAS_EXCHANGE, "outlookfolderclass");
// POP and IMAP message
createField(DAV, "uid");
createField("messageSize", 0x0e08, PropertyType.Integer);//PR_MESSAGE_SIZE
createField("imapUid", 0x0e23, PropertyType.Integer);//PR_INTERNET_ARTICLE_NUMBER
createField("junk", 0x1083, PropertyType.Integer);
createField("flagStatus", 0x1090, PropertyType.Integer);//PR_FLAG_STATUS
createField("messageFlags", 0x0e07, PropertyType.Integer);//PR_MESSAGE_FLAGS
createField("lastVerbExecuted", 0x1081, PropertyType.Integer);//PR_LAST_VERB_EXECUTED
createField(URN_SCHEMAS_HTTPMAIL, "read");
//createField("read", 0x0e69, PropertyType.Boolean);//PR_READ
createField("deleted", DistinguishedPropertySetType.Common, 0x8570);
createField(URN_SCHEMAS_HTTPMAIL, "date"); //PR_CLIENT_SUBMIT_TIME, 0x0039
//createField("date", 0x0e06, PropertyType.SystemTime);//PR_MESSAGE_DELIVERY_TIME
// IMAP search
createField(URN_SCHEMAS_HTTPMAIL, "subject");
//createField("subject", 0x0037, PropertyType.String);//PR_SUBJECT
createField("body", 0x1000, PropertyType.String);//PR_BODY
createField(URN_SCHEMAS_HTTPMAIL, "from");
//createField("from", DistinguishedPropertySetType.PublicStrings, 0x001f);//urn:schemas:httpmail:from
createField(URN_SCHEMAS_MAILHEADER, "to");
createField(URN_SCHEMAS_MAILHEADER, "cc");
createField("lastmodified", 0x3008, PropertyType.SystemTime);//PR_LAST_MODIFICATION_TIME DAV:getlastmodified
// failover search
createField(DAV, "displayname");
}
protected static void createField(String alias, int propertyTag, PropertyType propertyType) {
String name = 'x' + Integer.toHexString(propertyTag) + propertyTypeMap.get(propertyType);
Field field = new Field(alias, SCHEMAS_MAPI_PROPTAG, name);
fieldMap.put(field.alias, field);
}
protected static void createField(String alias, DistinguishedPropertySetType propertySetType, int propertyTag) {
String name = '{' + distinguishedPropertySetMap.get(propertySetType) + "}/0x" + Integer.toHexString(propertyTag);
Field field = new Field(alias, SCHEMAS_MAPI_ID, name);
fieldMap.put(field.alias, field);
}
protected static void createField(Namespace namespace, String name) {
Field field = new Field(namespace, name);
fieldMap.put(field.alias, field);
}
protected static void createField(String alias, Namespace namespace, String name) {
Field field = new Field(alias, namespace, name);
fieldMap.put(field.alias, field);
}
protected final DavPropertyName davPropertyName;
protected final String alias;
protected final String uri;
public Field(Namespace namespace, String name) {
this(name, namespace, name);
}
public Field(String alias, Namespace namespace, String name) {
davPropertyName = DavPropertyName.create(name, namespace);
this.alias = alias;
this.uri = namespace.getURI()+name;
}
public String getUri() {
return uri;
}
public String getAlias() {
return alias;
}
/**
* Get Field by alias.
*
* @param alias field alias
* @return field
*/
public static Field get(String alias) {
Field field = fieldMap.get(alias);
if (field == null) {
throw new IllegalArgumentException("Unknown field: " + alias);
}
return field;
}
/**
* Get Mime header fieks.
*
* @param alias field alias
* @return field
*/
public static Field getHeader(String headerName) {
String name = '{' + distinguishedPropertySetMap.get(DistinguishedPropertySetType.InternetHeaders) + "}/" + headerName;
return new Field(SCHEMAS_MAPI_STRING, name);
}
}

View File

@ -86,6 +86,12 @@ public class EwsExchangeSession extends ExchangeSession {
} }
} }
@Override
public MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException {
// TODO
throw new UnsupportedOperationException();
}
protected static class MultiCondition extends ExchangeSession.MultiCondition implements SearchExpression { protected static class MultiCondition extends ExchangeSession.MultiCondition implements SearchExpression {
protected MultiCondition(Operator operator, Condition... condition) { protected MultiCondition(Operator operator, Condition... condition) {
super(operator, condition); super(operator, condition);
@ -111,9 +117,7 @@ public class EwsExchangeSession extends ExchangeSession {
@Override @Override
public void appendTo(StringBuilder buffer) { public void appendTo(StringBuilder buffer) {
buffer.append("<t:Not>"); buffer.append("<t:Not>");
condition.appendTo(buffer); condition.appendTo(buffer);
buffer.append("</t:Not>"); buffer.append("</t:Not>");
} }
} }
@ -122,6 +126,7 @@ public class EwsExchangeSession extends ExchangeSession {
static { static {
attributeMap.put("folderclass", ExtendedFieldURI.PR_CONTAINER_CLASS); attributeMap.put("folderclass", ExtendedFieldURI.PR_CONTAINER_CLASS);
attributeMap.put("read", ExtendedFieldURI.PR_READ);
} }
protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression { protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression {
@ -129,10 +134,18 @@ public class EwsExchangeSession extends ExchangeSession {
super(attributeName, operator, value); super(attributeName, operator, value);
} }
protected FieldURI getFieldURI(String attributeName) {
FieldURI fieldURI = attributeMap.get(attributeName);
if (fieldURI == null) {
throw new IllegalArgumentException("Unknown field: " + attributeName);
}
return fieldURI;
}
@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()).append('>');
attributeMap.get(attributeName).appendTo(buffer); getFieldURI(attributeName).appendTo(buffer);
buffer.append("<t:FieldURIOrConstant><t:Constant Value=\""); buffer.append("<t:FieldURIOrConstant><t:Constant Value=\"");
buffer.append(StringUtil.xmlEncode(value)); buffer.append(StringUtil.xmlEncode(value));
@ -142,9 +155,24 @@ public class EwsExchangeSession extends ExchangeSession {
} }
} }
protected static class IsNullCondition extends ExchangeSession.IsNullCondition implements SearchExpression { protected static class HeaderCondition extends AttributeCondition {
protected HeaderCondition(String attributeName, Operator operator, String value) {
super(attributeName, operator, value);
}
@Override
protected FieldURI getFieldURI(String attributeName) {
return new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.InternetHeaders, attributeName);
}
}
protected static class IsNullCondition extends ExchangeSession.Condition implements SearchExpression {
protected String attributeName;
protected IsNullCondition(String attributeName) { protected IsNullCondition(String attributeName) {
super(attributeName); this.attributeName = attributeName;
} }
@Override @Override
@ -156,12 +184,12 @@ public class EwsExchangeSession extends ExchangeSession {
} }
@Override @Override
public Condition and(Condition... condition) { public MultiCondition and(Condition... condition) {
return new MultiCondition(Operator.And, condition); return new MultiCondition(Operator.And, condition);
} }
@Override @Override
public Condition or(Condition... condition) { public MultiCondition or(Condition... condition) {
return new MultiCondition(Operator.Or, condition); return new MultiCondition(Operator.Or, condition);
} }
@ -175,11 +203,46 @@ public class EwsExchangeSession extends ExchangeSession {
return new AttributeCondition(attributeName, Operator.IsEqualTo, value); return new AttributeCondition(attributeName, Operator.IsEqualTo, value);
} }
@Override
public Condition headerEquals(String headerName, String value) {
return new HeaderCondition(headerName, Operator.IsEqualTo, value);
}
@Override
public Condition gte(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsGreaterThanOrEqualTo, value);
}
@Override
public Condition lt(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsLessThan, value);
}
@Override
public Condition gt(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsGreaterThan, value);
}
@Override
public Condition like(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.Like, value);
}
@Override @Override
public Condition isNull(String attributeName) { public Condition isNull(String attributeName) {
return new IsNullCondition(attributeName); return new IsNullCondition(attributeName);
} }
@Override
public Condition isTrue(String attributeName) {
return new AttributeCondition(attributeName, Operator.IsEqualTo, "True");
}
@Override
public Condition isFalse(String attributeName) {
return new AttributeCondition(attributeName, Operator.IsEqualTo, "False");
}
protected Folder buildFolder(EWSMethod.Item item) { protected Folder buildFolder(EWSMethod.Item item) {
Folder folder = new Folder(); Folder folder = new Folder();
folder.folderId = new FolderId(item.get("FolderId")); folder.folderId = new FolderId(item.get("FolderId"));

View File

@ -31,22 +31,34 @@ public class ExtendedFieldURI implements FieldURI {
ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray
} }
protected static enum DistinguishedPropertySetType {
Meeting, Appointment, Common, PublicStrings, Address, InternetHeaders, CalendarAssistant, UnifiedMessaging, Task
}
protected String propertyTag; protected String propertyTag;
protected DistinguishedPropertySetType distinguishedPropertySetId;
protected String propertySetId; protected String propertySetId;
protected String propertyName;
protected int propertyId; protected int propertyId;
protected final PropertyType propertyType; protected PropertyType propertyType;
public ExtendedFieldURI(int intPropertyTag, PropertyType propertyType) { public ExtendedFieldURI(int intPropertyTag, PropertyType propertyType) {
this.propertyTag = "0x"+Integer.toHexString(intPropertyTag); this.propertyTag = "0x" + Integer.toHexString(intPropertyTag);
this.propertyType = propertyType; this.propertyType = propertyType;
} }
public ExtendedFieldURI(String propertySetId, int propertyId, PropertyType propertyType) { public ExtendedFieldURI(DistinguishedPropertySetType distinguishedPropertySetId, int propertyId, PropertyType propertyType) {
this.propertySetId = propertySetId; this.distinguishedPropertySetId = distinguishedPropertySetId;
this.propertyId = propertyId; this.propertyId = propertyId;
this.propertyType = propertyType; this.propertyType = propertyType;
} }
public ExtendedFieldURI(DistinguishedPropertySetType distinguishedPropertySetId, String propertyName) {
this.distinguishedPropertySetId = distinguishedPropertySetId;
this.propertyName = propertyName;
}
public String getPropertyTag() { public String getPropertyTag() {
return propertyTag; return propertyTag;
} }
@ -56,14 +68,22 @@ public class ExtendedFieldURI implements FieldURI {
if (propertyTag != null) { if (propertyTag != null) {
buffer.append("PropertyTag=\"").append(propertyTag).append("\" "); buffer.append("PropertyTag=\"").append(propertyTag).append("\" ");
} }
if (distinguishedPropertySetId != null) {
buffer.append("DistinguishedPropertySetId=\"").append(distinguishedPropertySetId).append("\" ");
}
if (propertySetId != null) { if (propertySetId != null) {
buffer.append("PropertySetId=\"").append(propertySetId).append("\" "); buffer.append("PropertySetId=\"").append(propertySetId).append("\" ");
} }
if (propertyName != null) {
buffer.append("propertyName=\"").append(propertyName).append("\" ");
}
if (propertyId != 0) { if (propertyId != 0) {
buffer.append("PropertyId=\"").append(String.valueOf(propertyId)).append("\" "); buffer.append("PropertyId=\"").append(String.valueOf(propertyId)).append("\" ");
} }
if (propertyType != null) {
buffer.append("PropertyType=\"").append(propertyType.toString()).append("\"/>"); buffer.append("PropertyType=\"").append(propertyType.toString()).append("\"/>");
} }
}
public static final ExtendedFieldURI PR_INSTANCE_KEY = new ExtendedFieldURI(0xff6, PropertyType.Binary); public static final ExtendedFieldURI PR_INSTANCE_KEY = new ExtendedFieldURI(0xff6, PropertyType.Binary);
public static final ExtendedFieldURI PR_MESSAGE_SIZE = new ExtendedFieldURI(0xe08, PropertyType.Integer); public static final ExtendedFieldURI PR_MESSAGE_SIZE = new ExtendedFieldURI(0xe08, PropertyType.Integer);
@ -77,6 +97,8 @@ public class ExtendedFieldURI implements FieldURI {
public static final ExtendedFieldURI PR_LAST_MODIFICATION_TIME = new ExtendedFieldURI(0x3008, PropertyType.SystemTime); public static final ExtendedFieldURI PR_LAST_MODIFICATION_TIME = new ExtendedFieldURI(0x3008, PropertyType.SystemTime);
// message properties
public static final ExtendedFieldURI PR_READ = new ExtendedFieldURI(0xe69, PropertyType.Boolean);
} }

View File

@ -730,45 +730,36 @@ public class ImapConnection extends AbstractConnection {
sendClient(commandId + " OK STORE completed"); sendClient(commandId + " OK STORE completed");
} }
protected void buildConditions(SearchConditions conditions, IMAPTokenizer tokens) throws IOException { protected ExchangeSession.Condition buildConditions(SearchConditions conditions, IMAPTokenizer tokens) throws IOException {
boolean or = false; ExchangeSession.MultiCondition condition = null;
while (tokens.hasMoreTokens()) { while (tokens.hasMoreTokens()) {
String token = tokens.nextQuotedToken().toUpperCase(); String token = tokens.nextQuotedToken().toUpperCase();
if (token.startsWith("(") && token.endsWith(")")) { if (token.startsWith("(") && token.endsWith(")")) {
// quoted search param // quoted search param
buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1))); if (condition == null) {
condition = session.and();
}
condition.append(buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1))));
} else if ("OR".equals(token)) { } else if ("OR".equals(token)) {
or = true; condition = session.or();
} else if (token.startsWith("OR ")) { } else if (token.startsWith("OR ")) {
or = true; condition = appendOrSearchParams(token, conditions);
appendOrSearchParams(token, conditions);
} else { } else {
String operator; if (condition == null) {
if (conditions.query.length() == 5) { condition = session.and();
operator = "";
} else if (or) {
operator = " OR ";
} else {
operator = " AND ";
} }
appendSearchParam(operator, tokens, token, conditions); condition.append(appendSearchParam(tokens, token, conditions));
} }
} }
return condition;
} }
protected List<Long> handleSearch(IMAPTokenizer tokens) throws IOException { protected List<Long> handleSearch(IMAPTokenizer tokens) throws IOException {
List<Long> uidList = new ArrayList<Long>(); List<Long> uidList = new ArrayList<Long>();
SearchConditions conditions = new SearchConditions(); SearchConditions conditions = new SearchConditions();
conditions.append("AND ("); ExchangeSession.Condition condition = buildConditions(conditions, tokens);
buildConditions(conditions, tokens); ExchangeSession.MessageList localMessages = currentFolder.searchMessages(condition);
conditions.append(")");
String query = conditions.query.toString();
DavGatewayTray.debug(new BundleMessage("LOG_SEARCH_QUERY", conditions.query));
if ("AND ()".equals(query)) {
query = null;
}
ExchangeSession.MessageList localMessages = currentFolder.searchMessages(query);
Iterator<ExchangeSession.Message> iterator; Iterator<ExchangeSession.Message> iterator;
if (conditions.uidRange != null) { if (conditions.uidRange != null) {
iterator = new UIDRangeIterator(localMessages, conditions.uidRange); iterator = new UIDRangeIterator(localMessages, conditions.uidRange);
@ -988,63 +979,60 @@ public class ImapConnection extends AbstractConnection {
} }
} }
/**
* client side search conditions
*/
static final class SearchConditions { static final class SearchConditions {
Boolean flagged; Boolean flagged;
Boolean answered; Boolean answered;
Boolean deleted; Boolean deleted;
String indexRange; String indexRange;
String uidRange; String uidRange;
final StringBuilder query = new StringBuilder();
public StringBuilder append(String value) {
return query.append(value);
}
} }
protected void appendOrSearchParams(String token, SearchConditions conditions) throws IOException { protected ExchangeSession.MultiCondition appendOrSearchParams(String token, SearchConditions conditions) throws IOException {
ExchangeSession.MultiCondition orCondition = session.or();
IMAPTokenizer innerTokens = new IMAPTokenizer(token); IMAPTokenizer innerTokens = new IMAPTokenizer(token);
innerTokens.nextToken(); innerTokens.nextToken();
boolean first = true;
while (innerTokens.hasMoreTokens()) { while (innerTokens.hasMoreTokens()) {
String innerToken = innerTokens.nextToken(); String innerToken = innerTokens.nextToken();
String operator = ""; orCondition.append(appendSearchParam(innerTokens, innerToken, conditions));
if (!first) {
operator = " OR ";
} }
first = false; return orCondition;
appendSearchParam(operator, innerTokens, innerToken, conditions);
} }
} protected ExchangeSession.Condition appendSearchParam(StringTokenizer tokens, String token, SearchConditions conditions) throws IOException {
protected void appendSearchParam(String operator, StringTokenizer tokens, String token, SearchConditions conditions) throws IOException {
if ("NOT".equals(token)) { if ("NOT".equals(token)) {
appendSearchParam(operator + " NOT ", tokens, tokens.nextToken(), conditions); String nextToken = tokens.nextToken();
if ("DELETED".equals(token)) {
conditions.deleted = Boolean.FALSE;
} else {
return session.not(appendSearchParam(tokens, nextToken, conditions));
}
} else if (token.startsWith("OR ")) { } else if (token.startsWith("OR ")) {
appendOrSearchParams(token, conditions); return appendOrSearchParams(token, conditions);
} else if ("SUBJECT".equals(token)) { } else if ("SUBJECT".equals(token)) {
conditions.append(operator).append("\"urn:schemas:httpmail:subject\" LIKE '%").append(tokens.nextToken()).append("%'"); return session.like("subject", tokens.nextToken());
} else if ("BODY".equals(token)) { } else if ("BODY".equals(token)) {
conditions.append(operator).append("\"http://schemas.microsoft.com/mapi/proptag/x01000001E\" LIKE '%").append(tokens.nextToken()).append("%'"); return session.like("body", tokens.nextToken());
} else if ("FROM".equals(token)) { } else if ("FROM".equals(token)) {
conditions.append(operator).append("\"urn:schemas:mailheader:from\" LIKE '%").append(tokens.nextToken()).append("%'"); return session.like("from", tokens.nextToken());
} else if ("TO".equals(token)) { } else if ("TO".equals(token)) {
conditions.append(operator).append("\"urn:schemas:mailheader:to\" LIKE '%").append(tokens.nextToken()).append("%'"); return session.like("to", tokens.nextToken());
} else if ("CC".equals(token)) { } else if ("CC".equals(token)) {
conditions.append(operator).append("\"urn:schemas:mailheader:cc\" LIKE '%").append(tokens.nextToken()).append("%'"); return session.like("cc", tokens.nextToken());
} else if ("LARGER".equals(token)) { } else if ("LARGER".equals(token)) {
conditions.append(operator).append("\"http://schemas.microsoft.com/mapi/proptag/x0e080003\" &gt;= ").append(Long.parseLong(tokens.nextToken())).append(""); return session.gte("messageSize", tokens.nextToken());
} else if ("SMALLER".equals(token)) { } else if ("SMALLER".equals(token)) {
conditions.append(operator).append("\"http://schemas.microsoft.com/mapi/proptag/x0e080003\" < ").append(Long.parseLong(tokens.nextToken())).append(""); return session.lt("messageSize", tokens.nextToken());
} else if (token.startsWith("SENT") || "SINCE".equals(token) || "BEFORE".equals(token)) { } else if (token.startsWith("SENT") || "SINCE".equals(token) || "BEFORE".equals(token)) {
conditions.append(operator); return appendDateSearchParam(tokens, token);
appendDateSearchParam(tokens, token, conditions);
} else if ("SEEN".equals(token)) { } else if ("SEEN".equals(token)) {
conditions.append(operator).append("\"urn:schemas:httpmail:read\" = True"); return session.isTrue("read");
} else if ("UNSEEN".equals(token) || "NEW".equals(token)) { } else if ("UNSEEN".equals(token) || "NEW".equals(token)) {
conditions.append(operator).append("\"urn:schemas:httpmail:read\" = False"); return session.isFalse("read");
} else if ("DELETED".equals(token)) { } else if ("DELETED".equals(token)) {
conditions.deleted = !operator.endsWith(" NOT "); conditions.deleted = Boolean.TRUE;
} else if ("UNDELETED".equals(token) || "NOT DELETED".equals(token)) { } else if ("UNDELETED".equals(token) || "NOT DELETED".equals(token)) {
conditions.deleted = Boolean.FALSE; conditions.deleted = Boolean.FALSE;
} else if ("FLAGGED".equals(token)) { } else if ("FLAGGED".equals(token)) {
@ -1061,15 +1049,7 @@ public class ImapConnection extends AbstractConnection {
if ("message-id".equals(headerName) && !value.startsWith("<")) { if ("message-id".equals(headerName) && !value.startsWith("<")) {
value = '<' + value + '>'; value = '<' + value + '>';
} }
String namespace; return session.headerEquals(headerName, value);
if (headerName.startsWith("x-")) {
// look for extended headers in MAPI namespace
namespace = "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/";
} else {
// look for standard properties in mailheader namespace
namespace = "urn:schemas:mailheader:";
}
conditions.append(operator).append('"').append(namespace).append(headerName).append("\"='").append(value).append('\'');
} else if ("UID".equals(token)) { } else if ("UID".equals(token)) {
String range = tokens.nextToken(); String range = tokens.nextToken();
if ("1:*".equals(range)) { if ("1:*".equals(range)) {
@ -1085,9 +1065,11 @@ public class ImapConnection extends AbstractConnection {
} else { } else {
throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", token); throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", token);
} }
// client side search token
return null;
} }
protected void appendDateSearchParam(StringTokenizer tokens, String token, SearchConditions conditions) throws IOException { protected ExchangeSession.Condition appendDateSearchParam(StringTokenizer tokens, String token) throws IOException {
Date startDate; Date startDate;
Date endDate; Date endDate;
SimpleDateFormat parser = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH); SimpleDateFormat parser = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);
@ -1106,27 +1088,21 @@ public class ImapConnection extends AbstractConnection {
} }
String searchAttribute; String searchAttribute;
if (token.startsWith("SENT")) { if (token.startsWith("SENT")) {
searchAttribute = "urn:schemas:httpmail:date"; searchAttribute = "date";
} else { } else {
searchAttribute = "DAV:getlastmodified"; searchAttribute = "lastmodified";
} }
if (token.endsWith("ON")) { if (token.endsWith("ON")) {
conditions.append("(\"").append(searchAttribute).append("\" > '") return session.and(session.gt(searchAttribute, dateFormatter.format(startDate)),
.append(dateFormatter.format(startDate)) session.lt(searchAttribute, dateFormatter.format(endDate)));
.append("' AND \"").append(searchAttribute).append("\" < '")
.append(dateFormatter.format(endDate))
.append("')");
} else if (token.endsWith("BEFORE")) { } else if (token.endsWith("BEFORE")) {
conditions.append("\"").append(searchAttribute).append("\" < '") return session.lt(searchAttribute, dateFormatter.format(startDate));
.append(dateFormatter.format(startDate))
.append('\'');
} else if (token.endsWith("SINCE")) { } else if (token.endsWith("SINCE")) {
conditions.append("\"").append(searchAttribute).append("\" >= '") return session.gte(searchAttribute, dateFormatter.format(startDate));
.append(dateFormatter.format(startDate)) } else {
.append('\''); throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", dateToken);
} }
} }
protected boolean expunge(boolean silent) throws IOException { protected boolean expunge(boolean silent) throws IOException {
@ -1436,8 +1412,8 @@ public class ImapConnection extends AbstractConnection {
endUid = startUid; endUid = startUid;
startUid = swap; startUid = swap;
} }
} else if ("*".equals(currentRange)){ } else if ("*".equals(currentRange)) {
startUid = endUid = messages.get(messages.size()-1).getImapUid(); startUid = endUid = messages.get(messages.size() - 1).getImapUid();
} else { } else {
startUid = endUid = convertToLong(currentRange); startUid = endUid = convertToLong(currentRange);
} }
@ -1519,7 +1495,7 @@ public class ImapConnection extends AbstractConnection {
endUid = startUid; endUid = startUid;
startUid = swap; startUid = swap;
} }
} else if ("*".equals(currentRange)){ } else if ("*".equals(currentRange)) {
startUid = endUid = messages.size(); startUid = endUid = messages.size();
} else { } else {
startUid = endUid = convertToLong(currentRange); startUid = endUid = convertToLong(currentRange);