diff --git a/app/src/main/java/at/bitfire/davdroid/resource/CalDavCalendar.java b/app/src/main/java/at/bitfire/davdroid/resource/CalDavCalendar.java index 34d8424a..be54ad18 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/CalDavCalendar.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/CalDavCalendar.java @@ -8,15 +8,30 @@ package at.bitfire.davdroid.resource; import android.accounts.Account; +import android.util.Log; import org.apache.http.impl.client.CloseableHttpClient; +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; +import java.io.StringWriter; import java.net.URISyntaxException; import at.bitfire.davdroid.syncadapter.AccountSettings; +import at.bitfire.davdroid.webdav.DavCalendarQuery; +import at.bitfire.davdroid.webdav.DavCompFilter; +import at.bitfire.davdroid.webdav.DavFilter; import at.bitfire.davdroid.webdav.DavMultiget; +import at.bitfire.davdroid.webdav.DavProp; + +public class CalDavCalendar extends RemoteCollection { + private final static String TAG = "davdroid.CalDAVCalendar"; + + public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException { + super(httpClient, baseURL, user, password, preemptiveAuth); + + } -public class CalDavCalendar extends RemoteCollection { @Override protected String memberAcceptedMimeTypes() @@ -35,7 +50,33 @@ public class CalDavCalendar extends RemoteCollection { } - public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException { - super(httpClient, baseURL, user, password, preemptiveAuth); + @Override + public String getMemberETagsQuery() { + DavCalendarQuery query = new DavCalendarQuery(); + + // prop + DavProp prop = new DavProp(); + prop.setGetetag(new DavProp.GetETag()); + query.setProp(prop); + + // filter + DavFilter filter = new DavFilter(); + query.setFilter(filter); + + DavCompFilter compFilter = new DavCompFilter("VCALENDAR"); + filter.setCompFilter(compFilter); + + compFilter.setCompFilter(new DavCompFilter("VEVENT")); + + Serializer serializer = new Persister(); + StringWriter writer = new StringWriter(); + try { + serializer.write(query, writer); + } catch (Exception e) { + Log.e(TAG, "Couldn't prepare REPORT query", e); + return null; + } + + return writer.toString(); } } diff --git a/app/src/main/java/at/bitfire/davdroid/resource/RemoteCollection.java b/app/src/main/java/at/bitfire/davdroid/resource/RemoteCollection.java index 8ddf77c6..04e275e3 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/RemoteCollection.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/RemoteCollection.java @@ -26,7 +26,9 @@ import java.util.List; import at.bitfire.davdroid.URIUtils; import at.bitfire.davdroid.syncadapter.AccountSettings; +import at.bitfire.davdroid.webdav.DavCalendarQuery; import at.bitfire.davdroid.webdav.DavException; +import at.bitfire.davdroid.webdav.DavFilter; import at.bitfire.davdroid.webdav.DavMultiget; import at.bitfire.davdroid.webdav.DavNoContentException; import at.bitfire.davdroid.webdav.HttpException; @@ -36,6 +38,7 @@ import at.bitfire.davdroid.webdav.WebDavResource.PutMode; import ezvcard.io.text.VCardParseException; import lombok.Cleanup; import lombok.Getter; +import lombok.Setter; /** * Represents a remotely stored synchronizable collection (collection as in @@ -75,8 +78,28 @@ public abstract class RemoteCollection { return collection.getCTag(); } + + /** + * Returns a body for the REPORT request that queries all members of the collection + * that should be considered. Allows collections to implement remote filters. + * @return body for REPORT request or null if PROPFIND shall be used + */ + public String getMemberETagsQuery() { + return null; + } + + /** + * Gets the ETags of the resources in a collection. If davQuery is set, it's required to be a + * complete addressbook-query or calendar-query XML and will cause getMemberETags() to use REPORT + * instead of PROPFIND. + * @return array of Resources where only the resource names and ETags are set + */ public Resource[] getMemberETags() throws URISyntaxException, IOException, DavException, HttpException { - collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG); + String query = getMemberETagsQuery(); + if (query != null) + collection.report(query); + else + collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG); List resources = new LinkedList(); if (collection.getMembers() != null) @@ -84,13 +107,15 @@ public abstract class RemoteCollection { resources.add(newResourceSkeleton(member.getName(), member.getETag())); return resources.toArray(new Resource[0]); + } + @SuppressWarnings("unchecked") public Resource[] multiGet(Resource[] resources) throws URISyntaxException, IOException, DavException, HttpException { try { if (resources.length == 1) - return (T[]) new Resource[]{get(resources[0])}; + return (T[]) new Resource[] { get(resources[0]) }; Log.i(TAG, "Multi-getting " + resources.length + " remote resource(s)"); diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java index 0d6f7ecb..11e37a6d 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java @@ -27,8 +27,8 @@ import at.bitfire.davdroid.resource.RemoteCollection; public class CalendarsSyncAdapterService extends Service { private static SyncAdapter syncAdapter; - - + + @Override public void onCreate() { if (syncAdapter == null) @@ -48,7 +48,7 @@ public class CalendarsSyncAdapterService extends Service { private static class SyncAdapter extends DavSyncAdapter { - private final static String TAG = "davdroid.CalendarsSyncAdapter"; + private final static String TAG = "davdroid.CalDAVSync"; private SyncAdapter(Context context) { diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java index 53ab8574..21d08230 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java @@ -47,7 +47,7 @@ public class ContactsSyncAdapterService extends Service { private static class ContactsSyncAdapter extends DavSyncAdapter { - private final static String TAG = "davdroid.ContactsSyncAdapter"; + private final static String TAG = "davdroid.CardDAVSync"; private ContactsSyncAdapter(Context context) { diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/DavCalendarQuery.java b/app/src/main/java/at/bitfire/davdroid/webdav/DavCalendarQuery.java new file mode 100644 index 00000000..ded60ad7 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/webdav/DavCalendarQuery.java @@ -0,0 +1,23 @@ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.NamespaceList; +import org.simpleframework.xml.Root; + +import lombok.Getter; +import lombok.Setter; + +@Root(name="calendar-query") +@NamespaceList({ + @Namespace(reference="DAV:"), + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +}) +@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +public class DavCalendarQuery { + @Element + @Getter @Setter DavProp prop; + + @Element + @Getter @Setter DavFilter filter; +} diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/DavCompFilter.java b/app/src/main/java/at/bitfire/davdroid/webdav/DavCompFilter.java new file mode 100644 index 00000000..ea833dc5 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/webdav/DavCompFilter.java @@ -0,0 +1,23 @@ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Root; + +import lombok.Getter; +import lombok.Setter; + +@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +public class DavCompFilter { + public DavCompFilter(String name) { + this.name = name; + } + + @Attribute(required=false) + String name; + + @Element(required=false,name="comp-filter") + @Getter @Setter DavCompFilter compFilter; + +} diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/DavFilter.java b/app/src/main/java/at/bitfire/davdroid/webdav/DavFilter.java new file mode 100644 index 00000000..e33cf475 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/webdav/DavFilter.java @@ -0,0 +1,13 @@ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Namespace; + +import lombok.Getter; +import lombok.Setter; + +@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +public class DavFilter { + @Element(required=false) + @Getter @Setter DavCompFilter compFilter; +} diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/DavProp.java b/app/src/main/java/at/bitfire/davdroid/webdav/DavProp.java index 81e43085..d4335a40 100644 --- a/app/src/main/java/at/bitfire/davdroid/webdav/DavProp.java +++ b/app/src/main/java/at/bitfire/davdroid/webdav/DavProp.java @@ -35,7 +35,7 @@ public class DavProp { GetCTag getctag; @Element(required=false) - GetETag getetag; + @Setter GetETag getetag; @Root(strict=false) public static class ResourceType { diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java b/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java index 3ce0788b..c1fb361f 100644 --- a/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java +++ b/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java @@ -332,6 +332,18 @@ public class WebDavResource { processMultiStatus(response); } + public void report(String query) throws IOException, HttpException, DavException { + HttpReport report = new HttpReport(location, query); + report.setHeader("Depth", "1"); + + @Cleanup CloseableHttpResponse response = httpClient.execute(report, context); + if (response == null) + throw new DavNoContentException(); + + checkResponse(response); + processMultiStatus(response); + } + /* resource operations */