mirror of
https://github.com/etesync/android
synced 2025-01-11 08:10:58 +00:00
Add support for well-known URIs (RFC 5785) - closes #148
This commit is contained in:
parent
cf40cb2ebc
commit
4be031e274
@ -115,51 +115,22 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
CloseableHttpClient httpClient = DavHttpClient.create(true, true);
|
CloseableHttpClient httpClient = DavHttpClient.create(true, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// (1/5) detect capabilities
|
|
||||||
WebDavResource base = new WebDavResource(httpClient, new URI(serverInfo.getProvidedURL()), serverInfo.getUserName(),
|
WebDavResource base = new WebDavResource(httpClient, new URI(serverInfo.getProvidedURL()), serverInfo.getUserName(),
|
||||||
serverInfo.getPassword(), serverInfo.isAuthPreemptive());
|
serverInfo.getPassword(), serverInfo.isAuthPreemptive());
|
||||||
base.options();
|
|
||||||
|
|
||||||
serverInfo.setCardDAV(base.supportsDAV("addressbook"));
|
// CardDAV
|
||||||
serverInfo.setCalDAV(base.supportsDAV("calendar-access"));
|
WebDavResource principal = getCurrentUserPrincipal(base, "carddav", "addressbook");
|
||||||
if (!base.supportsMethod("PROPFIND") || !base.supportsMethod("REPORT") ||
|
if (principal != null) {
|
||||||
(!serverInfo.isCalDAV() && !serverInfo.isCardDAV()))
|
serverInfo.setCardDAV(true);
|
||||||
throw new DavIncapableException(getContext().getString(R.string.neither_caldav_nor_carddav));
|
|
||||||
|
|
||||||
// (2/5) get principal URL
|
|
||||||
base.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
|
||||||
|
|
||||||
String principalPath = base.getCurrentUserPrincipal();
|
|
||||||
if (principalPath != null)
|
|
||||||
Log.i(TAG, "Found principal path: " + principalPath);
|
|
||||||
else
|
|
||||||
throw new DavIncapableException(getContext().getString(R.string.error_principal_path));
|
|
||||||
|
|
||||||
// (3/5) get home sets
|
|
||||||
WebDavResource principal = new WebDavResource(base, principalPath);
|
|
||||||
principal.propfind(Mode.HOME_SETS);
|
principal.propfind(Mode.HOME_SETS);
|
||||||
|
String pathAddressBooks = principal.getAddressbookHomeSet();
|
||||||
String pathAddressBooks = null;
|
|
||||||
if (serverInfo.isCardDAV()) {
|
|
||||||
pathAddressBooks = principal.getAddressbookHomeSet();
|
|
||||||
if (pathAddressBooks != null)
|
if (pathAddressBooks != null)
|
||||||
Log.i(TAG, "Found address book home set: " + pathAddressBooks);
|
Log.i(TAG, "Found address book home set: " + pathAddressBooks);
|
||||||
else
|
else
|
||||||
throw new DavIncapableException(getContext().getString(R.string.error_home_set_address_books));
|
throw new DavIncapableException(getContext().getString(R.string.error_home_set_address_books));
|
||||||
}
|
|
||||||
|
|
||||||
String pathCalendars = null;
|
WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks);
|
||||||
if (serverInfo.isCalDAV()) {
|
|
||||||
pathCalendars = principal.getCalendarHomeSet();
|
|
||||||
if (pathCalendars != null)
|
|
||||||
Log.i(TAG, "Found calendar home set: " + pathCalendars);
|
|
||||||
else
|
|
||||||
throw new DavIncapableException(getContext().getString(R.string.error_home_set_calendars));
|
|
||||||
}
|
|
||||||
|
|
||||||
// (4/5) get address books
|
|
||||||
if (serverInfo.isCardDAV()) {
|
|
||||||
WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks, true);
|
|
||||||
homeSetAddressBooks.propfind(Mode.MEMBERS_COLLECTIONS);
|
homeSetAddressBooks.propfind(Mode.MEMBERS_COLLECTIONS);
|
||||||
|
|
||||||
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<ServerInfo.ResourceInfo>();
|
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<ServerInfo.ResourceInfo>();
|
||||||
@ -176,13 +147,22 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
);
|
);
|
||||||
addressBooks.add(info);
|
addressBooks.add(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
serverInfo.setAddressBooks(addressBooks);
|
serverInfo.setAddressBooks(addressBooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (5/5) get calendars
|
// CalDAV
|
||||||
if (serverInfo.isCalDAV()) {
|
principal = getCurrentUserPrincipal(base, "caldav", "calendar-access");
|
||||||
WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars, true);
|
if (principal != null) {
|
||||||
|
serverInfo.setCalDAV(true);
|
||||||
|
|
||||||
|
principal.propfind(Mode.HOME_SETS);
|
||||||
|
String pathCalendars = principal.getCalendarHomeSet();
|
||||||
|
if (pathCalendars != null)
|
||||||
|
Log.i(TAG, "Found calendar home set: " + pathCalendars);
|
||||||
|
else
|
||||||
|
throw new DavIncapableException(getContext().getString(R.string.error_home_set_calendars));
|
||||||
|
|
||||||
|
WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars);
|
||||||
homeSetCalendars.propfind(Mode.MEMBERS_COLLECTIONS);
|
homeSetCalendars.propfind(Mode.MEMBERS_COLLECTIONS);
|
||||||
|
|
||||||
List<ServerInfo.ResourceInfo> calendars = new LinkedList<ServerInfo.ResourceInfo>();
|
List<ServerInfo.ResourceInfo> calendars = new LinkedList<ServerInfo.ResourceInfo>();
|
||||||
@ -209,23 +189,88 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
info.setTimezone(resource.getTimezone());
|
info.setTimezone(resource.getTimezone());
|
||||||
calendars.add(info);
|
calendars.add(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
serverInfo.setCalendars(calendars);
|
serverInfo.setCalendars(calendars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!serverInfo.isCalDAV() && !serverInfo.isCardDAV())
|
||||||
|
throw new DavIncapableException(getContext().getString(R.string.neither_caldav_nor_carddav));
|
||||||
|
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage()));
|
||||||
} catch (DavException e) {
|
|
||||||
Log.e(TAG, "DAV error while querying server info", e);
|
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage()));
|
|
||||||
} catch (HttpException e) {
|
} catch (HttpException e) {
|
||||||
Log.e(TAG, "HTTP error while querying server info", e);
|
Log.e(TAG, "HTTP error while querying server info", e);
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
|
||||||
|
} catch (DavException e) {
|
||||||
|
Log.e(TAG, "DAV error while querying server info", e);
|
||||||
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverInfo;
|
return serverInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the current-user-principal for a given WebDavResource. At first, /.well-known/ is tried. Only
|
||||||
|
* if no current-user-principal can be detected for the .well-known location, the given location of the resource
|
||||||
|
* is tried.
|
||||||
|
* When a current-user-principal is found, it is queried for davCapability via OPTIONS.
|
||||||
|
* @param resource Location that will be queried
|
||||||
|
* @param serviceName Well-known service name ("carddav", "caldav")
|
||||||
|
* @param davCapability DAV capability to check for ("addressbook", "calendar-access")
|
||||||
|
* @return WebDavResource of current-user-principal for the given service, or null if it can't be found or it is incapable
|
||||||
|
*/
|
||||||
|
private WebDavResource getCurrentUserPrincipal(WebDavResource resource, String serviceName, String davCapability) throws IOException {
|
||||||
|
// look for well-known service (RFC 5785)
|
||||||
|
try {
|
||||||
|
WebDavResource wellKnown = new WebDavResource(resource, "/.well-known/" + serviceName);
|
||||||
|
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
|
if (wellKnown.getCurrentUserPrincipal() != null) {
|
||||||
|
WebDavResource principal = new WebDavResource(wellKnown, wellKnown.getCurrentUserPrincipal());
|
||||||
|
if (checkCapabilities(principal, davCapability))
|
||||||
|
return principal;
|
||||||
|
else
|
||||||
|
Log.w(TAG, "Current-user-principal " + resource.getLocation() + " found via well-known service, but it doesn't support required DAV facilities");
|
||||||
|
}
|
||||||
|
} catch (HttpException e) {
|
||||||
|
Log.d(TAG, "Well-known service detection failed with HTTP error", e);
|
||||||
|
} catch (DavException e) {
|
||||||
|
Log.d(TAG, "Well-known service detection failed at DAV level", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// fall back to user-given initial context path
|
||||||
|
resource.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
|
if (resource.getCurrentUserPrincipal() != null) {
|
||||||
|
WebDavResource principal = new WebDavResource(resource, resource.getCurrentUserPrincipal());
|
||||||
|
if (checkCapabilities(principal, davCapability))
|
||||||
|
return principal;
|
||||||
|
else
|
||||||
|
Log.w(TAG, "Current-user-principal " + resource.getLocation() + " found at user-given location, but it doesn't support required DAV facilities");
|
||||||
|
}
|
||||||
|
} catch (HttpException e) {
|
||||||
|
Log.d(TAG, "Service detection failed with HTTP error", e);
|
||||||
|
} catch (DavException e) {
|
||||||
|
Log.d(TAG, "Service detection failed at DAV level", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkCapabilities(WebDavResource resource, String davCapability) throws IOException {
|
||||||
|
// check for necessary capabilities
|
||||||
|
try {
|
||||||
|
resource.options();
|
||||||
|
if (resource.supportsDAV(davCapability) &&
|
||||||
|
resource.supportsMethod("PROPFIND") &&
|
||||||
|
resource.supportsMethod("GET") &&
|
||||||
|
resource.supportsMethod("REPORT") &&
|
||||||
|
resource.supportsMethod("PUT") &&
|
||||||
|
resource.supportsMethod("DELETE"))
|
||||||
|
return true;
|
||||||
|
} catch(HttpException e) {
|
||||||
|
// for instance, 405 Method not allowed
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public class ServerInfo implements Serializable {
|
|||||||
|
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
private boolean calDAV, cardDAV;
|
private boolean calDAV = false, cardDAV = false;
|
||||||
private List<ResourceInfo>
|
private List<ResourceInfo>
|
||||||
addressBooks = new LinkedList<ResourceInfo>(),
|
addressBooks = new LinkedList<ResourceInfo>(),
|
||||||
calendars = new LinkedList<ResourceInfo>();
|
calendars = new LinkedList<ResourceInfo>();
|
||||||
|
@ -134,10 +134,6 @@ public class WebDavResource {
|
|||||||
location = parent.location.resolve(URIUtils.sanitize(member));
|
location = parent.location.resolve(URIUtils.sanitize(member));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent, String member, boolean trailingSlash) {
|
|
||||||
this(parent, (trailingSlash && !member.endsWith("/")) ? (member + "/") : member);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent, String member, String ETag) {
|
public WebDavResource(WebDavResource parent, String member, String ETag) {
|
||||||
this(parent, member);
|
this(parent, member);
|
||||||
properties.put(Property.ETAG, ETag);
|
properties.put(Property.ETAG, ETag);
|
||||||
|
@ -127,7 +127,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindAddressBooks() throws IOException, HttpException, DavException {
|
public void testPropfindAddressBooks() throws IOException, HttpException, DavException {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test", true);
|
WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test");
|
||||||
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
||||||
assertEquals(2, dav.getMembers().size());
|
assertEquals(2, dav.getMembers().size());
|
||||||
for (WebDavResource member : dav.getMembers()) {
|
for (WebDavResource member : dav.getMembers()) {
|
||||||
@ -140,7 +140,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindCalendars() throws IOException, HttpException, DavException {
|
public void testPropfindCalendars() throws IOException, HttpException, DavException {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "calendars/test", true);
|
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
||||||
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
||||||
assertEquals(3, dav.getMembers().size());
|
assertEquals(3, dav.getMembers().size());
|
||||||
assertEquals("0xFF00FF", dav.getMembers().get(2).getColor());
|
assertEquals("0xFF00FF", dav.getMembers().get(2).getColor());
|
||||||
@ -188,7 +188,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiGet() throws DavException, IOException, HttpException {
|
public void testMultiGet() throws DavException, IOException, HttpException {
|
||||||
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf", true);
|
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf");
|
||||||
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" });
|
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" });
|
||||||
assertEquals(2, davAddressBook.getMembers().size());
|
assertEquals(2, davAddressBook.getMembers().size());
|
||||||
for (WebDavResource member : davAddressBook.getMembers()) {
|
for (WebDavResource member : davAddressBook.getMembers()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user