Resource detection with dav4android

* handle authentication (only Basic auth yet)
* rewrite DavResourceFinder to use dav4android
This commit is contained in:
Ricki Hirner 2015-10-10 15:47:44 +02:00
parent 5aa7caec99
commit 0d26eac245
13 changed files with 181 additions and 51 deletions

View file

@ -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;
}
}

View file

@ -0,0 +1,116 @@
/*
* 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;
}
}
}

View file

@ -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())

View file

@ -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);

View file

@ -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);

View file

@ -25,11 +25,10 @@ public class ServerInfo implements Serializable {
private String errorMessage;
private boolean calDAV = false, cardDAV = false;
private List<ResourceInfo>
addressBooks = new LinkedList<>(),
calendars = new LinkedList<>(),
todoLists = new LinkedList<>();
taskLists = new LinkedList<>();
public boolean hasEnabledCalendars() {

View file

@ -101,7 +101,7 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
}
});
addSync(account, LocalTaskList.TASKS_AUTHORITY, serverInfo.getTodoLists(), new AddSyncCallback() {
addSync(account, LocalTaskList.TASKS_AUTHORITY, serverInfo.getTaskLists(), new AddSyncCallback() {
@Override
public void createLocalCollection(Account account, ServerInfo.ResourceInfo todoList) throws LocalStorageException {
LocalTaskList.create(account, getActivity().getContentResolver(), todoList);

View file

@ -72,7 +72,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
((AddAccountActivity)getActivity()).serverInfo = serverInfo;
Fragment nextFragment;
if (!serverInfo.getTodoLists().isEmpty() && !LocalTaskList.isAvailable(getActivity()))
if (!serverInfo.getTaskLists().isEmpty() && !LocalTaskList.isAvailable(getActivity()))
nextFragment = new InstallAppsFragment();
else
nextFragment = new SelectCollectionsFragment();

View file

@ -60,7 +60,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
nAddressBookHeadings = nAddressBooks == 0 ? 0 : 1;
nCalendars = serverInfo.getCalendars().size();
nCalendarHeadings = nCalendars == 0 ? 0 : 1;
nTaskLists = serverInfo.getTodoLists().size();
nTaskLists = serverInfo.getTaskLists().size();
nTaskListHeadings = nTaskLists == 0 ? 0 : 1;
}
@ -84,7 +84,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings) &&
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists)))
return serverInfo.getTodoLists().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings));
return serverInfo.getTaskLists().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings));
return null;
}

View file

@ -88,7 +88,7 @@ public class SelectCollectionsFragment extends ListFragment {
addressBook.setEnabled(false);
for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
calendar.setEnabled(false);
for (ServerInfo.ResourceInfo todoList : serverInfo.getTodoLists())
for (ServerInfo.ResourceInfo todoList : serverInfo.getTaskLists())
todoList.setEnabled(false);
ListAdapter adapter = getListView().getAdapter();

View file

@ -47,7 +47,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import at.bitfire.davdroid.DAVUtils;
import at.bitfire.davdroid.DavUtils;
import at.bitfire.davdroid.URIUtils;
import at.bitfire.davdroid.resource.iCalendar;
import at.bitfire.davdroid.webdav.DavProp.Comp;
@ -547,7 +547,7 @@ public class WebDavResource {
// getters / setters
public Integer getColor() {
return color != null ? DAVUtils.CalDAVtoARGBColor(color) : null;
return color != null ? DavUtils.CalDAVtoARGBColor(color) : null;
}
public URI getCurrentUserPrincipal() throws URISyntaxException {

View file

@ -1 +1,2 @@
lombok.addGeneratedAnnotation = false
lombok.anyConstructor.suppressConstructorProperties = true

@ -1 +1 @@
Subproject commit c8dffb7fa114a3ca46917ac18088cda981f0afd5
Subproject commit 57e1f34c45f070ca9b269f2bea109f6b1cdcb385