New version: 0.6.12

* if well-known URI detection fails with I/O errors, ignore it instead of aborting
* if current-user-principal is not supported by the server, use the user-given URL is the principal URL
* determine availability of CalDAV/CardDAV by whether a calendar/address-book home set is available
* if DAV:displayname is not available for a collection, use its URL path instead
* remove SSLPeerUnverifiedException handling (Apache HttpClient doesn't use this exception)
* use Depth: 1 (instead of Depth: 0) for multi-get REPORT requests
* don't display address books in "Select collections" UI if CardDAV is not available
* don't display calendars in "Select collections" UI if CalDAV is not available
* version bump to 0.6.12
pull/2/head
Ricki Hirner 9 years ago
parent 6c998c31c3
commit aeca582a7c

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="at.bitfire.davdroid"
android:versionCode="53" android:versionName="0.6.11"
android:versionCode="54" android:versionName="0.6.12"
android:installLocation="internalOnly">
<uses-sdk

@ -9,7 +9,7 @@ package at.bitfire.davdroid;
public class Constants {
public static final String
APP_VERSION = "0.6.11",
APP_VERSION = "0.6.12",
ACCOUNT_TYPE = "bitfire.at.davdroid",
WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";

@ -59,90 +59,94 @@ public class DavResourceFinder implements Closeable {
public void findResources(ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException {
// CardDAV
WebDavResource principal = getCurrentUserPrincipal(serverInfo, "carddav");
if (principal != null) {
serverInfo.setCardDAV(true);
URI uriAddressBookHomeSet = null;
try {
principal.propfind(Mode.HOME_SETS);
URI uriAddressBookHomeSet = principal.getAddressbookHomeSet();
if (uriAddressBookHomeSet != null) {
Log.i(TAG, "Found address book home set: " + uriAddressBookHomeSet);
WebDavResource homeSetAddressBooks = new WebDavResource(principal, uriAddressBookHomeSet);
if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) {
homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
if (homeSetAddressBooks.getMembers() != null)
for (WebDavResource resource : homeSetAddressBooks.getMembers())
if (resource.isAddressBook()) {
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
resource.isReadOnly(),
resource.getLocation().toString(),
resource.getDisplayName(),
resource.getDescription(), resource.getColor()
);
VCardVersion version = resource.getVCardVersion();
if (version == null)
version = VCardVersion.V3_0; // VCard 3.0 MUST be supported
info.setVCardVersion(version);
addressBooks.add(info);
}
serverInfo.setAddressBooks(addressBooks);
} else
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
}
uriAddressBookHomeSet = principal.getAddressbookHomeSet();
} catch (Exception e) {
Log.i(TAG, "Couldn't find address-book home set", e);
}
if (uriAddressBookHomeSet != null) {
serverInfo.setCardDAV(true);
Log.i(TAG, "Found address-book home set: " + uriAddressBookHomeSet);
WebDavResource homeSetAddressBooks = new WebDavResource(principal, uriAddressBookHomeSet);
if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) {
homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
if (homeSetAddressBooks.getMembers() != null)
for (WebDavResource resource : homeSetAddressBooks.getMembers())
if (resource.isAddressBook()) {
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
resource.isReadOnly(),
resource.getLocation().toString(),
resource.getDisplayName(),
resource.getDescription(), resource.getColor()
);
VCardVersion version = resource.getVCardVersion();
if (version == null)
version = VCardVersion.V3_0; // VCard 3.0 MUST be supported
info.setVCardVersion(version);
addressBooks.add(info);
}
serverInfo.setAddressBooks(addressBooks);
} else
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
}
// CalDAV
principal = getCurrentUserPrincipal(serverInfo, "caldav");
if (principal != null) {
URI uriCalendarHomeSet = null;
try {
principal.propfind(Mode.HOME_SETS);
uriCalendarHomeSet = principal.getCalendarHomeSet();
} catch(Exception e) {
Log.i(TAG, "Couldn't find calendar home set", e);
}
if (uriCalendarHomeSet != null) {
serverInfo.setCalDAV(true);
Log.i(TAG, "Found calendar home set: " + uriCalendarHomeSet);
principal.propfind(Mode.HOME_SETS);
URI uriCalendarHomeSet = principal.getCalendarHomeSet();
if (uriCalendarHomeSet != null) {
Log.i(TAG, "Found calendar home set: " + uriCalendarHomeSet);
WebDavResource homeSetCalendars = new WebDavResource(principal, uriCalendarHomeSet);
if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) {
homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
if (homeSetCalendars.getMembers() != null)
for (WebDavResource resource : homeSetCalendars.getMembers())
if (resource.isCalendar()) {
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
if (resource.getSupportedComponents() != null) {
// CALDAV:supported-calendar-component-set available
boolean supportsEvents = false;
for (String supportedComponent : resource.getSupportedComponents())
if (supportedComponent.equalsIgnoreCase("VEVENT"))
supportsEvents = true;
if (!supportsEvents) { // ignore collections without VEVENT support
Log.i(TAG, "Ignoring this calendar because of missing VEVENT support");
continue;
}
WebDavResource homeSetCalendars = new WebDavResource(principal, uriCalendarHomeSet);
if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) {
homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
if (homeSetCalendars.getMembers() != null)
for (WebDavResource resource : homeSetCalendars.getMembers())
if (resource.isCalendar()) {
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
if (resource.getSupportedComponents() != null) {
// CALDAV:supported-calendar-component-set available
boolean supportsEvents = false;
for (String supportedComponent : resource.getSupportedComponents())
if (supportedComponent.equalsIgnoreCase("VEVENT"))
supportsEvents = true;
if (!supportsEvents) { // ignore collections without VEVENT support
Log.i(TAG, "Ignoring this calendar because of missing VEVENT support");
continue;
}
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
ServerInfo.ResourceInfo.Type.CALENDAR,
resource.isReadOnly(),
resource.getLocation().toString(),
resource.getDisplayName(),
resource.getDescription(), resource.getColor()
);
info.setTimezone(resource.getTimezone());
calendars.add(info);
}
serverInfo.setCalendars(calendars);
} else
Log.w(TAG, "Found calendar home set, but it doesn't advertise CalDAV support");
}
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
ServerInfo.ResourceInfo.Type.CALENDAR,
resource.isReadOnly(),
resource.getLocation().toString(),
resource.getDisplayName(),
resource.getDescription(), resource.getColor()
);
info.setTimezone(resource.getTimezone());
calendars.add(info);
}
serverInfo.setCalendars(calendars);
} else
Log.w(TAG, "Found calendar home set, but it doesn't advertise CalDAV support");
}
if (!serverInfo.isCalDAV() && !serverInfo.isCardDAV())
throw new DavIncapableException(context.getString(R.string.setup_neither_caldav_nor_carddav));
@ -251,11 +255,13 @@ public class DavResourceFinder implements Closeable {
Log.w(TAG, "Not authorized for well-known " + serviceName + " service detection", e);
throw e;
} catch (URISyntaxException e) {
Log.w(TAG, "Well-known" + serviceName + " service detection failed because of invalid URIs", e);
Log.e(TAG, "Well-known" + serviceName + " service detection failed because of invalid URIs", e);
} catch (HttpException e) {
Log.d(TAG, "Well-known " + serviceName + " service detection failed with HTTP error", e);
} catch (DavException e) {
Log.w(TAG, "Well-known " + serviceName + " service detection failed with unexpected DAV response", e);
} catch (IOException e) {
Log.e(TAG, "Well-known " + serviceName + " service detection failed with I/O error", e);
}
// fall back to user-given initial context path
@ -271,7 +277,9 @@ public class DavResourceFinder implements Closeable {
} catch (DavException e) {
Log.e(TAG, "DAV error when querying principal", e);
}
Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName);
Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName + ", assuming user-given path is principal path");
return base;
}
return null;
}

@ -51,8 +51,10 @@ import net.fortuna.ical4j.model.property.Status;
import org.apache.commons.lang.StringUtils;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
@ -119,7 +121,7 @@ public class LocalCalendar extends LocalCollection<Event> {
color = (color_alpha << 24) | color_rgb;
}
}
ContentValues values = new ContentValues();
values.put(Calendars.ACCOUNT_NAME, account.name);
values.put(Calendars.ACCOUNT_TYPE, account.type);

@ -8,6 +8,7 @@
package at.bitfire.davdroid.resource;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
@ -68,5 +69,18 @@ public class ServerInfo implements Serializable {
VCardVersion vCardVersion;
String timezone;
public String getTitle() {
if (title == null) {
try {
java.net.URL url = new java.net.URL(URL);
return url.getPath();
} catch (MalformedURLException e) {
return URL;
}
} else
return title;
}
}
}

@ -125,8 +125,6 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
// overwrite by more specific message, if possible
if (ExceptionUtils.indexOfType(e, CertPathValidatorException.class) != -1)
serverInfo.setErrorMessage(getContext().getString(R.string.exception_cert_path_validation, e.getMessage()));
else if (ExceptionUtils.indexOfType(e, SSLPeerUnverifiedException.class) != -1)
serverInfo.setErrorMessage(getContext().getString(R.string.exception_peer_unverified, e.getMessage()));
} catch (HttpException e) {
Log.e(TAG, "HTTP error while querying server info", e);
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));

@ -30,7 +30,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
protected Context context;
protected ServerInfo serverInfo;
@Getter protected int nAddressBooks, nCalendars;
@Getter protected int nAddressBooks, nAddressbookHeadings, nCalendars, nCalendarHeadings;
public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
@ -38,7 +38,9 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
this.serverInfo = serverInfo;
nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
nAddressbookHeadings = (nAddressBooks == 0) ? 0 : 1;
nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
nCalendarHeadings = (nCalendars == 0) ? 0 : 1;
}
@ -46,15 +48,17 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
@Override
public int getCount() {
return nAddressBooks + nCalendars + 2;
return nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars;
}
@Override
public Object getItem(int position) {
if (position > 0 && position <= nAddressBooks)
return serverInfo.getAddressBooks().get(position - 1);
else if (position > nAddressBooks + 1)
return serverInfo.getCalendars().get(position - nAddressBooks - 2);
if (position >= nAddressbookHeadings &&
position < (nAddressbookHeadings + nAddressBooks))
return serverInfo.getAddressBooks().get(position - nAddressbookHeadings);
else if (position >= (nAddressbookHeadings + nAddressBooks + nCalendarHeadings) &&
(position < (nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
return serverInfo.getCalendars().get(position - (nAddressbookHeadings + nAddressBooks + nCalendarHeadings));
return null;
}
@ -78,13 +82,13 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
@Override
public int getItemViewType(int position) {
if (position == 0)
if ((nAddressbookHeadings != 0) && (position == 0))
return TYPE_ADDRESS_BOOKS_HEADING;
else if (position <= nAddressBooks)
else if ((nAddressbookHeadings != 0) && (position > 0) && (position < nAddressbookHeadings + nAddressBooks))
return TYPE_ADDRESS_BOOKS_ROW;
else if (position == nAddressBooks + 1)
else if ((nCalendars != 0) && (position == nAddressbookHeadings + nAddressBooks))
return TYPE_CALENDARS_HEADING;
else if (position <= nAddressBooks + nCalendars + 1)
else if ((nCalendars != 0) && (position > nAddressbookHeadings + nAddressBooks) && (position < nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
return TYPE_CALENDARS_ROW;
else
return IGNORE_ITEM_VIEW_TYPE;
@ -133,11 +137,7 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
view.setCompoundDrawablePadding(10);
// set text
String title = info.getTitle();
if (title == null) // unnamed collection
title = context.getString((info.getType() == Type.ADDRESS_BOOK) ?
R.string.setup_address_book : R.string.setup_calendar);
title = "<b>" + title + "</b>";
String title = "<b>" + info.getTitle() + "</b>";
if (info.isReadOnly())
title = title + " (" + context.getString(R.string.setup_read_only) + ")";

@ -30,7 +30,7 @@ public class HttpReport extends HttpEntityEnclosingRequestBaseHC4 {
setHeader("Content-Type", "text/xml; charset=UTF-8");
setHeader("Accept", "text/xml");
setHeader("Depth", "0");
setHeader("Depth", "1");
try {
setEntity(new StringEntity(entity, "UTF-8"));

@ -114,9 +114,7 @@
<string name="setup_querying_server">Contactant servidor. Espereu sisuplau.</string>
<string name="setup_what_to_sync">Quines col·leccions s\'han de sincronitzar?</string>
<string name="setup_address_books">Llibretes de contactes</string>
<string name="setup_address_book">Llibreta de contactes</string>
<string name="setup_calendars">Calendaris</string>
<string name="setup_calendar">Calendari</string>
<string name="setup_select_address_book">Seleccioneu com a molt una llibreta de contactes (Polseu de nou per deseleccionar):</string>
<string name="setup_select_calendars">Seleccioneu els vostres calendaris:</string>

@ -113,9 +113,7 @@
<string name="setup_querying_server">Probíhá komunikace se serverem. Chvilku strpení…</string>
<string name="setup_what_to_sync">Které sbírky mají být synchronizovány?</string>
<string name="setup_address_books">Adresáře</string>
<string name="setup_address_book">Adresář</string>
<string name="setup_calendars">Kalendáře</string>
<string name="setup_calendar">Kalendář</string>
<string name="setup_select_address_book">Vybrat alespoň jeden adresář (opakovaně tapnout pro odebrání):</string>
<string name="setup_select_calendars">Vybrat své kalendáře:</string>

@ -10,7 +10,6 @@
<string name="exception_http">HTTP-Fehler: %s</string>
<string name="exception_incapable_resource">Fehlende Server-Unterstützung: %s</string>
<string name="exception_io">E/A-Fehler: %s</string>
<string name="exception_peer_unverified">Zertifikat ist nicht für \"%s\" ausgestellt</string>
<string name="exception_uri_syntax">URI ungültig: %s</string>
<!-- MainActivity -->
@ -110,9 +109,7 @@
<string name="setup_querying_server">Daten werden vom Server abgefragt. Bitte warten…</string>
<string name="setup_what_to_sync">Welche Ordner sollen synchronisiert werden?</string>
<string name="setup_address_books">Adressbücher</string>
<string name="setup_address_book">Adressbuch</string>
<string name="setup_calendars">Kalender</string>
<string name="setup_calendar">Kalender</string>
<string name="setup_select_address_book">Ein oder kein Adressbuch auswählen (nochmal berühren, um abzuwählen):</string>
<string name="setup_select_calendars">Kalender zur Synchronisation auswählen:</string>

@ -117,9 +117,7 @@
<string name="setup_querying_server">Kapcsolódás a szerverhez. Egy pillanat…</string>
<string name="setup_what_to_sync">Melyik gyűjtemények legyenek szinkronizálva?</string>
<string name="setup_address_books">Címjegyzékek</string>
<string name="setup_address_book">Címjegyzék</string>
<string name="setup_calendars">Naptárak</string>
<string name="setup_calendar">Naptár</string>
<string name="setup_select_address_book">Egy címjegyzék választható (a kijelölés visszavonása újbóli érintéssel vagy másik tétel kiválasztásával):</string>
<string name="setup_select_calendars">Naptárak kiválasztása:</string>

@ -108,9 +108,7 @@
<string name="setup_querying_server">Шаљем упит серверу. Сачекајте…</string>
<string name="setup_what_to_sync">Које збирке да синхронизујем?</string>
<string name="setup_address_books">Адресари</string>
<string name="setup_address_book">Адресар</string>
<string name="setup_calendars">Календари</string>
<string name="setup_calendar">Календар</string>
<string name="setup_select_address_book">Изаберите један адресар (додирните поново да поништите избор):</string>
<string name="setup_select_calendars">Изаберите ваше календаре:</string>

@ -98,9 +98,7 @@
<string name="setup_querying_server">正在请求,请稍等…</string>
<string name="setup_what_to_sync">需要同步哪些集合?</string>
<string name="setup_address_books">通讯录</string>
<string name="setup_address_book">通讯录</string>
<string name="setup_calendars">日历</string>
<string name="setup_calendar">日历</string>
<string name="setup_select_address_book">最多选择一个通讯录:(再次点按可取消选择)</string>
<string name="setup_select_calendars">选择日历:</string>

@ -12,7 +12,6 @@
<string name="exception_http">HTTP error: %s</string>
<string name="exception_incapable_resource">Missing capabilities: %s</string>
<string name="exception_io">I/O error: %s</string>
<string name="exception_peer_unverified">Certificate not issued for \"%s\"</string>
<string name="exception_uri_syntax">Invalid URI: %s</string>
<!-- MainActivity -->
@ -114,9 +113,7 @@
<string name="setup_querying_server">Querying server. Please wait…</string>
<string name="setup_what_to_sync">Which collections shall be synchronized?</string>
<string name="setup_address_books">Address books</string>
<string name="setup_address_book">Address book</string>
<string name="setup_calendars">Calendars</string>
<string name="setup_calendar">Calendar</string>
<string name="setup_select_address_book">Select up to one address book (tap again to unselect):</string>
<string name="setup_select_calendars">Select your calendars:</string>

Loading…
Cancel
Save