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

Commit 0d26eac2 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Resource detection with dav4android

* handle authentication (only Basic auth yet)
* rewrite DavResourceFinder to use dav4android
parent 5aa7caec
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -8,17 +8,12 @@

package at.bitfire.davdroid;

import android.util.Log;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DAVUtils {
	private static final String TAG = "davdroid.DAVutils";

public class DavUtils {
	public static final int calendarGreen = 0xFFC3EA6E;


	public static int CalDAVtoARGBColor(String davColor) {
		int color = calendarGreen;		// fallback: "DAVdroid green"
		if (davColor != null) {
@@ -29,9 +24,8 @@ public class DAVUtils {
				int color_alpha = m.group(2) != null ? (Integer.parseInt(m.group(2), 16) & 0xFF) : 0xFF;
				color = (color_alpha << 24) | color_rgb;
			} else
				Log.w(TAG, "Couldn't parse color " + davColor + ", using DAVdroid green");
				Constants.log.warn("Couldn't parse color " + davColor + ", using DAVdroid green");
		}
		return color;
	}

}
+116 −0
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;

import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;

import java.io.IOException;
import java.net.Proxy;
import java.util.List;
import java.util.concurrent.TimeUnit;

import at.bitfire.dav4android.HttpUtils;
import lombok.RequiredArgsConstructor;

public class HttpClient extends OkHttpClient {

    protected static final String
            HEADER_AUTHORIZATION = "Authorization";


    public HttpClient() {
        super();
        initialize();
        enableLogs();
    }

    public HttpClient(String username, String password, boolean preemptive) {
        super();
        initialize();

        // authentication
        if (preemptive)
            networkInterceptors().add(new PreemptiveAuthenticationInterceptor(username, password));
        else
            setAuthenticator(new DavAuthenticator(username, password));

        enableLogs();
    }


    protected void initialize() {
        // don't follow redirects automatically because this may rewrite DAV methods to GET
        setFollowRedirects(false);

        // timeouts
        setConnectTimeout(20, TimeUnit.SECONDS);
        setWriteTimeout(15, TimeUnit.SECONDS);
        setReadTimeout(45, TimeUnit.SECONDS);
    }

    protected void enableLogs() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                at.bitfire.dav4android.Constants.log.trace(message);
            }
        });
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        networkInterceptors().add(logging);
    }


    @RequiredArgsConstructor
    static class PreemptiveAuthenticationInterceptor implements Interceptor {
        final String username, password;

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            request = request.newBuilder()
                    .header("Authorization", Credentials.basic(username, password))
                    .build();
            return chain.proceed(request);
        }
    }

    @RequiredArgsConstructor
    static class DavAuthenticator implements Authenticator {
        final String username, password;

        @Override
        public Request authenticate(Proxy proxy, Response response) throws IOException {
            // check whether this is the first authentication try with our credentials
            Response priorResponse = response.priorResponse();
            boolean triedBefore = priorResponse != null ? priorResponse.request().header(HEADER_AUTHORIZATION) != null : false;
            if (triedBefore)
                // credentials didn't work last time, and they won't work now → stop here
                return null;

            //List<HttpUtils.AuthScheme> schemes = HttpUtils.parseWwwAuthenticate(response.headers("WWW-Authenticate"));
            // TODO Digest auth

            return response.request().newBuilder()
                    .header(HEADER_AUTHORIZATION, Credentials.basic(username, password))
                    .build();
        }

        @Override
        public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
            return null;
        }
    }

}
+50 −30
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import java.util.LinkedList;
import java.util.List;

import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.HttpClient;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.AddressbookDescription;
@@ -35,11 +34,15 @@ import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.CalendarColor;
import at.bitfire.dav4android.property.CalendarDescription;
import at.bitfire.dav4android.property.CalendarHomeSet;
import at.bitfire.dav4android.property.CalendarTimezone;
import at.bitfire.dav4android.property.CurrentUserPrincipal;
import at.bitfire.dav4android.property.CurrentUserPrivilegeSet;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.DAVUtils;
import at.bitfire.davdroid.DavUtils;
import at.bitfire.davdroid.HttpClient;

public class DavResourceFinder {
	private final static String TAG = "davdroid.ResourceFinder";
@@ -52,21 +55,7 @@ public class DavResourceFinder {
	}

	public void findResources(final ServerInfo serverInfo) throws URISyntaxException, IOException, HttpException, DavException {
        final HttpClient httpClient = new HttpClient();
        /*httpClient.setAuthenticator(new Authenticator() {
            @Override
            public Request authenticate(Proxy proxy, Response response) throws IOException {
                String credential = Credentials.basic(serverInfo.getUserName(), serverInfo.getPassword());
                return response.request().newBuilder()
                        .header("Authorization", credential)
                        .build();
            }

            @Override
            public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
                return null;
            }
        });*/
        final HttpClient httpClient = new HttpClient(serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.authPreemptive);

        // CardDAV
        Constants.log.info("*** CardDAV resource detection ***");
@@ -77,23 +66,26 @@ public class DavResourceFinder {
        AddressbookHomeSet addrHomeSet = (AddressbookHomeSet)principal.properties.get(AddressbookHomeSet.NAME);
        if (addrHomeSet != null && !addrHomeSet.hrefs.isEmpty()) {
            Constants.log.info("Found addressbook home set(s): " + addrHomeSet);
            serverInfo.setCardDAV(true);

            // enumerate address books
            List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
            for (String href : addrHomeSet.hrefs) {
                DavResource homeSet = new DavResource(httpClient, principalUrl.resolve(href));
                homeSet.propfind(1, ResourceType.NAME, DisplayName.NAME, AddressbookDescription.NAME);
                homeSet.propfind(1, ResourceType.NAME, CurrentUserPrivilegeSet.NAME, DisplayName.NAME, AddressbookDescription.NAME);
                for (DavResource member : homeSet.members) {
                    ResourceType type = (ResourceType)member.properties.get(ResourceType.NAME);
                    if (type != null && type.types.contains(ResourceType.ADDRESSBOOK)) {
                        Constants.log.info("Found address book: " + member.location);

                        CurrentUserPrivilegeSet privs = (CurrentUserPrivilegeSet)member.properties.get(CurrentUserPrivilegeSet.NAME);
                        if (privs != null && (!privs.mayRead || !privs.mayWriteContent)) {
                            Constants.log.info("Only read/write address books are supported, ignoring this one");
                            continue;
                        }

                        DisplayName displayName = (DisplayName)member.properties.get(DisplayName.NAME);
                        AddressbookDescription description = (AddressbookDescription)member.properties.get(AddressbookDescription.NAME);

                        // TODO read-only

                        addressBooks.add(new ServerInfo.ResourceInfo(
                                ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
                                false,
@@ -117,13 +109,16 @@ public class DavResourceFinder {
        CalendarHomeSet calHomeSet = (CalendarHomeSet)principal.properties.get(CalendarHomeSet.NAME);
        if (calHomeSet != null && !calHomeSet.hrefs.isEmpty()) {
            Constants.log.info("Found calendar home set(s): " + calHomeSet);
            serverInfo.setCalDAV(true);

            // enumerate address books
            List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
            List<ServerInfo.ResourceInfo>
                    calendars = new LinkedList<>(),
                    taskLists = new LinkedList<>();

            for (String href : calHomeSet.hrefs) {
                DavResource homeSet = new DavResource(httpClient, principalUrl.resolve(href));
                homeSet.propfind(1, ResourceType.NAME, DisplayName.NAME, CalendarDescription.NAME, CalendarColor.NAME);
                homeSet.propfind(1, ResourceType.NAME, CurrentUserPrivilegeSet.NAME, DisplayName.NAME,
                        CalendarDescription.NAME, CalendarColor.NAME, CalendarTimezone.NAME, SupportedCalendarComponentSet.NAME);
                for (DavResource member : homeSet.members) {
                    ResourceType type = (ResourceType)member.properties.get(ResourceType.NAME);
                    if (type != null && type.types.contains(ResourceType.CALENDAR)) {
@@ -133,20 +128,45 @@ public class DavResourceFinder {
                        CalendarDescription description = (CalendarDescription)member.properties.get(CalendarDescription.NAME);
                        CalendarColor color = (CalendarColor)member.properties.get(CalendarColor.NAME);

                        // TODO read-only, time-zone, supported components
                        CurrentUserPrivilegeSet privs = (CurrentUserPrivilegeSet)member.properties.get(CurrentUserPrivilegeSet.NAME);
                        boolean readOnly = false;
                        if (privs != null) {
                            if (!privs.mayRead) {
                                Constants.log.info("Calendar not readable, ignoring this one");
                                continue;
                            }
                            readOnly = !privs.mayWriteContent;
                        }

                        calendars.add(new ServerInfo.ResourceInfo(
                        ServerInfo.ResourceInfo collection = new ServerInfo.ResourceInfo(
                                ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
                                false,
                                readOnly,
                                member.location.toString(),
                                displayName != null ? displayName.displayName : null,
                                description != null ? description.description : null,
                                color != null ? DAVUtils.CalDAVtoARGBColor(color.color) : null
                        ));
                                color != null ? DavUtils.CalDAVtoARGBColor(color.color) : null
                        );

                        CalendarTimezone tz = (CalendarTimezone)member.properties.get(CalendarTimezone.NAME);
                        if (tz != null)
                            collection.timezone = tz.vTimeZone;

                        boolean isCalendar = true, isTaskList = true;
                        SupportedCalendarComponentSet comp = (SupportedCalendarComponentSet)member.properties.get(SupportedCalendarComponentSet.NAME);
                        if (comp != null) {
                            isCalendar = comp.supportsEvents;
                            isTaskList = comp.supportsTasks;
                        }

                        if (isCalendar)
                            calendars.add(collection);
                        if (isTaskList)
                            taskLists.add(collection);
                    }
                }
            }
            serverInfo.setCalendars(calendars);
            serverInfo.setTaskLists(taskLists);
        }

		/*if (!serverInfo.isCalDAV() && !serverInfo.isCardDAV())
+2 −2
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;

import at.bitfire.davdroid.DAVUtils;
import at.bitfire.davdroid.DavUtils;
import at.bitfire.davdroid.DateUtils;
import at.bitfire.davdroid.webdav.WebDavResource;
import lombok.Cleanup;
@@ -116,7 +116,7 @@ public class LocalCalendar extends LocalCollection<Event> {
		values.put(Calendars.ACCOUNT_TYPE, account.type);
		values.put(Calendars.NAME, info.getURL());
		values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
		values.put(Calendars.CALENDAR_COLOR, info.getColor() != null ? info.getColor() : DAVUtils.calendarGreen);
		values.put(Calendars.CALENDAR_COLOR, info.getColor() != null ? info.getColor() : DavUtils.calendarGreen);
		values.put(Calendars.OWNER_ACCOUNT, account.name);
		values.put(Calendars.SYNC_EVENTS, 1);
		values.put(Calendars.VISIBLE, 1);
+2 −2
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ import org.dmfs.provider.tasks.TaskContract;

import java.util.LinkedList;

import at.bitfire.davdroid.DAVUtils;
import at.bitfire.davdroid.DavUtils;
import at.bitfire.davdroid.DateUtils;
import at.bitfire.davdroid.webdav.WebDavResource;
import lombok.Cleanup;
@@ -75,7 +75,7 @@ public class LocalTaskList extends LocalCollection<Task> {
		values.put(TaskContract.TaskLists.ACCOUNT_TYPE, account.type);
		values.put(TaskContract.TaskLists._SYNC_ID, info.getURL());
		values.put(TaskContract.TaskLists.LIST_NAME, info.getTitle());
		values.put(TaskContract.TaskLists.LIST_COLOR, info.getColor() != null ? info.getColor() : DAVUtils.calendarGreen);
		values.put(TaskContract.TaskLists.LIST_COLOR, info.getColor() != null ? info.getColor() : DavUtils.calendarGreen);
		values.put(TaskContract.TaskLists.OWNER, account.name);
		values.put(TaskContract.TaskLists.ACCESS_LEVEL, 0);
		values.put(TaskContract.TaskLists.SYNC_ENABLED, 1);
Loading