mirror of
https://github.com/etesync/android
synced 2025-01-23 14:10:54 +00:00
Ensure trailing slashes are always used for collections + tests
This commit is contained in:
parent
b2bd53f36f
commit
1ec1db3045
@ -7,6 +7,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid;
|
package at.bitfire.davdroid;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -18,7 +20,32 @@ public class URIUtils {
|
|||||||
private static final String TAG = "davdroid.URIUtils";
|
private static final String TAG = "davdroid.URIUtils";
|
||||||
|
|
||||||
|
|
||||||
// handles invalid URLs/paths as good as possible
|
public static String ensureTrailingSlash(String href) {
|
||||||
|
if (!href.endsWith("/")) {
|
||||||
|
Log.d(TAG, "Implicitly appending trailing slash to collection " + href);
|
||||||
|
return href + "/";
|
||||||
|
} else
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URI ensureTrailingSlash(URI href) {
|
||||||
|
if (!href.getPath().endsWith("/"))
|
||||||
|
try {
|
||||||
|
URI newURI = new URI(href.getScheme(), href.getAuthority(), href.getPath() + "/", href.getQuery(), null);
|
||||||
|
|
||||||
|
// "@" is the only character that is not encoded
|
||||||
|
newURI = new URI(newURI.toString().replaceAll("@", "%40"));
|
||||||
|
|
||||||
|
Log.d(TAG, "Implicitly appending trailing slash to collection " + href + " -> " + newURI);
|
||||||
|
return newURI;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.e(TAG, "Couldn't append trailing slash to collection URI", e);
|
||||||
|
}
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** handles invalid URLs/paths as good as possible **/
|
||||||
public static String sanitize(String original) {
|
public static String sanitize(String original) {
|
||||||
if (original == null)
|
if (original == null)
|
||||||
return null;
|
return null;
|
||||||
@ -67,4 +94,5 @@ public class URIUtils {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -88,15 +88,17 @@ public class DavResourceFinder {
|
|||||||
if (homeSetCalendars.getMembers() != null)
|
if (homeSetCalendars.getMembers() != null)
|
||||||
for (WebDavResource resource : homeSetCalendars.getMembers())
|
for (WebDavResource resource : homeSetCalendars.getMembers())
|
||||||
if (resource.isCalendar()) {
|
if (resource.isCalendar()) {
|
||||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getRawPath());
|
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||||
if (resource.getSupportedComponents() != null) {
|
if (resource.getSupportedComponents() != null) {
|
||||||
// CALDAV:supported-calendar-component-set available
|
// CALDAV:supported-calendar-component-set available
|
||||||
boolean supportsEvents = false;
|
boolean supportsEvents = false;
|
||||||
for (String supportedComponent : resource.getSupportedComponents())
|
for (String supportedComponent : resource.getSupportedComponents())
|
||||||
if (supportedComponent.equalsIgnoreCase("VEVENT"))
|
if (supportedComponent.equalsIgnoreCase("VEVENT"))
|
||||||
supportsEvents = true;
|
supportsEvents = true;
|
||||||
if (!supportsEvents) // ignore collections without VEVENT support
|
if (!supportsEvents) { // ignore collections without VEVENT support
|
||||||
|
Log.i(TAG, "Ignoring this calendar because of missing VEVENT support");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||||
|
@ -39,13 +39,16 @@ public class DavProp {
|
|||||||
@Root(strict=false)
|
@Root(strict=false)
|
||||||
public static class ResourceType {
|
public static class ResourceType {
|
||||||
@Element(required=false)
|
@Element(required=false)
|
||||||
@Getter private Addressbook addressbook;
|
@Getter private Collection collection;
|
||||||
@Element(required=false)
|
public static class Collection { }
|
||||||
@Getter private Calendar calendar;
|
|
||||||
|
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private Addressbook addressbook;
|
||||||
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
||||||
public static class Addressbook { }
|
public static class Addressbook { }
|
||||||
|
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private Calendar calendar;
|
||||||
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
||||||
public static class Calendar { }
|
public static class Calendar { }
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,8 @@ public class WebDavResource {
|
|||||||
CURRENT_USER_PRINCIPAL, // resource detection
|
CURRENT_USER_PRINCIPAL, // resource detection
|
||||||
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
||||||
CONTENT_TYPE, READ_ONLY, // WebDAV (common)
|
CONTENT_TYPE, READ_ONLY, // WebDAV (common)
|
||||||
DISPLAY_NAME, DESCRIPTION, CTAG, ETAG,
|
DISPLAY_NAME, DESCRIPTION, ETAG,
|
||||||
|
IS_COLLECTION, CTAG, // collections
|
||||||
IS_CALENDAR, COLOR, TIMEZONE, // CalDAV
|
IS_CALENDAR, COLOR, TIMEZONE, // CalDAV
|
||||||
IS_ADDRESSBOOK, VCARD_VERSION // CardDAV
|
IS_ADDRESSBOOK, VCARD_VERSION // CardDAV
|
||||||
}
|
}
|
||||||
@ -452,6 +453,7 @@ public class WebDavResource {
|
|||||||
// member list will be built from response
|
// member list will be built from response
|
||||||
List<WebDavResource> members = new LinkedList<WebDavResource>();
|
List<WebDavResource> members = new LinkedList<WebDavResource>();
|
||||||
|
|
||||||
|
// iterate through all resources (either ourselves or member)
|
||||||
for (DavResponse singleResponse : multiStatus.response) {
|
for (DavResponse singleResponse : multiStatus.response) {
|
||||||
URI href;
|
URI href;
|
||||||
try {
|
try {
|
||||||
@ -461,42 +463,19 @@ public class WebDavResource {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Processing multi-status element: " + href);
|
Log.d(TAG, "Processing multi-status element: " + href);
|
||||||
|
|
||||||
// about which resource is this response?
|
// process known properties
|
||||||
WebDavResource referenced = null;
|
HashMap<Property, String> properties = new HashMap<Property, String>();
|
||||||
|
List<String> supportedComponents = null;
|
||||||
// "this" resource is either at "location" …
|
byte[] data = null;
|
||||||
if (location.equals(href)) { // -> ourselves
|
|
||||||
referenced = this;
|
|
||||||
} else {
|
|
||||||
// … or at location + "/" (in case of a collection where the server has implicitly appended the trailing slash)
|
|
||||||
if (!location.getRawPath().endsWith("/")) // this is only possible if location doesn't have a trailing slash
|
|
||||||
try {
|
|
||||||
URI locationAsCollection = new URI(location.getScheme(), location.getAuthority(), location.getPath() + "/", location.getQuery(), null);
|
|
||||||
if (locationAsCollection.equals(href)) {
|
|
||||||
Log.d(TAG, "Server implicitly appended trailing slash to " + locationAsCollection);
|
|
||||||
referenced = this;
|
|
||||||
}
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
Log.wtf(TAG, "Couldn't understand our own URI", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, the referenced resource is a member
|
|
||||||
if (referenced == null) {
|
|
||||||
referenced = new WebDavResource(this, href);
|
|
||||||
members.add(referenced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DavPropstat singlePropstat : singleResponse.getPropstat()) {
|
for (DavPropstat singlePropstat : singleResponse.getPropstat()) {
|
||||||
StatusLine status = BasicLineParser.parseStatusLine(singlePropstat.status, new BasicLineParser());
|
StatusLine status = BasicLineParser.parseStatusLine(singlePropstat.status, new BasicLineParser());
|
||||||
|
|
||||||
// ignore information about missing properties etc.
|
// ignore information about missing properties etc.
|
||||||
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DavProp prop = singlePropstat.prop;
|
DavProp prop = singlePropstat.prop;
|
||||||
HashMap<Property, String> properties = referenced.properties;
|
|
||||||
|
|
||||||
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
||||||
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
||||||
@ -520,15 +499,20 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
||||||
properties.put(Property.ADDRESSBOOK_HOMESET, prop.addressbookHomeSet.getHref().href);
|
properties.put(Property.ADDRESSBOOK_HOMESET, URIUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href));
|
||||||
|
|
||||||
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
||||||
properties.put(Property.CALENDAR_HOMESET, prop.calendarHomeSet.getHref().href);
|
properties.put(Property.CALENDAR_HOMESET, URIUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href));
|
||||||
|
|
||||||
if (prop.displayname != null)
|
if (prop.displayname != null)
|
||||||
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
|
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
|
||||||
|
|
||||||
if (prop.resourcetype != null) {
|
if (prop.resourcetype != null) {
|
||||||
|
if (prop.resourcetype.getCollection() != null) {
|
||||||
|
properties.put(Property.IS_COLLECTION, "1");
|
||||||
|
// is a collection, ensure trailing slash
|
||||||
|
href = URIUtils.ensureTrailingSlash(href);
|
||||||
|
}
|
||||||
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
||||||
properties.put(Property.IS_ADDRESSBOOK, "1");
|
properties.put(Property.IS_ADDRESSBOOK, "1");
|
||||||
|
|
||||||
@ -554,9 +538,9 @@ public class WebDavResource {
|
|||||||
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
||||||
|
|
||||||
if (prop.supportedCalendarComponentSet != null) {
|
if (prop.supportedCalendarComponentSet != null) {
|
||||||
referenced.supportedComponents = new LinkedList<String>();
|
supportedComponents = new LinkedList<String>();
|
||||||
for (Comp component : prop.supportedCalendarComponentSet)
|
for (Comp component : prop.supportedCalendarComponentSet)
|
||||||
referenced.supportedComponents.add(component.getName());
|
supportedComponents.add(component.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -568,9 +552,26 @@ public class WebDavResource {
|
|||||||
properties.put(Property.ETAG, prop.getetag.getETag());
|
properties.put(Property.ETAG, prop.getetag.getETag());
|
||||||
|
|
||||||
if (prop.calendarData != null && prop.calendarData.ical != null)
|
if (prop.calendarData != null && prop.calendarData.ical != null)
|
||||||
referenced.content = prop.calendarData.ical.getBytes();
|
data = prop.calendarData.ical.getBytes();
|
||||||
else if (prop.addressData != null && prop.addressData.vcard != null)
|
else if (prop.addressData != null && prop.addressData.vcard != null)
|
||||||
referenced.content = prop.addressData.vcard.getBytes();
|
data = prop.addressData.vcard.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// about which resource is this response?
|
||||||
|
// "this" resource is either at "location" …
|
||||||
|
if (location.equals(href)) { // -> ourselves
|
||||||
|
this.properties.putAll(properties);
|
||||||
|
if (supportedComponents != null)
|
||||||
|
this.supportedComponents = supportedComponents;
|
||||||
|
this.content = data;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
WebDavResource member = new WebDavResource(this, href);
|
||||||
|
member.properties = properties;
|
||||||
|
member.supportedComponents = supportedComponents;
|
||||||
|
member.content = data;
|
||||||
|
|
||||||
|
members.add(member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ exports.getBodyParts = function(conf) {
|
|||||||
<CARD:addressbook/>\
|
<CARD:addressbook/>\
|
||||||
</resourcetype>\
|
</resourcetype>\
|
||||||
<CARD:addressbook-description>\
|
<CARD:addressbook-description>\
|
||||||
Address Book with absolute URL\
|
Address Book with absolute URL and at sign in path\
|
||||||
</CARD:addressbook-description>\
|
</CARD:addressbook-description>\
|
||||||
</prop>\
|
</prop>\
|
||||||
<status>HTTP/1.1 200 OK</status>\
|
<status>HTTP/1.1 200 OK</status>\
|
||||||
|
@ -7,11 +7,24 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.test;
|
package at.bitfire.davdroid.test;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import at.bitfire.davdroid.URIUtils;
|
import at.bitfire.davdroid.URIUtils;
|
||||||
|
|
||||||
public class URIUtilsTest extends TestCase {
|
public class URIUtilsTest extends TestCase {
|
||||||
|
|
||||||
|
public void testEnsureTrailingSlash() throws URISyntaxException {
|
||||||
|
assertEquals("/test/", URIUtils.ensureTrailingSlash("/test"));
|
||||||
|
assertEquals("/test/", URIUtils.ensureTrailingSlash("/test/"));
|
||||||
|
|
||||||
|
String withoutSlash = "http://www.test.at/dav/collection",
|
||||||
|
withSlash = withoutSlash + "/";
|
||||||
|
assertEquals(new URI(withSlash), URIUtils.ensureTrailingSlash(new URI(withoutSlash)));
|
||||||
|
assertEquals(new URI(withSlash), URIUtils.ensureTrailingSlash(new URI(withSlash)));
|
||||||
|
}
|
||||||
|
|
||||||
public void testSanitize() {
|
public void testSanitize() {
|
||||||
assertNull(URIUtils.sanitize(null));
|
assertNull(URIUtils.sanitize(null));
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
public void testPropfindHomeSets() throws IOException, HttpException, DavException {
|
public void testPropfindHomeSets() throws IOException, HttpException, DavException {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
||||||
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
||||||
assertEquals("/dav/addressbooks/test", dav.getAddressbookHomeSet());
|
assertEquals("/dav/addressbooks/test/", dav.getAddressbookHomeSet());
|
||||||
assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet());
|
assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
List<WebDavResource> members = dav.getMembers();
|
List<WebDavResource> members = dav.getMembers();
|
||||||
assertEquals(2, members.size());
|
assertEquals(2, members.size());
|
||||||
assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
|
assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
|
||||||
assertEquals("HTTPS://example.com/user%40domain/absolute-url.vcf", members.get(1).getLocation().toString());
|
assertEquals("HTTPS://example.com/user%40domain/absolute-url.vcf/", members.get(1).getLocation().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user