1
0
mirror of https://github.com/etesync/android synced 2025-01-22 21:51:04 +00:00

Improve DavResourceFinder

* check whether user-given URL actually provides CalDAV/CardDAV before trusting the current-user-principal
  as there may be different principals for CalDAV and CardDAV (if both services are completely separated)
This commit is contained in:
Ricki Hirner 2015-10-17 19:13:16 +02:00
parent 80231dd44b
commit d3c1688407
No known key found for this signature in database
GPG Key ID: C4A212CF0B2B4566
9 changed files with 76 additions and 96 deletions

View File

@ -21,16 +21,17 @@ import org.xbill.DNS.Type;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.UrlUtils;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.exception.NotFoundException;
import at.bitfire.dav4android.property.AddressbookDescription;
import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.CalendarColor;
@ -47,8 +48,6 @@ import at.bitfire.davdroid.HttpClient;
import lombok.NonNull;
public class DavResourceFinder {
private final static String TAG = "davdroid.ResourceFinder";
protected enum Service {
CALDAV("caldav"),
CARDDAV("carddav");
@ -62,10 +61,10 @@ public class DavResourceFinder {
protected final HttpClient httpClient;
protected final ServerInfo serverInfo;
protected List<ServerInfo.ResourceInfo>
addressbooks = new LinkedList<>(),
calendars = new LinkedList<>(),
taskLists = new LinkedList<>();
protected Map<HttpUrl, ServerInfo.ResourceInfo>
addressbooks = new HashMap<>(),
calendars = new HashMap<>(),
taskLists = new HashMap<>();
public DavResourceFinder(Context context, ServerInfo serverInfo) {
@ -80,13 +79,12 @@ public class DavResourceFinder {
findResources(Service.CALDAV);
}
public void findResources(Service service) throws URISyntaxException, IOException, HttpException, DavException { {
public void findResources(Service service) throws URISyntaxException, IOException, HttpException, DavException {
URI baseURI = serverInfo.getBaseURI();
String domain = null;
HttpUrl principalUrl = null;
Set<HttpUrl> calendarHomeSets = new HashSet<>(),
addressbookHomeSets = new HashSet<>();
Set<HttpUrl> homeSets = new HashSet<>();
if (service == Service.CALDAV) {
calendars.clear();
@ -94,7 +92,7 @@ public class DavResourceFinder {
} else if (service == Service.CARDDAV)
addressbooks.clear();
Constants.log.info("STARTING COLLECTION DISCOVERY FOR SERVICE " + service);
Constants.log.info("*** STARTING COLLECTION DISCOVERY FOR SERVICE " + service.name.toUpperCase() + "***");
if ("http".equals(baseURI.getScheme()) || "https".equals(baseURI.getScheme())) {
HttpUrl userURL = HttpUrl.get(baseURI);
@ -131,35 +129,44 @@ public class DavResourceFinder {
for (String href : calendarHomeSet.hrefs) {
HttpUrl url = userURL.resolve(href);
if (url != null)
calendarHomeSets.add(url);
homeSets.add(url);
}
}
} else if (service == Service.CARDDAV) {
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet)davBase.properties.get(AddressbookHomeSet.NAME);
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet) davBase.properties.get(AddressbookHomeSet.NAME);
if (addressbookHomeSet != null) {
Constants.log.info("Found <addressbook-home-set> at user-given URL");
for (String href : addressbookHomeSet.hrefs) {
HttpUrl url = userURL.resolve(href);
if (url != null)
addressbookHomeSets.add(url);
homeSets.add(url);
}
}
}
/* When home sets haven already been found, skip further searching.
* Otherwise (no home sets found), treat the user-given URL as "initial context path" for service discovery. */
if (calendarHomeSets.isEmpty()) {
Constants.log.info("No <calendar-home-set> set found, looking for <current-user-principal>");
* Otherwise (no home sets found), treat the user-given URL as "initial context path" for service discovery.
*
* Keep in mind that the CalDAV principal URL must not be the CardDAV principal URL! */
if (homeSets.isEmpty())
try {
Constants.log.info("No home sets found, looking for <current-user-principal>");
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal) davBase.properties.get(CurrentUserPrincipal.NAME);
if (currentUserPrincipal != null && currentUserPrincipal.href != null)
principalUrl = davBase.location.resolve(currentUserPrincipal.href);
davBase.options();
if ((service == Service.CALDAV && davBase.capabilities.contains("calendar-access")) ||
(service == Service.CARDDAV && davBase.capabilities.contains("addressbook"))) {
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal)davBase.properties.get(CurrentUserPrincipal.NAME);
if (currentUserPrincipal != null && currentUserPrincipal.href != null)
principalUrl = davBase.location.resolve(currentUserPrincipal.href);
if (principalUrl == null) {
Constants.log.info("User-given URL doesn't contain <current-user-principal>, trying /.well-known/" + service.name);
principalUrl = getCurrentUserPrincipal(userURL.resolve("/.well-known/" + service.name));
if (principalUrl == null) {
Constants.log.info("User-given URL doesn't contain <current-user-principal>, trying /.well-known/" + service.name);
principalUrl = getCurrentUserPrincipal(userURL.resolve("/.well-known/" + service.name));
}
}
} catch(IOException|HttpException|DavException e) {
Constants.log.debug("Couldn't find <current-user-principal>", e);
}
}
// try service discovery with "domain" = user-given host name
domain = baseURI.getHost();
@ -187,24 +194,24 @@ public class DavResourceFinder {
if (service == Service.CALDAV) {
principal.propfind(0, CalendarHomeSet.NAME);
CalendarHomeSet calendarHomeSet = (CalendarHomeSet)principal.properties.get(CalendarHomeSet.NAME);
CalendarHomeSet calendarHomeSet = (CalendarHomeSet) principal.properties.get(CalendarHomeSet.NAME);
if (calendarHomeSet != null) {
Constants.log.info("Found <calendar-home-set> at principal URL");
for (String href : calendarHomeSet.hrefs) {
HttpUrl url = principal.location.resolve(href);
if (url != null)
calendarHomeSets.add(url);
homeSets.add(url);
}
}
} else if (service == Service.CARDDAV) {
principal.propfind(0, AddressbookHomeSet.NAME);
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet)principal.properties.get(AddressbookHomeSet.NAME);
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet) principal.properties.get(AddressbookHomeSet.NAME);
if (addressbookHomeSet != null) {
Constants.log.info("Found <addressbook-home-set> at principal URL");
for (String href : addressbookHomeSet.hrefs) {
HttpUrl url = principal.location.resolve(href);
if (url != null)
addressbookHomeSets.add(url);
homeSets.add(url);
}
}
}
@ -215,8 +222,8 @@ public class DavResourceFinder {
}
// now query all home sets
if (service == Service.CALDAV)
for (HttpUrl url : calendarHomeSets)
for (HttpUrl url : homeSets)
if (service == Service.CALDAV)
try {
Constants.log.info("Listing calendar collections in home set " + url);
DavResource homeSet = new DavResource(httpClient, url);
@ -229,11 +236,10 @@ public class DavResourceFinder {
// members of the home set can be calendars, too
for (DavResource member : homeSet.members)
addIfCalendar(member);
} catch (IOException|HttpException|DavException e) {
} catch (IOException | HttpException | DavException e) {
Constants.log.debug("PROPFIND on " + url + " failed", e);
}
else if (service == Service.CARDDAV)
for (HttpUrl url : addressbookHomeSets)
else if (service == Service.CARDDAV)
try {
Constants.log.info("Listing address books in home set " + url);
DavResource homeSet = new DavResource(httpClient, url);
@ -245,19 +251,18 @@ public class DavResourceFinder {
// members of the home set can be calendars, too
for (DavResource member : homeSet.members)
addIfAddressBook(member);
} catch (IOException|HttpException|DavException e) {
} catch (IOException | HttpException | DavException e) {
Constants.log.debug("PROPFIND on " + url + " failed", e);
}
// TODO remove duplicates
// TODO notify user on errors?
if (service == Service.CALDAV) {
serverInfo.setCalendars(calendars);
serverInfo.setTaskLists(taskLists);
serverInfo.setCalendars(calendars.values().toArray(new ServerInfo.ResourceInfo[calendars.size()]));
serverInfo.setTaskLists(taskLists.values().toArray(new ServerInfo.ResourceInfo[taskLists.size()]));
} else if (service == Service.CARDDAV)
serverInfo.setAddressBooks(addressbooks);
}}
serverInfo.setAddressBooks(addressbooks.values().toArray(new ServerInfo.ResourceInfo[addressbooks.size()]));
}
/**
* If the given DavResource is a #{@link ResourceType#ADDRESSBOOK}, add it to #{@link #addressbooks}.
@ -266,8 +271,10 @@ public class DavResourceFinder {
protected void addIfAddressBook(@NonNull DavResource dav) {
ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME);
if (resourceType != null && resourceType.types.contains(ResourceType.ADDRESSBOOK)) {
dav.location = UrlUtils.withTrailingSlash(dav.location);
Constants.log.info("Found address book at " + dav.location);
addressbooks.add(resourceInfo(dav, ServerInfo.ResourceInfo.Type.ADDRESS_BOOK));
addressbooks.put(dav.location, resourceInfo(dav, ServerInfo.ResourceInfo.Type.ADDRESS_BOOK));
}
}
@ -282,7 +289,9 @@ public class DavResourceFinder {
protected void addIfCalendar(@NonNull DavResource dav) {
ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME);
if (resourceType != null && resourceType.types.contains(ResourceType.CALENDAR)) {
dav.location = UrlUtils.withTrailingSlash(dav.location);
Constants.log.info("Found calendar collection at " + dav.location);
boolean supportsEvents = true, supportsTasks = true;
SupportedCalendarComponentSet supportedCalendarComponentSet = (SupportedCalendarComponentSet)dav.properties.get(SupportedCalendarComponentSet.NAME);
if (supportedCalendarComponentSet != null) {
@ -290,9 +299,9 @@ public class DavResourceFinder {
supportsTasks = supportedCalendarComponentSet.supportsTasks;
}
if (supportsEvents)
calendars.add(resourceInfo(dav, ServerInfo.ResourceInfo.Type.CALENDAR));
calendars.put(dav.location, resourceInfo(dav, ServerInfo.ResourceInfo.Type.CALENDAR));
if (supportsTasks)
taskLists.add(resourceInfo(dav, ServerInfo.ResourceInfo.Type.CALENDAR));
taskLists.put(dav.location, resourceInfo(dav, ServerInfo.ResourceInfo.Type.CALENDAR));
}
}
@ -339,7 +348,7 @@ public class DavResourceFinder {
return new ServerInfo.ResourceInfo(
type,
readOnly,
dav.location.toString(),
UrlUtils.withTrailingSlash(dav.location).toString(),
title,
description,
color
@ -435,7 +444,7 @@ public class DavResourceFinder {
// helpers
private SRVRecord selectSRVRecord(Record[] records) {
if (records.length > 1)
Constants.log.warn("Multiple SRV records not supported yet; using first one");

View File

@ -1,20 +0,0 @@
/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.resource;
public class InvalidResourceException extends Exception {
private static final long serialVersionUID = 1593585432655578220L;
public InvalidResourceException(String message) {
super(message);
}
public InvalidResourceException(Throwable throwable) {
super(throwable);
}
}

View File

@ -73,7 +73,7 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
throw new CalendarStorageException("Couldn't acquire ContentProviderClient for " + CalendarContract.AUTHORITY);
ContentValues values = new ContentValues();
values.put(Calendars.NAME, info.getURL());
values.put(Calendars.NAME, info.getUrl());
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
values.put(Calendars.CALENDAR_COLOR, info.color != null ? info.color : defaultColor);

View File

@ -58,7 +58,7 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
throw new CalendarStorageException("Couldn't access OpenTasks provider");
ContentValues values = new ContentValues();
values.put(TaskLists._SYNC_ID, info.getURL());
values.put(TaskLists._SYNC_ID, info.getUrl());
values.put(TaskLists.LIST_NAME, info.getTitle());
values.put(TaskLists.LIST_COLOR, info.color != null ? info.color : defaultColor);
values.put(TaskLists.OWNER, account.name);

View File

@ -10,11 +10,11 @@ package at.bitfire.davdroid.resource;
import com.squareup.okhttp.HttpUrl;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import at.bitfire.dav4android.UrlUtils;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@ -27,10 +27,10 @@ public class ServerInfo implements Serializable {
private String errorMessage;
private List<ResourceInfo>
addressBooks = new LinkedList<>(),
calendars = new LinkedList<>(),
taskLists = new LinkedList<>();
private ResourceInfo
addressBooks[] = new ResourceInfo[0],
calendars[] = new ResourceInfo[0],
taskLists[] = new ResourceInfo[0];
public boolean hasEnabledCalendars() {
@ -43,8 +43,9 @@ public class ServerInfo implements Serializable {
@RequiredArgsConstructor(suppressConstructorProperties=true)
@Data
public static class ResourceInfo implements Cloneable, Serializable {
public enum Type {
public static class ResourceInfo implements Serializable {
public enum Type {
ADDRESS_BOOK,
CALENDAR
}
@ -54,7 +55,7 @@ public class ServerInfo implements Serializable {
final Type type;
final boolean readOnly;
final String URL, // absolute URL of resource
final String url, // absolute URL of resource
title,
description;
final Integer color;
@ -75,7 +76,7 @@ public class ServerInfo implements Serializable {
type = src.type;
readOnly = src.readOnly;
URL = src.URL;
url = src.url;
title = src.title;
description = src.description;
color = src.color;
@ -83,14 +84,5 @@ public class ServerInfo implements Serializable {
timezone = src.timezone;
}
// some logic
public String getTitle() {
if (title == null) {
HttpUrl url = HttpUrl.parse(URL);
return url != null ? url.toString() : "";
} else
return title;
}
}
}

View File

@ -29,7 +29,6 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Calendar;
import java.util.List;
import at.bitfire.davdroid.Constants;
@ -107,7 +106,7 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
LocalAddressBook addressBook = new LocalAddressBook(account, provider);
// set URL
addressBook.setURL(resource.getURL());
addressBook.setURL(resource.getUrl());
// set Settings
ContentValues settings = new ContentValues(2);
@ -150,7 +149,7 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
void createLocalCollection(Account account, ServerInfo.ResourceInfo resource) throws ContactsStorageException;
}
protected void addSync(Account account, String authority, List<ServerInfo.ResourceInfo> resourceList, AddSyncCallback callback) {
protected void addSync(Account account, String authority, ServerInfo.ResourceInfo[] resourceList, AddSyncCallback callback) {
boolean sync = false;
for (ServerInfo.ResourceInfo resource : resourceList)
if (resource.isEnabled()) {

View File

@ -70,7 +70,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
((AddAccountActivity)getActivity()).serverInfo = serverInfo;
Fragment nextFragment;
if (!serverInfo.getTaskLists().isEmpty() && !LocalTaskList.tasksProviderAvailable(getActivity().getContentResolver()))
if (serverInfo.getTaskLists().length > 0 && !LocalTaskList.tasksProviderAvailable(getActivity().getContentResolver()))
nextFragment = new InstallAppsFragment();
else
nextFragment = new SelectCollectionsFragment();

View File

@ -56,11 +56,11 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
this.context = context;
this.serverInfo = serverInfo;
nAddressBooks = serverInfo.getAddressBooks().size();
nAddressBooks = serverInfo.getAddressBooks().length;
nAddressBookHeadings = nAddressBooks == 0 ? 0 : 1;
nCalendars = serverInfo.getCalendars().size();
nCalendars = serverInfo.getCalendars().length;
nCalendarHeadings = nCalendars == 0 ? 0 : 1;
nTaskLists = serverInfo.getTaskLists().size();
nTaskLists = serverInfo.getTaskLists().length;
nTaskListHeadings = nTaskLists == 0 ? 0 : 1;
}
@ -76,15 +76,15 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
public Object getItem(int position) {
if (position >= nAddressBookHeadings &&
position < (nAddressBookHeadings + nAddressBooks))
return serverInfo.getAddressBooks().get(position - nAddressBookHeadings);
return serverInfo.getAddressBooks()[position - nAddressBookHeadings];
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings) &&
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
return serverInfo.getCalendars().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings));
return serverInfo.getCalendars()[position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings)];
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings) &&
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists)))
return serverInfo.getTaskLists().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings));
return serverInfo.getTaskLists()[position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings)];
return null;
}
@ -198,7 +198,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
String description = info.getDescription();
if (description == null)
description = info.getURL();
description = info.getUrl();
// FIXME escape HTML
view.setText(Html.fromHtml(title + "<br/>" + description));

@ -1 +1 @@
Subproject commit a22eb4eb193c8f22180369791df3671e1cab6f1c
Subproject commit e7218aeb8ad8a96620208140bc7b86f63bf05fad