1
0
mirror of https://github.com/etesync/android synced 2025-08-03 12:28:04 +00:00

VCard 4 support detection; new ez-vcard version

* detect CardDAV VCard version support using supported-address-data + test
* account setting for supported VCard version
* don't ask for calendar details when querying CardDAV collections
* don't ask for address book details when querying CalDAV collections
* ez-vcard update to 0.9.5 (fixes #268), adapted exception handling
* refactoring: unnecessary DavProp prefixes removed
This commit is contained in:
rfc2822 2014-07-30 01:47:35 +02:00
parent 2c79ae20e5
commit bdee53b5ab
16 changed files with 171 additions and 105 deletions

Binary file not shown.

BIN
libs/ez-vcard-0.9.5.jar Normal file

Binary file not shown.

View File

@ -24,7 +24,6 @@ import android.util.Log;
import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.Constants;
import ezvcard.Ezvcard; import ezvcard.Ezvcard;
import ezvcard.VCard; import ezvcard.VCard;
import ezvcard.VCardException;
import ezvcard.VCardVersion; import ezvcard.VCardVersion;
import ezvcard.ValidationWarnings; import ezvcard.ValidationWarnings;
import ezvcard.parameter.EmailType; import ezvcard.parameter.EmailType;
@ -127,7 +126,7 @@ public class Contact extends Resource {
/* VCard methods */ /* VCard methods */
@Override @Override
public void parseEntity(InputStream is) throws IOException, VCardException { public void parseEntity(InputStream is) throws IOException {
VCard vcard = Ezvcard.parse(is).first(); VCard vcard = Ezvcard.parse(is).first();
if (vcard == null) if (vcard == null)
return; return;
@ -389,7 +388,7 @@ public class Contact extends Resource {
vcard.addPhoto(new Photo(photo, ImageType.JPEG)); vcard.addPhoto(new Photo(photo, ImageType.JPEG));
// PRODID, REV // PRODID, REV
vcard.setProdId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")"); vcard.setProductId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")");
vcard.setRevision(Revision.now()); vcard.setRevision(Revision.now());
// validate and print warnings // validate and print warnings

View File

@ -28,6 +28,7 @@ import at.bitfire.davdroid.webdav.HttpPropfind;
import at.bitfire.davdroid.webdav.WebDavResource; import at.bitfire.davdroid.webdav.WebDavResource;
import at.bitfire.davdroid.webdav.WebDavResource.PutMode; import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
import ezvcard.io.text.VCardParseException;
/** /**
* Represents a remotely stored synchronizable collection (collection as in * Represents a remotely stored synchronizable collection (collection as in
@ -126,7 +127,11 @@ public abstract class RemoteCollection<T extends Resource> {
throw new DavNoContentException(); throw new DavNoContentException();
@Cleanup InputStream is = new ByteArrayInputStream(data); @Cleanup InputStream is = new ByteArrayInputStream(data);
try {
resource.parseEntity(is); resource.parseEntity(is);
} catch(VCardParseException e) {
throw new InvalidResourceException(e);
}
return resource; return resource;
} }

View File

@ -23,6 +23,7 @@ import android.os.Bundle;
import android.provider.CalendarContract; import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars; import android.provider.CalendarContract.Calendars;
import android.util.Log; import android.util.Log;
import ezvcard.VCardVersion;
public class AccountSettings { public class AccountSettings {
private final static String TAG = "davdroid.AccountSettings"; private final static String TAG = "davdroid.AccountSettings";
@ -35,7 +36,8 @@ public class AccountSettings {
KEY_AUTH_PREEMPTIVE = "auth_preemptive", KEY_AUTH_PREEMPTIVE = "auth_preemptive",
KEY_ADDRESSBOOK_URL = "addressbook_url", KEY_ADDRESSBOOK_URL = "addressbook_url",
KEY_ADDRESSBOOK_CTAG = "addressbook_ctag"; KEY_ADDRESSBOOK_CTAG = "addressbook_ctag",
KEY_ADDRESSBOOK_VCARD_VERSION = "addressbook_vcard_version";
Context context; Context context;
AccountManager accountManager; AccountManager accountManager;
@ -68,6 +70,7 @@ public class AccountSettings {
for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks()) for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks())
if (addressBook.isEnabled()) { if (addressBook.isEnabled()) {
bundle.putString(KEY_ADDRESSBOOK_URL, addressBook.getURL()); bundle.putString(KEY_ADDRESSBOOK_URL, addressBook.getURL());
bundle.putString(KEY_ADDRESSBOOK_VCARD_VERSION, addressBook.getVCardVersion().getVersion());
continue; continue;
} }
return bundle; return bundle;
@ -103,6 +106,14 @@ public class AccountSettings {
accountManager.setUserData(account, KEY_ADDRESSBOOK_CTAG, cTag); accountManager.setUserData(account, KEY_ADDRESSBOOK_CTAG, cTag);
} }
public VCardVersion getAddressBookVCardVersion() {
VCardVersion version = VCardVersion.V3_0;
String versionStr = accountManager.getUserData(account, KEY_ADDRESSBOOK_VCARD_VERSION);
if (versionStr != null)
version = VCardVersion.valueOfByStr(versionStr);
return version;
}
// update from previous account settings // update from previous account settings

View File

@ -8,6 +8,7 @@ import java.util.List;
import ch.boye.httpclientandroidlib.HttpException; import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
import ezvcard.VCardVersion;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import at.bitfire.davdroid.R; import at.bitfire.davdroid.R;
@ -40,7 +41,7 @@ public class DavResourceFinder {
WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks); WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks);
if (checkCapabilities(homeSetAddressBooks, "addressbook")) { if (checkCapabilities(homeSetAddressBooks, "addressbook")) {
homeSetAddressBooks.propfind(Mode.MEMBERS_COLLECTIONS); homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<ServerInfo.ResourceInfo>(); List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<ServerInfo.ResourceInfo>();
if (homeSetAddressBooks.getMembers() != null) if (homeSetAddressBooks.getMembers() != null)
@ -54,6 +55,12 @@ public class DavResourceFinder {
resource.getDisplayName(), resource.getDisplayName(),
resource.getDescription(), resource.getColor() 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); addressBooks.add(info);
} }
serverInfo.setAddressBooks(addressBooks); serverInfo.setAddressBooks(addressBooks);
@ -74,7 +81,7 @@ public class DavResourceFinder {
WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars); WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars);
if (checkCapabilities(homeSetCalendars, "calendar-access")) { if (checkCapabilities(homeSetCalendars, "calendar-access")) {
homeSetCalendars.propfind(Mode.MEMBERS_COLLECTIONS); homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
List<ServerInfo.ResourceInfo> calendars = new LinkedList<ServerInfo.ResourceInfo>(); List<ServerInfo.ResourceInfo> calendars = new LinkedList<ServerInfo.ResourceInfo>();
if (homeSetCalendars.getMembers() != null) if (homeSetCalendars.getMembers() != null)

View File

@ -118,6 +118,10 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
httpClientLock.readLock().lock(); httpClientLock.readLock().lock();
httpClientLock.writeLock().unlock(); httpClientLock.writeLock().unlock();
// TODO use VCard 4.0 if possible
AccountSettings accountSettings = new AccountSettings(getContext(), account);
Log.d(TAG, "Server supports VCard version " + accountSettings.getAddressBookVCardVersion());
try { try {
// get local <-> remote collection pairs // get local <-> remote collection pairs
Map<LocalCollection<?>, RemoteCollection<?>> syncCollections = getSyncPairs(account, provider); Map<LocalCollection<?>, RemoteCollection<?>> syncCollections = getSyncPairs(account, provider);

View File

@ -11,6 +11,7 @@ import java.io.Serializable;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import ezvcard.VCardVersion;
import lombok.Data; import lombok.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -30,6 +31,7 @@ public class ServerInfo implements Serializable {
addressBooks = new LinkedList<ResourceInfo>(), addressBooks = new LinkedList<ResourceInfo>(),
calendars = new LinkedList<ResourceInfo>(); calendars = new LinkedList<ResourceInfo>();
public boolean hasEnabledCalendars() { public boolean hasEnabledCalendars() {
for (ResourceInfo calendar : calendars) for (ResourceInfo calendar : calendars)
if (calendar.enabled) if (calendar.enabled)
@ -54,6 +56,8 @@ public class ServerInfo implements Serializable {
final boolean readOnly; final boolean readOnly;
final String URL, title, description, color; final String URL, title, description, color;
VCardVersion vCardVersion;
String timezone; String timezone;
} }
} }

View File

@ -32,12 +32,12 @@ public class DavMultiget {
DavMultiget multiget = (type == Type.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget(); DavMultiget multiget = (type == Type.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget();
multiget.prop = new DavProp(); multiget.prop = new DavProp();
multiget.prop.getetag = new DavProp.DavPropGetETag(); multiget.prop.getetag = new DavProp.GetETag();
if (type == Type.ADDRESS_BOOK) if (type == Type.ADDRESS_BOOK)
multiget.prop.addressData = new DavProp.DavPropAddressData(); multiget.prop.addressData = new DavProp.AddressData();
else if (type == Type.CALENDAR) else if (type == Type.CALENDAR)
multiget.prop.calendarData = new DavProp.DavPropCalendarData(); multiget.prop.calendarData = new DavProp.CalendarData();
multiget.hrefs = new ArrayList<DavHref>(names.length); multiget.hrefs = new ArrayList<DavHref>(names.length);
for (String name : names) for (String name : names)

View File

@ -25,19 +25,19 @@ public class DavProp {
/* RFC 4918 WebDAV */ /* RFC 4918 WebDAV */
@Element(required=false) @Element(required=false)
DavPropResourceType resourcetype; ResourceType resourcetype;
@Element(required=false) @Element(required=false)
DavPropDisplayName displayname; DisplayName displayname;
@Element(required=false) @Element(required=false)
DavPropGetCTag getctag; GetCTag getctag;
@Element(required=false) @Element(required=false)
DavPropGetETag getetag; GetETag getetag;
@Root(strict=false) @Root(strict=false)
public static class DavPropResourceType { public static class ResourceType {
@Element(required=false) @Element(required=false)
@Getter private Addressbook addressbook; @Getter private Addressbook addressbook;
@Element(required=false) @Element(required=false)
@ -50,18 +50,18 @@ public class DavProp {
public static class Calendar { } public static class Calendar { }
} }
public static class DavPropDisplayName { public static class DisplayName {
@Text(required=false) @Text(required=false)
@Getter private String displayName; @Getter private String displayName;
} }
@Namespace(prefix="CS",reference="http://calendarserver.org/ns/") @Namespace(prefix="CS",reference="http://calendarserver.org/ns/")
public static class DavPropGetCTag { public static class GetCTag {
@Text(required=false) @Text(required=false)
@Getter private String CTag; @Getter private String CTag;
} }
public static class DavPropGetETag { public static class GetETag {
@Text(required=false) @Text(required=false)
@Getter private String ETag; @Getter private String ETag;
} }
@ -70,9 +70,9 @@ public class DavProp {
/* RFC 5397 WebDAV Current Principal Extension */ /* RFC 5397 WebDAV Current Principal Extension */
@Element(required=false,name="current-user-principal") @Element(required=false,name="current-user-principal")
DavCurrentUserPrincipal currentUserPrincipal; CurrentUserPrincipal currentUserPrincipal;
public static class DavCurrentUserPrincipal { public static class CurrentUserPrincipal {
@Element(required=false) @Element(required=false)
@Getter private DavHref href; @Getter private DavHref href;
} }
@ -81,9 +81,9 @@ public class DavProp {
/* RFC 3744 WebDAV Access Control Protocol */ /* RFC 3744 WebDAV Access Control Protocol */
@ElementList(required=false,name="current-user-privilege-set",entry="privilege") @ElementList(required=false,name="current-user-privilege-set",entry="privilege")
List<DavPropPrivilege> currentUserPrivilegeSet; List<Privilege> currentUserPrivilegeSet;
public static class DavPropPrivilege { public static class Privilege {
@Element(required=false) @Element(required=false)
@Getter private PrivAll all; @Getter private PrivAll all;
@ -111,84 +111,97 @@ public class DavProp {
/* RFC 4791 CalDAV, RFC 6352 CardDAV */ /* RFC 4791 CalDAV, RFC 6352 CardDAV */
@Element(required=false,name="addressbook-home-set") @Element(required=false,name="addressbook-home-set")
DavAddressbookHomeSet addressbookHomeSet; AddressbookHomeSet addressbookHomeSet;
@Element(required=false,name="calendar-home-set") @Element(required=false,name="calendar-home-set")
DavCalendarHomeSet calendarHomeSet; CalendarHomeSet calendarHomeSet;
@Element(required=false,name="addressbook-description") @Element(required=false,name="addressbook-description")
DavPropAddressbookDescription addressbookDescription; AddressbookDescription addressbookDescription;
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
@ElementList(required=false,name="supported-address-data",entry="address-data-type")
List<AddressDataType> supportedAddressData;
@Element(required=false,name="calendar-description") @Element(required=false,name="calendar-description")
DavPropCalendarDescription calendarDescription; CalendarDescription calendarDescription;
@Element(required=false,name="calendar-color") @Element(required=false,name="calendar-color")
DavPropCalendarColor calendarColor; CalendarColor calendarColor;
@Element(required=false,name="calendar-timezone") @Element(required=false,name="calendar-timezone")
DavPropCalendarTimezone calendarTimezone; CalendarTimezone calendarTimezone;
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
@ElementList(required=false,name="supported-calendar-component-set",entry="comp") @ElementList(required=false,name="supported-calendar-component-set",entry="comp")
List<DavPropComp> supportedCalendarComponentSet; List<Comp> supportedCalendarComponentSet;
@Element(name="address-data",required=false) @Element(name="address-data",required=false)
DavPropAddressData addressData; AddressData addressData;
@Element(name="calendar-data",required=false) @Element(name="calendar-data",required=false)
DavPropCalendarData calendarData; CalendarData calendarData;
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
public static class DavAddressbookHomeSet { public static class AddressbookHomeSet {
@Element(required=false) @Element(required=false)
@Getter private DavHref href; @Getter private DavHref href;
} }
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
public static class DavCalendarHomeSet { public static class CalendarHomeSet {
@Element(required=false) @Element(required=false)
@Getter private DavHref href; @Getter private DavHref href;
} }
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
public static class DavPropAddressbookDescription { public static class AddressbookDescription {
@Text(required=false) @Text(required=false)
@Getter private String description; @Getter private String description;
} }
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
public static class AddressDataType {
@Attribute(name="content-type")
@Getter private String contentType;
@Attribute
@Getter private String version;
}
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
public static class DavPropCalendarDescription { public static class CalendarDescription {
@Text(required=false) @Text(required=false)
@Getter private String description; @Getter private String description;
} }
@Namespace(prefix="A",reference="http://apple.com/ns/ical/") @Namespace(prefix="A",reference="http://apple.com/ns/ical/")
public static class DavPropCalendarColor { public static class CalendarColor {
@Text(required=false) @Text(required=false)
@Getter private String color; @Getter private String color;
} }
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
public static class DavPropCalendarTimezone { public static class CalendarTimezone {
@Text(required=false) @Text(required=false)
@Getter private String timezone; @Getter private String timezone;
} }
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
public static class DavPropComp { public static class Comp {
@Attribute @Attribute
@Getter String name; @Getter String name;
} }
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
public static class DavPropAddressData { public static class AddressData {
@Text(required=false) @Text(required=false)
@Getter String vcard; @Getter String vcard;
} }
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
public static class DavPropCalendarData { public static class CalendarData {
@Text(required=false) @Text(required=false)
@Getter String ical; @Getter String ical;
} }

View File

@ -26,7 +26,8 @@ public class HttpPropfind extends HttpEntityEnclosingRequestBase {
public enum Mode { public enum Mode {
CURRENT_USER_PRINCIPAL, CURRENT_USER_PRINCIPAL,
HOME_SETS, HOME_SETS,
MEMBERS_COLLECTIONS, CARDDAV_COLLECTIONS,
CALDAV_COLLECTIONS,
COLLECTION_CTAG, COLLECTION_CTAG,
MEMBERS_ETAG MEMBERS_ETAG
} }
@ -47,30 +48,37 @@ public class HttpPropfind extends HttpEntityEnclosingRequestBase {
int depth = 0; int depth = 0;
switch (mode) { switch (mode) {
case CURRENT_USER_PRINCIPAL: case CURRENT_USER_PRINCIPAL:
propfind.prop.currentUserPrincipal = new DavProp.DavCurrentUserPrincipal(); propfind.prop.currentUserPrincipal = new DavProp.CurrentUserPrincipal();
break; break;
case HOME_SETS: case HOME_SETS:
propfind.prop.addressbookHomeSet = new DavProp.DavAddressbookHomeSet(); propfind.prop.addressbookHomeSet = new DavProp.AddressbookHomeSet();
propfind.prop.calendarHomeSet = new DavProp.DavCalendarHomeSet(); propfind.prop.calendarHomeSet = new DavProp.CalendarHomeSet();
break; break;
case MEMBERS_COLLECTIONS: case CARDDAV_COLLECTIONS:
depth = 1; depth = 1;
propfind.prop.displayname = new DavProp.DavPropDisplayName(); propfind.prop.displayname = new DavProp.DisplayName();
propfind.prop.resourcetype = new DavProp.DavPropResourceType(); propfind.prop.resourcetype = new DavProp.ResourceType();
propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.DavPropPrivilege>(); propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.Privilege>();
propfind.prop.addressbookDescription = new DavProp.DavPropAddressbookDescription(); propfind.prop.addressbookDescription = new DavProp.AddressbookDescription();
propfind.prop.calendarDescription = new DavProp.DavPropCalendarDescription(); propfind.prop.supportedAddressData = new LinkedList<DavProp.AddressDataType>();
propfind.prop.calendarColor = new DavProp.DavPropCalendarColor(); break;
propfind.prop.calendarTimezone = new DavProp.DavPropCalendarTimezone(); case CALDAV_COLLECTIONS:
propfind.prop.supportedCalendarComponentSet = new LinkedList<DavProp.DavPropComp>(); depth = 1;
propfind.prop.displayname = new DavProp.DisplayName();
propfind.prop.resourcetype = new DavProp.ResourceType();
propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.Privilege>();
propfind.prop.calendarDescription = new DavProp.CalendarDescription();
propfind.prop.calendarColor = new DavProp.CalendarColor();
propfind.prop.calendarTimezone = new DavProp.CalendarTimezone();
propfind.prop.supportedCalendarComponentSet = new LinkedList<DavProp.Comp>();
break; break;
case COLLECTION_CTAG: case COLLECTION_CTAG:
propfind.prop.getctag = new DavProp.DavPropGetCTag(); propfind.prop.getctag = new DavProp.GetCTag();
break; break;
case MEMBERS_ETAG: case MEMBERS_ETAG:
depth = 1; depth = 1;
propfind.prop.getctag = new DavProp.DavPropGetCTag(); propfind.prop.getctag = new DavProp.GetCTag();
propfind.prop.getetag = new DavProp.DavPropGetETag(); propfind.prop.getetag = new DavProp.GetETag();
break; break;
} }

View File

@ -30,7 +30,7 @@ import org.simpleframework.xml.core.Persister;
import android.util.Log; import android.util.Log;
import at.bitfire.davdroid.URIUtils; import at.bitfire.davdroid.URIUtils;
import at.bitfire.davdroid.resource.Event; import at.bitfire.davdroid.resource.Event;
import at.bitfire.davdroid.webdav.DavProp.DavPropComp; import at.bitfire.davdroid.webdav.DavProp.Comp;
import ch.boye.httpclientandroidlib.Header; import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity; import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpHost; import ch.boye.httpclientandroidlib.HttpHost;
@ -53,6 +53,7 @@ import ch.boye.httpclientandroidlib.impl.client.BasicCredentialsProvider;
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
import ch.boye.httpclientandroidlib.message.BasicLineParser; import ch.boye.httpclientandroidlib.message.BasicLineParser;
import ch.boye.httpclientandroidlib.util.EntityUtils; import ch.boye.httpclientandroidlib.util.EntityUtils;
import ezvcard.VCardVersion;
/** /**
@ -64,14 +65,12 @@ public class WebDavResource {
private static final String TAG = "davdroid.WebDavResource"; private static final String TAG = "davdroid.WebDavResource";
public enum Property { public enum Property {
CURRENT_USER_PRINCIPAL, CURRENT_USER_PRINCIPAL, // resource detection
READ_ONLY,
DISPLAY_NAME, DESCRIPTION, COLOR,
TIMEZONE, SUPPORTED_COMPONENTS,
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET, ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
IS_ADDRESSBOOK, IS_CALENDAR, CONTENT_TYPE, READ_ONLY, // WebDAV (common)
CTAG, ETAG, DISPLAY_NAME, DESCRIPTION, CTAG, ETAG,
CONTENT_TYPE IS_CALENDAR, COLOR, TIMEZONE, // CalDAV
IS_ADDRESSBOOK, VCARD_VERSION // CardDAV
} }
public enum PutMode { public enum PutMode {
ADD_DONT_OVERWRITE, ADD_DONT_OVERWRITE,
@ -189,6 +188,22 @@ public class WebDavResource {
return properties.get(Property.CURRENT_USER_PRINCIPAL); return properties.get(Property.CURRENT_USER_PRINCIPAL);
} }
public String getAddressbookHomeSet() {
return properties.get(Property.ADDRESSBOOK_HOMESET);
}
public String getCalendarHomeSet() {
return properties.get(Property.CALENDAR_HOMESET);
}
public String getContentType() {
return properties.get(Property.CONTENT_TYPE);
}
public void setContentType(String mimeType) {
properties.put(Property.CONTENT_TYPE, mimeType);
}
public boolean isReadOnly() { public boolean isReadOnly() {
return properties.containsKey(Property.READ_ONLY); return properties.containsKey(Property.READ_ONLY);
} }
@ -201,22 +216,6 @@ public class WebDavResource {
return properties.get(Property.DESCRIPTION); return properties.get(Property.DESCRIPTION);
} }
public String getColor() {
return properties.get(Property.COLOR);
}
public String getTimezone() {
return properties.get(Property.TIMEZONE);
}
public String getAddressbookHomeSet() {
return properties.get(Property.ADDRESSBOOK_HOMESET);
}
public String getCalendarHomeSet() {
return properties.get(Property.CALENDAR_HOMESET);
}
public String getCTag() { public String getCTag() {
return properties.get(Property.CTAG); return properties.get(Property.CTAG);
} }
@ -228,20 +227,25 @@ public class WebDavResource {
return properties.get(Property.ETAG); return properties.get(Property.ETAG);
} }
public String getContentType() { public boolean isCalendar() {
return properties.get(Property.CONTENT_TYPE); return properties.containsKey(Property.IS_CALENDAR);
} }
public void setContentType(String mimeType) { public String getColor() {
properties.put(Property.CONTENT_TYPE, mimeType); return properties.get(Property.COLOR);
}
public String getTimezone() {
return properties.get(Property.TIMEZONE);
} }
public boolean isAddressBook() { public boolean isAddressBook() {
return properties.containsKey(Property.IS_ADDRESSBOOK); return properties.containsKey(Property.IS_ADDRESSBOOK);
} }
public boolean isCalendar() { public VCardVersion getVCardVersion() {
return properties.containsKey(Property.IS_CALENDAR); String versionStr = properties.get(Property.VCARD_VERSION);
return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null;
} }
@ -500,7 +504,7 @@ public class WebDavResource {
mayUnbind = false, mayUnbind = false,
mayWrite = false, mayWrite = false,
mayWriteContent = false; mayWriteContent = false;
for (DavProp.DavPropPrivilege privilege : prop.currentUserPrivilegeSet) { for (DavProp.Privilege privilege : prop.currentUserPrivilegeSet) {
if (privilege.getAll() != null) mayAll = true; if (privilege.getAll() != null) mayAll = true;
if (privilege.getBind() != null) mayBind = true; if (privilege.getBind() != null) mayBind = true;
if (privilege.getUnbind() != null) mayUnbind = true; if (privilege.getUnbind() != null) mayUnbind = true;
@ -514,20 +518,26 @@ public class WebDavResource {
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null) if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
properties.put(Property.ADDRESSBOOK_HOMESET, prop.addressbookHomeSet.getHref().href); properties.put(Property.ADDRESSBOOK_HOMESET, prop.addressbookHomeSet.getHref().href);
if (singlePropstat.prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null) if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
properties.put(Property.CALENDAR_HOMESET, prop.calendarHomeSet.getHref().href); properties.put(Property.CALENDAR_HOMESET, prop.calendarHomeSet.getHref().href);
if (prop.displayname != null) if (prop.displayname != null)
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName()); properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
if (prop.resourcetype != null) { if (prop.resourcetype != null) {
if (prop.resourcetype.getAddressbook() != null) { if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
properties.put(Property.IS_ADDRESSBOOK, "1"); properties.put(Property.IS_ADDRESSBOOK, "1");
if (prop.addressbookDescription != null) if (prop.addressbookDescription != null)
properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription()); properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription());
if (prop.supportedAddressData != null)
for (DavProp.AddressDataType dataType : prop.supportedAddressData)
if ("text/vcard".equalsIgnoreCase(dataType.getContentType()))
// ignore "3.0" as it MUST be supported anyway
if ("4.0".equals(dataType.getVersion()))
properties.put(Property.VCARD_VERSION, VCardVersion.V4_0.getVersion());
} }
if (prop.resourcetype.getCalendar() != null) { if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes
properties.put(Property.IS_CALENDAR, "1"); properties.put(Property.IS_CALENDAR, "1");
if (prop.calendarDescription != null) if (prop.calendarDescription != null)
@ -541,7 +551,7 @@ public class WebDavResource {
if (prop.supportedCalendarComponentSet != null) { if (prop.supportedCalendarComponentSet != null) {
referenced.supportedComponents = new LinkedList<String>(); referenced.supportedComponents = new LinkedList<String>();
for (DavPropComp component : prop.supportedCalendarComponentSet) for (Comp component : prop.supportedCalendarComponentSet)
referenced.supportedComponents.add(component.getName()); referenced.supportedComponents.add(component.getName());
} }
} }

View File

@ -10,7 +10,7 @@ exports.getBodyParts = function(conf) {
new RoboHydraHeadDAV({ new RoboHydraHeadDAV({
path: "/dav/principals/users/test", path: "/dav/principals/users/test",
handler: function(req,res,next) { handler: function(req,res,next) {
if (req.method == "PROPFIND" && req.rawBody.toString().match(/home-set/)) { if (req.method == "PROPFIND" && req.rawBody.toString().match(/home-?set/)) {
res.statusCode = 207; res.statusCode = 207;
res.write('\<?xml version="1.0" encoding="utf-8" ?>\ res.write('\<?xml version="1.0" encoding="utf-8" ?>\
<multistatus xmlns="DAV:">\ <multistatus xmlns="DAV:">\
@ -56,7 +56,7 @@ exports.getBodyParts = function(conf) {
</propstat>\ </propstat>\
</response>\ </response>\
<response>\ <response>\
<href>/dav/addressbooks/test/default.vcf/</href>\ <href>/dav/addressbooks/test/default-v4.vcf/</href>\
<propstat>\ <propstat>\
<prop xmlns:CARD="urn:ietf:params:xml:ns:carddav">\ <prop xmlns:CARD="urn:ietf:params:xml:ns:carddav">\
<resourcetype>\ <resourcetype>\
@ -64,6 +64,10 @@ exports.getBodyParts = function(conf) {
<CARD:addressbook/>\ <CARD:addressbook/>\
</resourcetype>\ </resourcetype>\
<CARD:addressbook-description>Default Address Book</CARD:addressbook-description>\ <CARD:addressbook-description>Default Address Book</CARD:addressbook-description>\
<CARD:supported-address-data>\
<CARD:address-data-type content-type="text/vcard" version="3.0" />\
<CARD:address-data-type content-type="text/vcard" version="4.0" />\
</CARD:supported-address-data>\
</prop>\ </prop>\
<status>HTTP/1.1 200 OK</status>\ <status>HTTP/1.1 200 OK</status>\
</propstat>\ </propstat>\
@ -78,7 +82,7 @@ exports.getBodyParts = function(conf) {
new RoboHydraHeadDAV({ new RoboHydraHeadDAV({
path: "/dav/calendars/test/", path: "/dav/calendars/test/",
handler: function(req,res,next) { handler: function(req,res,next) {
if (req.method == "PROPFIND" && req.rawBody.toString().match(/addressbook-description/)) { if (req.method == "PROPFIND" && req.rawBody.toString().match(/calendar-description/)) {
res.statusCode = 207; res.statusCode = 207;
res.write('\<?xml version="1.0" encoding="utf-8" ?>\ res.write('\<?xml version="1.0" encoding="utf-8" ?>\
<multistatus xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav">\ <multistatus xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav">\

View File

@ -2,6 +2,7 @@ package at.bitfire.davdroid.syncadapter;
import java.util.List; import java.util.List;
import ezvcard.VCardVersion;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import at.bitfire.davdroid.syncadapter.ServerInfo.ResourceInfo; import at.bitfire.davdroid.syncadapter.ServerInfo.ResourceInfo;
import at.bitfire.davdroid.test.Constants; import at.bitfire.davdroid.test.Constants;
@ -18,6 +19,7 @@ public class DavResourceFinderTest extends InstrumentationTestCase {
assertEquals(1, collections.size()); assertEquals(1, collections.size());
assertEquals("Default Address Book", collections.get(0).getDescription()); assertEquals("Default Address Book", collections.get(0).getDescription());
assertEquals(VCardVersion.V4_0, collections.get(0).getVCardVersion());
// CalDAV // CalDAV
assertTrue(info.isCalDAV()); assertTrue(info.isCalDAV());

View File

@ -15,7 +15,6 @@ import net.fortuna.ical4j.data.ParserException;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import at.bitfire.davdroid.resource.Contact; import at.bitfire.davdroid.resource.Contact;
import ezvcard.VCardException;
import ezvcard.property.Impp; import ezvcard.property.Impp;
public class ContactTest extends InstrumentationTestCase { public class ContactTest extends InstrumentationTestCase {
@ -25,7 +24,7 @@ public class ContactTest extends InstrumentationTestCase {
assetMgr = getInstrumentation().getContext().getResources().getAssets(); assetMgr = getInstrumentation().getContext().getResources().getAssets();
} }
public void testIMPP() throws VCardException, IOException { public void testIMPP() throws IOException {
Contact c = parseVCard("impp.vcf"); Contact c = parseVCard("impp.vcf");
assertEquals("test mctest", c.getDisplayName()); assertEquals("test mctest", c.getDisplayName());
@ -52,7 +51,7 @@ public class ContactTest extends InstrumentationTestCase {
} }
private Contact parseVCard(String fileName) throws VCardException, IOException { private Contact parseVCard(String fileName) throws IOException {
@Cleanup InputStream in = assetMgr.open(fileName, AssetManager.ACCESS_STREAMING); @Cleanup InputStream in = assetMgr.open(fileName, AssetManager.ACCESS_STREAMING);
Contact c = new Contact(fileName, null); Contact c = new Contact(fileName, null);

View File

@ -109,10 +109,10 @@ public class WebDavResourceTest extends InstrumentationTestCase {
public void testPropfindAddressBooks() throws IOException, HttpException, DavException { public void testPropfindAddressBooks() throws IOException, HttpException, DavException {
WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test"); WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test");
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS); dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
assertEquals(2, dav.getMembers().size()); assertEquals(2, dav.getMembers().size());
for (WebDavResource member : dav.getMembers()) { for (WebDavResource member : dav.getMembers()) {
if (member.getName().equals("default.vcf")) if (member.getName().equals("default-v4.vcf"))
assertTrue(member.isAddressBook()); assertTrue(member.isAddressBook());
else else
assertFalse(member.isAddressBook()); assertFalse(member.isAddressBook());
@ -122,7 +122,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
public void testPropfindCalendars() throws IOException, HttpException, DavException { public void testPropfindCalendars() throws IOException, HttpException, DavException {
WebDavResource dav = new WebDavResource(davCollection, "calendars/test"); WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS); dav.propfind(HttpPropfind.Mode.CALDAV_COLLECTIONS);
assertEquals(3, dav.getMembers().size()); assertEquals(3, dav.getMembers().size());
assertEquals("0xFF00FF", dav.getMembers().get(2).getColor()); assertEquals("0xFF00FF", dav.getMembers().get(2).getColor());
for (WebDavResource member : dav.getMembers()) { for (WebDavResource member : dav.getMembers()) {
@ -217,7 +217,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
public void testInvalidURLs() throws IOException, HttpException, DavException { public void testInvalidURLs() throws IOException, HttpException, DavException {
WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/"); WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/");
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS); dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
List<WebDavResource> members = dav.getMembers(); List<WebDavResource> members = dav.getMembers();
assertEquals(2, members.size()); assertEquals(2, members.size());
assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString()); assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());