mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-14 03:32:22 -05:00
Caldav: implement time-range request
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1253 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
87aa40b7d1
commit
e4460e159f
@ -687,7 +687,7 @@ public class CaldavConnection extends AbstractConnection {
|
||||
appendEventsResponses(response, request, events);
|
||||
} else {
|
||||
// TODO: handle contacts ?
|
||||
events = session.getAllEvents(request.getFolderPath());
|
||||
events = session.searchEvents(request.getFolderPath(), request.timeRangeStart, request.timeRangeEnd);
|
||||
appendEventsResponses(response, request, events);
|
||||
}
|
||||
|
||||
@ -1201,6 +1201,8 @@ public class CaldavConnection extends AbstractConnection {
|
||||
protected final HashSet<String> properties = new HashSet<String>();
|
||||
protected HashSet<String> hrefs;
|
||||
protected boolean isMultiGet;
|
||||
protected String timeRangeStart;
|
||||
protected String timeRangeEnd;
|
||||
|
||||
protected CaldavRequest(String command, String path, Map<String, String> headers, String body) throws IOException {
|
||||
this.command = command;
|
||||
@ -1376,6 +1378,9 @@ public class CaldavConnection extends AbstractConnection {
|
||||
} else if ("calendar-multiget".equals(currentElement)
|
||||
|| "addressbook-multiget".equals(currentElement)) {
|
||||
isMultiGet = true;
|
||||
} else if ("time-range".equals(currentElement)) {
|
||||
timeRangeStart = streamReader.getAttributeValue(null, "start");
|
||||
timeRangeEnd = streamReader.getAttributeValue(null, "end");
|
||||
} else if (inProperties) {
|
||||
properties.add(currentElement);
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ public abstract class ExchangeSession {
|
||||
LOGGER.debug("Session " + this + " created");
|
||||
}
|
||||
|
||||
protected static String formatSearchDate(Date date) {
|
||||
public static String formatSearchDate(Date date) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS, Locale.ENGLISH);
|
||||
dateFormatter.setTimeZone(GMT_TIMEZONE);
|
||||
return dateFormatter.format(date);
|
||||
@ -603,7 +603,10 @@ public abstract class ExchangeSession {
|
||||
public abstract MessageList searchMessages(String folderName, Set<String> attributes, Condition condition) throws IOException;
|
||||
|
||||
protected enum Operator {
|
||||
Or, And, Not, IsEqualTo, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsNull, IsTrue, IsFalse,
|
||||
Or, And, Not, IsEqualTo,
|
||||
IsGreaterThan, IsGreaterThanOrEqualTo,
|
||||
IsLessThan, IsLowerThanOrEqualTo,
|
||||
IsNull, IsTrue, IsFalse,
|
||||
Like, StartsWith, Contains
|
||||
}
|
||||
|
||||
@ -796,6 +799,15 @@ public abstract class ExchangeSession {
|
||||
*/
|
||||
public abstract Condition lt(String attributeName, String value);
|
||||
|
||||
/**
|
||||
* Lower than or equals condition.
|
||||
*
|
||||
* @param attributeName logical Exchange attribute name
|
||||
* @param value attribute value
|
||||
* @return condition
|
||||
*/
|
||||
public abstract Condition lte(String attributeName, String value);
|
||||
|
||||
/**
|
||||
* Contains condition.
|
||||
*
|
||||
@ -2526,13 +2538,51 @@ public abstract class ExchangeSession {
|
||||
dateCondition = gt("dtstart", formatSearchDate(cal.getTime()));
|
||||
}
|
||||
|
||||
return searchEvents(folderPath, dateCondition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search events between start and end.
|
||||
*
|
||||
* @param folderPath Exchange folder path
|
||||
* @param timeRangeStart date range start in zulu format
|
||||
* @param timeRangeEnd date range start in zulu format
|
||||
* @return list of calendar events
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public List<Event> searchEvents(String folderPath, String timeRangeStart, String timeRangeEnd) throws IOException {
|
||||
try {
|
||||
SimpleDateFormat parser = getZuluDateFormat();
|
||||
ExchangeSession.MultiCondition andCondition = and();
|
||||
if (timeRangeStart != null) {
|
||||
andCondition.add(gte("dtstart", formatSearchDate(parser.parse(timeRangeStart))));
|
||||
}
|
||||
if (timeRangeEnd != null) {
|
||||
andCondition.add(lte("dtend", formatSearchDate(parser.parse(timeRangeEnd))));
|
||||
}
|
||||
andCondition.add(equals("instancetype", 0));
|
||||
return searchEvents(folderPath, ITEM_PROPERTIES, andCondition);
|
||||
} catch (ParseException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search calendar events in provided folder.
|
||||
*
|
||||
* @param folderPath Exchange folder path
|
||||
* @param dateCondition date filter
|
||||
* @return list of calendar events
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public List<Event> searchEvents(String folderPath, Condition dateCondition) throws IOException {
|
||||
|
||||
Condition privateCondition = null;
|
||||
if (isSharedFolder(folderPath)) {
|
||||
LOGGER.debug("Shared or public calendar: exclude private events");
|
||||
privateCondition = equals("sensitivity", 0);
|
||||
}
|
||||
|
||||
|
||||
// instancetype 0 single appointment / 1 master recurring appointment
|
||||
return searchEvents(folderPath, ITEM_PROPERTIES,
|
||||
and(or(isNull("instancetype"),
|
||||
equals("instancetype", 1),
|
||||
@ -2541,7 +2591,6 @@ public abstract class ExchangeSession {
|
||||
privateCondition));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search calendar events or messages in provided folder matching the search query.
|
||||
*
|
||||
@ -2551,7 +2600,7 @@ public abstract class ExchangeSession {
|
||||
* @return list of calendar messages as Event objects
|
||||
* @throws IOException on error
|
||||
*/
|
||||
protected abstract List<Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException;
|
||||
public abstract List<Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException;
|
||||
|
||||
/**
|
||||
* convert vcf extension to EML.
|
||||
|
@ -398,6 +398,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
operatorMap.put(Operator.IsEqualTo, " = ");
|
||||
operatorMap.put(Operator.IsGreaterThanOrEqualTo, " >= ");
|
||||
operatorMap.put(Operator.IsGreaterThan, " > ");
|
||||
operatorMap.put(Operator.IsLowerThanOrEqualTo, " <= ");
|
||||
operatorMap.put(Operator.IsLessThan, " < ");
|
||||
operatorMap.put(Operator.Like, " like ");
|
||||
operatorMap.put(Operator.IsNull, " is null");
|
||||
@ -516,6 +517,11 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
return new AttributeCondition(attributeName, Operator.IsGreaterThanOrEqualTo, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition lte(String attributeName, String value) {
|
||||
return new AttributeCondition(attributeName, Operator.IsLowerThanOrEqualTo, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition lt(String attributeName, String value) {
|
||||
return new AttributeCondition(attributeName, Operator.IsLessThan, value);
|
||||
@ -1141,7 +1147,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ExchangeSession.Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
|
||||
public List<ExchangeSession.Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
|
||||
List<ExchangeSession.Event> events = new ArrayList<ExchangeSession.Event>();
|
||||
MultiStatusResponse[] responses = searchItems(folderPath, attributes, and(isFalse("isfolder"), isFalse("ishidden"), condition), FolderQueryTraversal.Shallow);
|
||||
for (MultiStatusResponse response : responses) {
|
||||
|
@ -166,6 +166,7 @@ public class Field {
|
||||
createField(SCHEMAS_EXCHANGE, "permanenturl");
|
||||
createField(URN_SCHEMAS_CALENDAR, "instancetype"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:instancetype/Integer
|
||||
createField(URN_SCHEMAS_CALENDAR, "dtstart"); // 0x10C3 SystemTime
|
||||
createField(URN_SCHEMAS_CALENDAR, "dtend"); // 0x10C4 SystemTime
|
||||
createField(SCHEMAS_EXCHANGE, "sensitivity"); // PR_SENSITIVITY 0x0036 Integer
|
||||
createField(URN_SCHEMAS_CALENDAR, "timezoneid"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:timezoneid/Integer
|
||||
createField("processed", 0x65e8, PropertyType.Boolean);// PR_MESSAGE_PROCESSED
|
||||
|
@ -439,6 +439,11 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
return new AttributeCondition(attributeName, Operator.IsGreaterThanOrEqualTo, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition lte(String attributeName, String value) {
|
||||
return new AttributeCondition(attributeName, Operator.IsLowerThanOrEqualTo, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition lt(String attributeName, String value) {
|
||||
return new AttributeCondition(attributeName, Operator.IsLessThan, value);
|
||||
@ -906,7 +911,7 @@ public class EwsExchangeSession extends ExchangeSession {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ExchangeSession.Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
|
||||
public List<ExchangeSession.Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException {
|
||||
List<ExchangeSession.Event> events = new ArrayList<ExchangeSession.Event>();
|
||||
List<EWSMethod.Item> responses = searchItems(folderPath, attributes,
|
||||
condition,
|
||||
|
@ -46,6 +46,7 @@ public class Field {
|
||||
FIELD_MAP.put("permanenturl", new ExtendedFieldURI(0x670E, ExtendedFieldURI.PropertyType.String)); //PR_FLAT_URL_NAME
|
||||
FIELD_MAP.put("instancetype", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.PublicStrings, "urn:schemas:calendar:instancetype"));
|
||||
FIELD_MAP.put("dtstart", new ExtendedFieldURI(0x10C3, ExtendedFieldURI.PropertyType.SystemTime));
|
||||
FIELD_MAP.put("dtstart", new ExtendedFieldURI(0x10C4, ExtendedFieldURI.PropertyType.SystemTime));
|
||||
|
||||
FIELD_MAP.put("mimeContent", new UnindexedFieldURI("item:MimeContent"));
|
||||
|
||||
|
164
src/test/davmail/caldav/TestCaldav.java
Normal file
164
src/test/davmail/caldav/TestCaldav.java
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.caldav;
|
||||
|
||||
import davmail.AbstractDavMailTestCase;
|
||||
import davmail.DavGateway;
|
||||
import davmail.Settings;
|
||||
import davmail.exchange.ExchangeSession;
|
||||
import davmail.exchange.ExchangeSessionFactory;
|
||||
import org.apache.commons.httpclient.*;
|
||||
import org.apache.commons.httpclient.auth.AuthScope;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.RequestEntity;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.MultiStatus;
|
||||
import org.apache.jackrabbit.webdav.MultiStatusResponse;
|
||||
import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Test Caldav listener.
|
||||
*/
|
||||
public class TestCaldav extends AbstractDavMailTestCase {
|
||||
|
||||
class SearchReportMethod extends DavMethodBase {
|
||||
SearchReportMethod(String path, String stringContent) throws UnsupportedEncodingException {
|
||||
this(path, stringContent.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
SearchReportMethod(String path, final byte[] content) {
|
||||
super(path);
|
||||
setRequestEntity(new RequestEntity() {
|
||||
|
||||
public boolean isRepeatable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeRequest(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(content);
|
||||
}
|
||||
|
||||
public long getContentLength() {
|
||||
return content.length;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return "text/xml;charset=UTF-8";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "REPORT";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSuccess(int statusCode) {
|
||||
return statusCode == HttpStatus.SC_MULTI_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient httpClient;
|
||||
|
||||
@Override
|
||||
public void setUp() throws IOException {
|
||||
super.setUp();
|
||||
if (httpClient == null) {
|
||||
// start gateway
|
||||
DavGateway.start();
|
||||
httpClient = new HttpClient();
|
||||
HostConfiguration hostConfig = httpClient.getHostConfiguration();
|
||||
URI httpURI = new URI("http://localhost:" + Settings.getProperty("davmail.caldavPort"), true);
|
||||
hostConfig.setHost(httpURI);
|
||||
AuthScope authScope = new AuthScope(null, -1);
|
||||
httpClient.getState().setCredentials(authScope, new NTCredentials(Settings.getProperty("davmail.username"), Settings.getProperty("davmail.password"), "", ""));
|
||||
}
|
||||
if (session == null) {
|
||||
session = ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"), Settings.getProperty("davmail.password"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetRoot() throws IOException {
|
||||
GetMethod method = new GetMethod("/");
|
||||
httpClient.executeMethod(method);
|
||||
assertEquals(HttpStatus.SC_OK, method.getStatusCode());
|
||||
}
|
||||
|
||||
public void testGetUserRoot() throws IOException {
|
||||
GetMethod method = new GetMethod("/users/" + session.getEmail() + '/');
|
||||
httpClient.executeMethod(method);
|
||||
assertEquals(HttpStatus.SC_OK, method.getStatusCode());
|
||||
}
|
||||
|
||||
public void testGetCalendar() throws IOException {
|
||||
GetMethod method = new GetMethod("/users/" + session.getEmail() + "/calendar/");
|
||||
httpClient.executeMethod(method);
|
||||
assertEquals(HttpStatus.SC_OK, method.getStatusCode());
|
||||
}
|
||||
|
||||
public void testReportCalendar() throws IOException, DavException {
|
||||
SimpleDateFormat formatter = ExchangeSession.getZuluDateFormat();
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Date end = cal.getTime();
|
||||
cal.add(Calendar.MONTH, -1);
|
||||
Date start = cal.getTime();
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
buffer.append("<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\" xmlns:D=\"DAV:\">");
|
||||
buffer.append("<D:prop>");
|
||||
buffer.append("<C:calendar-data/>");
|
||||
buffer.append("</D:prop>");
|
||||
buffer.append("<C:comp-filter name=\"VCALENDAR\">");
|
||||
buffer.append("<C:comp-filter name=\"VEVENT\">");
|
||||
buffer.append("<C:time-range start=\"").append(formatter.format(start)).append("\" end=\"").append(formatter.format(end)).append("\"/>");
|
||||
buffer.append("</C:comp-filter>");
|
||||
buffer.append("</C:comp-filter>");
|
||||
buffer.append("<C:filter>");
|
||||
buffer.append("</C:filter>");
|
||||
buffer.append("</C:calendar-query>");
|
||||
SearchReportMethod method = new SearchReportMethod("/users/" + session.getEmail() + "/calendar/",buffer.toString());
|
||||
httpClient.executeMethod(method);
|
||||
assertEquals(HttpStatus.SC_MULTI_STATUS, method.getStatusCode());
|
||||
MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
|
||||
MultiStatusResponse[] responses = multiStatus.getResponses();
|
||||
|
||||
Set<String> ITEM_PROPERTIES = new HashSet<String>();
|
||||
ITEM_PROPERTIES.add("instancetype");
|
||||
List<ExchangeSession.Event> events = session.searchEvents("/users/" + session.getEmail() + "/calendar/", ITEM_PROPERTIES,
|
||||
session.and(
|
||||
session.and(
|
||||
session.gt("dtstart", session.formatSearchDate(start)),
|
||||
session.lt("dtend", session.formatSearchDate(end))
|
||||
)
|
||||
, session.or(session.equals("instancetype", 1), session.equals("instancetype", 0))
|
||||
)
|
||||
);
|
||||
|
||||
assertEquals(events.size(), responses.length);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user