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:
parent
aab4709956
commit
51936619e3
@ -109,6 +109,7 @@ public abstract class ExchangeSession {
|
||||
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 URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
|
||||
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));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@ -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.
|
||||
* 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) {
|
||||
ArrayList<DavProperty> list = new ArrayList<DavProperty>();
|
||||
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).
|
||||
*
|
||||
@ -771,24 +724,46 @@ public abstract class ExchangeSession {
|
||||
* @throws IOException on error
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param folderName Exchange folder name
|
||||
* @param conditions conditions string in Exchange SQL syntax
|
||||
* @param condition search filter
|
||||
* @return message list
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public MessageList searchMessages(String folderName, String conditions) throws IOException {
|
||||
return searchMessages(folderName, "\"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"" +
|
||||
" ,\"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);
|
||||
public MessageList searchMessages(String folderName, Condition condition) throws IOException {
|
||||
return searchMessages(folderName, IMAP_MESSAGE_ATTRIBUTES, condition);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -800,41 +775,17 @@ public abstract class ExchangeSession {
|
||||
* @return message list
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public MessageList searchMessages(String folderName, String attributes, String conditions) 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;
|
||||
}
|
||||
public abstract MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
protected abstract static class AttributeCondition extends Condition {
|
||||
public abstract static class AttributeCondition extends Condition {
|
||||
protected String attributeName;
|
||||
protected Operator operator;
|
||||
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 Condition[] conditions;
|
||||
protected List<Condition> conditions;
|
||||
|
||||
protected MultiCondition(Operator operator, Condition... conditions) {
|
||||
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 NotCondition(Condition condition) {
|
||||
@ -864,24 +821,40 @@ public abstract class ExchangeSession {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract static class IsNullCondition extends Condition {
|
||||
protected String attributeName;
|
||||
public abstract static class MonoCondition extends Condition {
|
||||
protected String attributeName;
|
||||
protected Operator operator;
|
||||
|
||||
protected IsNullCondition(String attributeName) {
|
||||
protected MonoCondition(String attributeName, Operator operator) {
|
||||
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 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 isTrue(String attributeName);
|
||||
|
||||
public abstract Condition isFalse(String attributeName);
|
||||
|
||||
/**
|
||||
* Search mail and generic folders under given folder.
|
||||
* Exclude calendar and contacts folders
|
||||
@ -1288,19 +1261,19 @@ public abstract class ExchangeSession {
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public void loadMessages() throws IOException {
|
||||
messages = ExchangeSession.this.searchMessages(folderPath, "");
|
||||
messages = ExchangeSession.this.searchMessages(folderPath, null);
|
||||
fixUids(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search messages in folder matching query.
|
||||
*
|
||||
* @param query search query
|
||||
* @param condition search query
|
||||
* @return message list
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public MessageList searchMessages(String query) throws IOException {
|
||||
MessageList localMessages = ExchangeSession.this.searchMessages(folderName, query);
|
||||
public MessageList searchMessages(Condition condition) throws IOException {
|
||||
MessageList localMessages = ExchangeSession.this.searchMessages(folderName, condition);
|
||||
fixUids(localMessages);
|
||||
return localMessages;
|
||||
}
|
||||
@ -1406,23 +1379,23 @@ public abstract class ExchangeSession {
|
||||
/**
|
||||
* enclosing message list
|
||||
*/
|
||||
protected MessageList messageList;
|
||||
public MessageList messageList;
|
||||
/**
|
||||
* Message url.
|
||||
*/
|
||||
protected String messageUrl;
|
||||
public String messageUrl;
|
||||
/**
|
||||
* Message permanent url (does not change on message move).
|
||||
*/
|
||||
protected String permanentUrl;
|
||||
public String permanentUrl;
|
||||
/**
|
||||
* Message uid.
|
||||
*/
|
||||
protected String uid;
|
||||
public String uid;
|
||||
/**
|
||||
* Message IMAP uid, unique in folder (x0e230003).
|
||||
*/
|
||||
protected long imapUid;
|
||||
public long imapUid;
|
||||
/**
|
||||
* MAPI message size.
|
||||
*/
|
||||
@ -3011,7 +2984,7 @@ public abstract class ExchangeSession {
|
||||
// failover for Exchange 2007 plus encoding issue
|
||||
String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
|
||||
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()) {
|
||||
item = getItem(messages.get(0).getPermanentUrl());
|
||||
} else {
|
||||
|
@ -18,10 +18,12 @@
|
||||
*/
|
||||
package davmail.exchange.dav;
|
||||
|
||||
import davmail.BundleMessage;
|
||||
import davmail.exception.DavMailAuthenticationException;
|
||||
import davmail.exception.DavMailException;
|
||||
import davmail.exchange.ExchangeSession;
|
||||
import davmail.http.DavGatewayHttpClientFacade;
|
||||
import davmail.ui.tray.DavGatewayTray;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
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.jackrabbit.webdav.MultiStatusResponse;
|
||||
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.xml.Namespace;
|
||||
|
||||
@ -36,16 +41,25 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Webdav Exchange adapter.
|
||||
* Compatible with Exchange 2003 and 2007 with webdav available.
|
||||
*/
|
||||
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
|
||||
@ -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 {
|
||||
// Retrieve well known URLs
|
||||
MultiStatusResponse[] responses;
|
||||
@ -119,14 +142,14 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
throw new DavMailException("EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER", mailPath);
|
||||
}
|
||||
DavPropertySet properties = responses[0].getProperties(HttpStatus.SC_OK);
|
||||
inboxUrl = getURIPropertyIfExists(properties, "inbox", URN_SCHEMAS_HTTPMAIL);
|
||||
deleteditemsUrl = getURIPropertyIfExists(properties, "deleteditems", URN_SCHEMAS_HTTPMAIL);
|
||||
sentitemsUrl = getURIPropertyIfExists(properties, "sentitems", URN_SCHEMAS_HTTPMAIL);
|
||||
sendmsgUrl = getURIPropertyIfExists(properties, "sendmsg", URN_SCHEMAS_HTTPMAIL);
|
||||
draftsUrl = getURIPropertyIfExists(properties, "drafts", URN_SCHEMAS_HTTPMAIL);
|
||||
calendarUrl = getURIPropertyIfExists(properties, "calendar", URN_SCHEMAS_HTTPMAIL);
|
||||
contactsUrl = getURIPropertyIfExists(properties, "contacts", URN_SCHEMAS_HTTPMAIL);
|
||||
outboxUrl = getURIPropertyIfExists(properties, "outbox", URN_SCHEMAS_HTTPMAIL);
|
||||
inboxUrl = getURIPropertyIfExists(properties, "inbox");
|
||||
deleteditemsUrl = getURIPropertyIfExists(properties, "deleteditems");
|
||||
sentitemsUrl = getURIPropertyIfExists(properties, "sentitems");
|
||||
sendmsgUrl = getURIPropertyIfExists(properties, "sendmsg");
|
||||
draftsUrl = getURIPropertyIfExists(properties, "drafts");
|
||||
calendarUrl = getURIPropertyIfExists(properties, "calendar");
|
||||
contactsUrl = getURIPropertyIfExists(properties, "contacts");
|
||||
outboxUrl = getURIPropertyIfExists(properties, "outbox");
|
||||
// junk folder not available over webdav
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
@ -228,37 +250,71 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
|
||||
@Override
|
||||
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('\'').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 IsNullCondition(String attributeName) {
|
||||
super(attributeName);
|
||||
protected static class HeaderCondition extends AttributeCondition {
|
||||
|
||||
protected HeaderCondition(String attributeName, Operator operator, String value) {
|
||||
super(attributeName, operator, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
buffer.append('"').append(attributeMap.get(attributeName)).append('"');
|
||||
buffer.append(" is null");
|
||||
buffer.append('"').append(Field.getHeader(attributeName)).append('"');
|
||||
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
|
||||
public Condition and(Condition... condition) {
|
||||
public MultiCondition and(Condition... condition) {
|
||||
return new MultiCondition(Operator.And, condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition or(Condition... condition) {
|
||||
public MultiCondition or(Condition... condition) {
|
||||
return new MultiCondition(Operator.Or, condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition not(Condition condition) {
|
||||
return new NotCondition(condition);
|
||||
if (condition == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new NotCondition(condition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -266,9 +322,44 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -348,7 +439,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
"\"urn:schemas:httpmail:unreadcount\" FROM Scope('").append(mode).append(" TRAVERSAL OF \"").append(getFolderPath(folderName)).append("\"')\n" +
|
||||
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = True \n");
|
||||
if (condition != null) {
|
||||
searchRequest.append(" AND ");
|
||||
searchRequest.append(" AND ");
|
||||
condition.appendTo(searchRequest);
|
||||
}
|
||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
|
||||
@ -364,4 +455,98 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
188
src/java/davmail/exchange/dav/Field.java
Normal file
188
src/java/davmail/exchange/dav/Field.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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 MultiCondition(Operator operator, Condition... condition) {
|
||||
super(operator, condition);
|
||||
@ -111,9 +117,7 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
buffer.append("<t:Not>");
|
||||
|
||||
condition.appendTo(buffer);
|
||||
|
||||
buffer.append("</t:Not>");
|
||||
}
|
||||
}
|
||||
@ -122,6 +126,7 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
|
||||
static {
|
||||
attributeMap.put("folderclass", ExtendedFieldURI.PR_CONTAINER_CLASS);
|
||||
attributeMap.put("read", ExtendedFieldURI.PR_READ);
|
||||
}
|
||||
|
||||
protected static class AttributeCondition extends ExchangeSession.AttributeCondition implements SearchExpression {
|
||||
@ -129,10 +134,18 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
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
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
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(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) {
|
||||
super(attributeName);
|
||||
this.attributeName = attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,12 +184,12 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition and(Condition... condition) {
|
||||
public MultiCondition and(Condition... condition) {
|
||||
return new MultiCondition(Operator.And, condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition or(Condition... condition) {
|
||||
public MultiCondition or(Condition... condition) {
|
||||
return new MultiCondition(Operator.Or, condition);
|
||||
}
|
||||
|
||||
@ -175,11 +203,46 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
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
|
||||
public Condition isNull(String 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) {
|
||||
Folder folder = new Folder();
|
||||
folder.folderId = new FolderId(item.get("FolderId"));
|
||||
|
@ -31,22 +31,34 @@ public class ExtendedFieldURI implements FieldURI {
|
||||
ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray
|
||||
}
|
||||
|
||||
protected static enum DistinguishedPropertySetType {
|
||||
Meeting, Appointment, Common, PublicStrings, Address, InternetHeaders, CalendarAssistant, UnifiedMessaging, Task
|
||||
}
|
||||
|
||||
|
||||
protected String propertyTag;
|
||||
protected DistinguishedPropertySetType distinguishedPropertySetId;
|
||||
protected String propertySetId;
|
||||
protected String propertyName;
|
||||
protected int propertyId;
|
||||
protected final PropertyType propertyType;
|
||||
protected PropertyType propertyType;
|
||||
|
||||
public ExtendedFieldURI(int intPropertyTag, PropertyType propertyType) {
|
||||
this.propertyTag = "0x"+Integer.toHexString(intPropertyTag);
|
||||
this.propertyTag = "0x" + Integer.toHexString(intPropertyTag);
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
public ExtendedFieldURI(String propertySetId, int propertyId, PropertyType propertyType) {
|
||||
this.propertySetId = propertySetId;
|
||||
public ExtendedFieldURI(DistinguishedPropertySetType distinguishedPropertySetId, int propertyId, PropertyType propertyType) {
|
||||
this.distinguishedPropertySetId = distinguishedPropertySetId;
|
||||
this.propertyId = propertyId;
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
public ExtendedFieldURI(DistinguishedPropertySetType distinguishedPropertySetId, String propertyName) {
|
||||
this.distinguishedPropertySetId = distinguishedPropertySetId;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public String getPropertyTag() {
|
||||
return propertyTag;
|
||||
}
|
||||
@ -56,13 +68,21 @@ public class ExtendedFieldURI implements FieldURI {
|
||||
if (propertyTag != null) {
|
||||
buffer.append("PropertyTag=\"").append(propertyTag).append("\" ");
|
||||
}
|
||||
if (distinguishedPropertySetId != null) {
|
||||
buffer.append("DistinguishedPropertySetId=\"").append(distinguishedPropertySetId).append("\" ");
|
||||
}
|
||||
if (propertySetId != null) {
|
||||
buffer.append("PropertySetId=\"").append(propertySetId).append("\" ");
|
||||
}
|
||||
if (propertyName != null) {
|
||||
buffer.append("propertyName=\"").append(propertyName).append("\" ");
|
||||
}
|
||||
if (propertyId != 0) {
|
||||
buffer.append("PropertyId=\"").append(String.valueOf(propertyId)).append("\" ");
|
||||
}
|
||||
buffer.append("PropertyType=\"").append(propertyType.toString()).append("\"/>");
|
||||
if (propertyType != null) {
|
||||
buffer.append("PropertyType=\"").append(propertyType.toString()).append("\"/>");
|
||||
}
|
||||
}
|
||||
|
||||
public static final ExtendedFieldURI PR_INSTANCE_KEY = new ExtendedFieldURI(0xff6, PropertyType.Binary);
|
||||
@ -77,6 +97,8 @@ public class ExtendedFieldURI implements FieldURI {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -730,45 +730,36 @@ public class ImapConnection extends AbstractConnection {
|
||||
sendClient(commandId + " OK STORE completed");
|
||||
}
|
||||
|
||||
protected void buildConditions(SearchConditions conditions, IMAPTokenizer tokens) throws IOException {
|
||||
boolean or = false;
|
||||
protected ExchangeSession.Condition buildConditions(SearchConditions conditions, IMAPTokenizer tokens) throws IOException {
|
||||
ExchangeSession.MultiCondition condition = null;
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String token = tokens.nextQuotedToken().toUpperCase();
|
||||
if (token.startsWith("(") && token.endsWith(")")) {
|
||||
// quoted search param
|
||||
buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1)));
|
||||
} else if ("OR".equals(token)) {
|
||||
or = true;
|
||||
} else if (token.startsWith("OR ")) {
|
||||
or = true;
|
||||
appendOrSearchParams(token, conditions);
|
||||
} else {
|
||||
String operator;
|
||||
if (conditions.query.length() == 5) {
|
||||
operator = "";
|
||||
} else if (or) {
|
||||
operator = " OR ";
|
||||
} else {
|
||||
operator = " AND ";
|
||||
if (condition == null) {
|
||||
condition = session.and();
|
||||
}
|
||||
appendSearchParam(operator, tokens, token, conditions);
|
||||
condition.append(buildConditions(conditions, new IMAPTokenizer(token.substring(1, token.length() - 1))));
|
||||
} else if ("OR".equals(token)) {
|
||||
condition = session.or();
|
||||
} else if (token.startsWith("OR ")) {
|
||||
condition = appendOrSearchParams(token, conditions);
|
||||
} else {
|
||||
if (condition == null) {
|
||||
condition = session.and();
|
||||
}
|
||||
condition.append(appendSearchParam(tokens, token, conditions));
|
||||
}
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
|
||||
protected List<Long> handleSearch(IMAPTokenizer tokens) throws IOException {
|
||||
List<Long> uidList = new ArrayList<Long>();
|
||||
SearchConditions conditions = new SearchConditions();
|
||||
conditions.append("AND (");
|
||||
buildConditions(conditions, tokens);
|
||||
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);
|
||||
ExchangeSession.Condition condition = buildConditions(conditions, tokens);
|
||||
ExchangeSession.MessageList localMessages = currentFolder.searchMessages(condition);
|
||||
Iterator<ExchangeSession.Message> iterator;
|
||||
if (conditions.uidRange != null) {
|
||||
iterator = new UIDRangeIterator(localMessages, conditions.uidRange);
|
||||
@ -988,63 +979,60 @@ public class ImapConnection extends AbstractConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* client side search conditions
|
||||
*/
|
||||
static final class SearchConditions {
|
||||
Boolean flagged;
|
||||
Boolean answered;
|
||||
Boolean deleted;
|
||||
String indexRange;
|
||||
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);
|
||||
innerTokens.nextToken();
|
||||
boolean first = true;
|
||||
while (innerTokens.hasMoreTokens()) {
|
||||
String innerToken = innerTokens.nextToken();
|
||||
String operator = "";
|
||||
if (!first) {
|
||||
operator = " OR ";
|
||||
}
|
||||
first = false;
|
||||
appendSearchParam(operator, innerTokens, innerToken, conditions);
|
||||
orCondition.append(appendSearchParam(innerTokens, innerToken, conditions));
|
||||
}
|
||||
|
||||
return orCondition;
|
||||
}
|
||||
|
||||
protected void appendSearchParam(String operator, StringTokenizer tokens, String token, SearchConditions conditions) throws IOException {
|
||||
protected ExchangeSession.Condition appendSearchParam(StringTokenizer tokens, String token, SearchConditions conditions) throws IOException {
|
||||
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 ")) {
|
||||
appendOrSearchParams(token, conditions);
|
||||
return appendOrSearchParams(token, conditions);
|
||||
} 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)) {
|
||||
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)) {
|
||||
conditions.append(operator).append("\"urn:schemas:mailheader:from\" LIKE '%").append(tokens.nextToken()).append("%'");
|
||||
return session.like("from", tokens.nextToken());
|
||||
} 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)) {
|
||||
conditions.append(operator).append("\"urn:schemas:mailheader:cc\" LIKE '%").append(tokens.nextToken()).append("%'");
|
||||
return session.like("cc", tokens.nextToken());
|
||||
} else if ("LARGER".equals(token)) {
|
||||
conditions.append(operator).append("\"http://schemas.microsoft.com/mapi/proptag/x0e080003\" >= ").append(Long.parseLong(tokens.nextToken())).append("");
|
||||
return session.gte("messageSize", tokens.nextToken());
|
||||
} 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)) {
|
||||
conditions.append(operator);
|
||||
appendDateSearchParam(tokens, token, conditions);
|
||||
return appendDateSearchParam(tokens, 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)) {
|
||||
conditions.append(operator).append("\"urn:schemas:httpmail:read\" = False");
|
||||
return session.isFalse("read");
|
||||
} else if ("DELETED".equals(token)) {
|
||||
conditions.deleted = !operator.endsWith(" NOT ");
|
||||
conditions.deleted = Boolean.TRUE;
|
||||
} else if ("UNDELETED".equals(token) || "NOT DELETED".equals(token)) {
|
||||
conditions.deleted = Boolean.FALSE;
|
||||
} else if ("FLAGGED".equals(token)) {
|
||||
@ -1061,15 +1049,7 @@ public class ImapConnection extends AbstractConnection {
|
||||
if ("message-id".equals(headerName) && !value.startsWith("<")) {
|
||||
value = '<' + value + '>';
|
||||
}
|
||||
String namespace;
|
||||
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('\'');
|
||||
return session.headerEquals(headerName, value);
|
||||
} else if ("UID".equals(token)) {
|
||||
String range = tokens.nextToken();
|
||||
if ("1:*".equals(range)) {
|
||||
@ -1085,9 +1065,11 @@ public class ImapConnection extends AbstractConnection {
|
||||
} else {
|
||||
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 endDate;
|
||||
SimpleDateFormat parser = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);
|
||||
@ -1106,27 +1088,21 @@ public class ImapConnection extends AbstractConnection {
|
||||
}
|
||||
String searchAttribute;
|
||||
if (token.startsWith("SENT")) {
|
||||
searchAttribute = "urn:schemas:httpmail:date";
|
||||
searchAttribute = "date";
|
||||
} else {
|
||||
searchAttribute = "DAV:getlastmodified";
|
||||
searchAttribute = "lastmodified";
|
||||
}
|
||||
|
||||
if (token.endsWith("ON")) {
|
||||
conditions.append("(\"").append(searchAttribute).append("\" > '")
|
||||
.append(dateFormatter.format(startDate))
|
||||
.append("' AND \"").append(searchAttribute).append("\" < '")
|
||||
.append(dateFormatter.format(endDate))
|
||||
.append("')");
|
||||
return session.and(session.gt(searchAttribute, dateFormatter.format(startDate)),
|
||||
session.lt(searchAttribute, dateFormatter.format(endDate)));
|
||||
} else if (token.endsWith("BEFORE")) {
|
||||
conditions.append("\"").append(searchAttribute).append("\" < '")
|
||||
.append(dateFormatter.format(startDate))
|
||||
.append('\'');
|
||||
return session.lt(searchAttribute, dateFormatter.format(startDate));
|
||||
} else if (token.endsWith("SINCE")) {
|
||||
conditions.append("\"").append(searchAttribute).append("\" >= '")
|
||||
.append(dateFormatter.format(startDate))
|
||||
.append('\'');
|
||||
return session.gte(searchAttribute, dateFormatter.format(startDate));
|
||||
} else {
|
||||
throw new DavMailException("EXCEPTION_INVALID_SEARCH_PARAMETERS", dateToken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean expunge(boolean silent) throws IOException {
|
||||
@ -1436,8 +1412,8 @@ public class ImapConnection extends AbstractConnection {
|
||||
endUid = startUid;
|
||||
startUid = swap;
|
||||
}
|
||||
} else if ("*".equals(currentRange)){
|
||||
startUid = endUid = messages.get(messages.size()-1).getImapUid();
|
||||
} else if ("*".equals(currentRange)) {
|
||||
startUid = endUid = messages.get(messages.size() - 1).getImapUid();
|
||||
} else {
|
||||
startUid = endUid = convertToLong(currentRange);
|
||||
}
|
||||
@ -1519,7 +1495,7 @@ public class ImapConnection extends AbstractConnection {
|
||||
endUid = startUid;
|
||||
startUid = swap;
|
||||
}
|
||||
} else if ("*".equals(currentRange)){
|
||||
} else if ("*".equals(currentRange)) {
|
||||
startUid = endUid = messages.size();
|
||||
} else {
|
||||
startUid = endUid = convertToLong(currentRange);
|
||||
|
Loading…
Reference in New Issue
Block a user