Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 127e3273 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

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)
parent 557e69f1
Loading
Loading
Loading
Loading
+55 −46
Original line number Diff line number Diff line
@@ -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,7 +129,7 @@ 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) {
@@ -141,16 +139,22 @@ public class DavResourceFinder {
                    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>");

                    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);
@@ -160,6 +164,9 @@ public class DavResourceFinder {
                            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();
@@ -193,7 +200,7 @@ public class DavResourceFinder {
                        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) {
@@ -204,7 +211,7 @@ public class DavResourceFinder {
                        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
        for (HttpUrl url : homeSets)
            if (service == Service.CALDAV)
            for (HttpUrl url : calendarHomeSets)
                try {
                    Constants.log.info("Listing calendar collections in home set " + url);
                    DavResource homeSet = new DavResource(httpClient, url);
@@ -233,7 +240,6 @@ public class DavResourceFinder {
                    Constants.log.debug("PROPFIND on " + url + " failed", e);
                }
            else if (service == Service.CARDDAV)
            for (HttpUrl url : addressbookHomeSets)
                try {
                    Constants.log.info("Listing address books in home set " + url);
                    DavResource homeSet = new DavResource(httpClient, url);
@@ -249,15 +255,14 @@ public class DavResourceFinder {
                    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
+0 −20
Original line number Diff line number Diff line
/*
 * 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);
	}
}
+1 −1
Original line number Diff line number Diff line
@@ -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);

+1 −1
Original line number Diff line number Diff line
@@ -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);
+10 −18
Original line number Diff line number Diff line
@@ -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,7 +43,8 @@ public class ServerInfo implements Serializable {

	@RequiredArgsConstructor(suppressConstructorProperties=true)
	@Data
	public static class ResourceInfo implements Cloneable, Serializable {
	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;
		}
	}
}
Loading