mirror of
https://github.com/etesync/android
synced 2024-11-26 01:48:34 +00:00
Refactoring
* WebDavResource: properties in separate subclass * improve time zone handling * always provide task list color
This commit is contained in:
parent
5ec4dbb9e7
commit
1c461e9d13
@ -10,13 +10,9 @@ package at.bitfire.davdroid;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateList;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||
import net.fortuna.ical4j.model.property.ExDate;
|
||||
import net.fortuna.ical4j.model.property.RDate;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class ContactTest extends InstrumentationTestCase {
|
||||
Contact c = new Contact("test.vcf", null);
|
||||
|
||||
// should generate VCard 3.0 by default
|
||||
assertEquals("text/vcard", c.getMimeType());
|
||||
assertEquals("text/vcard;charset=UTF-8", c.getMimeType());
|
||||
assertTrue(new String(c.toEntity().toByteArray()).contains("VERSION:3.0"));
|
||||
|
||||
// now let's generate VCard 4.0
|
||||
|
@ -9,16 +9,23 @@ package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.text.format.Time;
|
||||
|
||||
import net.fortuna.ical4j.data.ParserException;
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
import net.fortuna.ical4j.util.TimeZones;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import lombok.Cleanup;
|
||||
|
||||
public class EventTest extends InstrumentationTestCase {
|
||||
protected final TimeZone tzVienna = DateUtils.tzRegistry.getTimeZone("Europe/Vienna");
|
||||
|
||||
AssetManager assetMgr;
|
||||
|
||||
Event eOnThatDay, eAllDay1Day, eAllDay10Days, eAllDay0Sec;
|
||||
@ -33,6 +40,21 @@ public class EventTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
|
||||
public void testGetTzID() throws Exception {
|
||||
// DATE (without time)
|
||||
assertEquals(TimeZones.UTC_ID, Event.getTzId(new DtStart(new Date("20150101"))));
|
||||
|
||||
// DATE-TIME without time zone (floating time): should be UTC (because net.fortuna.ical4j.timezone.date.floating=false)
|
||||
assertEquals(TimeZones.UTC_ID, Event.getTzId(new DtStart(new DateTime("20150101T000000"))));
|
||||
|
||||
// DATE-TIME without time zone (UTC)
|
||||
assertEquals(TimeZones.UTC_ID, Event.getTzId(new DtStart(new DateTime(1438607288000L))));
|
||||
|
||||
// DATE-TIME with time zone
|
||||
assertEquals(tzVienna.getID(), Event.getTzId(new DtStart(new DateTime("20150101T000000", tzVienna))));
|
||||
}
|
||||
|
||||
|
||||
public void testRecurringWithException() throws Exception {
|
||||
Event event = parseCalendar("recurring-with-exception1.ics");
|
||||
assertTrue(event.isAllDay());
|
||||
@ -55,75 +77,31 @@ public class EventTest extends InstrumentationTestCase {
|
||||
public void testStartEndTimesAllDay() throws IOException, ParserException {
|
||||
// event with start date only
|
||||
assertEquals(868838400000L, eOnThatDay.getDtStartInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtStartTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eOnThatDay.getDtStartTzID());
|
||||
// DTEND missing in VEVENT, must have been set to DTSTART+1 day
|
||||
assertEquals(868838400000L + 86400000, eOnThatDay.getDtEndInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtEndTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eOnThatDay.getDtEndTzID());
|
||||
|
||||
// event with start+end date for all-day event (one day)
|
||||
assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtStartTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay1Day.getDtStartTzID());
|
||||
assertEquals(868838400000L + 86400000, eAllDay1Day.getDtEndInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtEndTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay1Day.getDtEndTzID());
|
||||
|
||||
// event with start+end date for all-day event (ten days)
|
||||
assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtStartTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay10Days.getDtStartTzID());
|
||||
assertEquals(868838400000L + 10*86400000, eAllDay10Days.getDtEndInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtEndTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay10Days.getDtEndTzID());
|
||||
|
||||
// event with start+end date on some day (invalid 0 sec-event)
|
||||
assertEquals(868838400000L, eAllDay0Sec.getDtStartInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtStartTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay0Sec.getDtStartTzID());
|
||||
// DTEND invalid in VEVENT, must have been set to DTSTART+1 day
|
||||
assertEquals(868838400000L + 86400000, eAllDay0Sec.getDtEndInMillis());
|
||||
assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtEndTzID());
|
||||
assertEquals(TimeZones.UTC_ID, eAllDay0Sec.getDtEndTzID());
|
||||
}
|
||||
|
||||
public void testTimezoneDefToTzId() {
|
||||
// test valid definition
|
||||
final String VTIMEZONE_SAMPLE = // taken from RFC 4791, 5.2.2. CALDAV:calendar-timezone Property
|
||||
"BEGIN:VCALENDAR\n" +
|
||||
"PRODID:-//Example Corp.//CalDAV Client//EN\n" +
|
||||
"VERSION:2.0\n" +
|
||||
"BEGIN:VTIMEZONE\n" +
|
||||
"TZID:US-Eastern\n" +
|
||||
"LAST-MODIFIED:19870101T000000Z\n" +
|
||||
"BEGIN:STANDARD\n" +
|
||||
"DTSTART:19671029T020000\n" +
|
||||
"RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" +
|
||||
"TZOFFSETFROM:-0400\n" +
|
||||
"TZOFFSETTO:-0500\n" +
|
||||
"TZNAME:Eastern Standard Time (US & Canada)\n" +
|
||||
"END:STANDARD\n" +
|
||||
"BEGIN:DAYLIGHT\n" +
|
||||
"DTSTART:19870405T020000\n" +
|
||||
"RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" +
|
||||
"TZOFFSETFROM:-0500\n" +
|
||||
"TZOFFSETTO:-0400\n" +
|
||||
"TZNAME:Eastern Daylight Time (US & Canada)\n" +
|
||||
"END:DAYLIGHT\n" +
|
||||
"END:VTIMEZONE\n" +
|
||||
"END:VCALENDAR";
|
||||
assertEquals("US-Eastern", Event.TimezoneDefToTzId(VTIMEZONE_SAMPLE));
|
||||
|
||||
// test null value
|
||||
try {
|
||||
Event.TimezoneDefToTzId(null);
|
||||
fail();
|
||||
} catch(IllegalArgumentException e) {
|
||||
assert(true);
|
||||
}
|
||||
|
||||
// test invalid time zone
|
||||
try {
|
||||
Event.TimezoneDefToTzId("/* invalid content */");
|
||||
fail();
|
||||
} catch(IllegalArgumentException e) {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnfolding() throws IOException, InvalidResourceException {
|
||||
Event e = parseCalendar("two-line-description-without-crlf.ics");
|
||||
assertEquals("http://www.tgbornheim.de/index.php?sessionid=&page=&id=&sportcentergroup=&day=6", e.getDescription());
|
||||
|
@ -25,25 +25,14 @@ import android.test.InstrumentationTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateList;
|
||||
import net.fortuna.ical4j.model.Dur;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
import net.fortuna.ical4j.model.component.VAlarm;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||
import net.fortuna.ical4j.model.property.DtEnd;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
import net.fortuna.ical4j.model.property.ExDate;
|
||||
import net.fortuna.ical4j.model.property.RDate;
|
||||
import net.fortuna.ical4j.util.Dates;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import lombok.Cleanup;
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.fortuna.ical4j.data.CalendarBuilder;
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
|
||||
public class iCalendarTest extends TestCase {
|
||||
protected final TimeZone tzVienna = DateUtils.tzRegistry.getTimeZone("Europe/Vienna");
|
||||
|
||||
public void testTimezoneDefToTzId() {
|
||||
// test valid definition
|
||||
assertEquals("US-Eastern", Event.TimezoneDefToTzId("BEGIN:VCALENDAR\n" +
|
||||
"PRODID:-//Example Corp.//CalDAV Client//EN\n" +
|
||||
"VERSION:2.0\n" +
|
||||
"BEGIN:VTIMEZONE\n" +
|
||||
"TZID:US-Eastern\n" +
|
||||
"LAST-MODIFIED:19870101T000000Z\n" +
|
||||
"BEGIN:STANDARD\n" +
|
||||
"DTSTART:19671029T020000\n" +
|
||||
"RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" +
|
||||
"TZOFFSETFROM:-0400\n" +
|
||||
"TZOFFSETTO:-0500\n" +
|
||||
"TZNAME:Eastern Standard Time (US & Canada)\n" +
|
||||
"END:STANDARD\n" +
|
||||
"BEGIN:DAYLIGHT\n" +
|
||||
"DTSTART:19870405T020000\n" +
|
||||
"RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" +
|
||||
"TZOFFSETFROM:-0500\n" +
|
||||
"TZOFFSETTO:-0400\n" +
|
||||
"TZNAME:Eastern Daylight Time (US & Canada)\n" +
|
||||
"END:DAYLIGHT\n" +
|
||||
"END:VTIMEZONE\n" +
|
||||
"END:VCALENDAR"));
|
||||
|
||||
// test invalid time zone
|
||||
assertNull(iCalendar.TimezoneDefToTzId("/* invalid content */"));
|
||||
|
||||
// test time zone without TZID
|
||||
assertNull(iCalendar.TimezoneDefToTzId("BEGIN:VCALENDAR\n" +
|
||||
"PRODID:-//Inverse inc./SOGo 2.2.10//EN\n" +
|
||||
"VERSION:2.0\n" +
|
||||
"END:VCALENDAR"));
|
||||
}
|
||||
|
||||
public void testValidateTimeZone() throws Exception {
|
||||
assertNotNull(tzVienna);
|
||||
|
||||
// date (no time zone) should be ignored
|
||||
DtStart date = new DtStart(new Date("20150101"));
|
||||
iCalendar.validateTimeZone(date);
|
||||
assertNull(date.getTimeZone());
|
||||
|
||||
// date-time (Europe/Vienna) should be unchanged
|
||||
DtStart dtStart = new DtStart("20150101", tzVienna);
|
||||
iCalendar.validateTimeZone(dtStart);
|
||||
assertEquals(tzVienna, dtStart.getTimeZone());
|
||||
|
||||
// time zone that is not available on Android systems should be changed to system default
|
||||
CalendarBuilder builder = new CalendarBuilder();
|
||||
net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader("BEGIN:VCALENDAR\n" +
|
||||
"BEGIN:VTIMEZONE\n" +
|
||||
"TZID:CustomTime\n" +
|
||||
"BEGIN:STANDARD\n" +
|
||||
"TZOFFSETFROM:-0400\n" +
|
||||
"TZOFFSETTO:-0500\n" +
|
||||
"DTSTART:19600101T000000\n" +
|
||||
"END:STANDARD\n" +
|
||||
"END:VTIMEZONE\n" +
|
||||
"END:VCALENDAR"));
|
||||
final TimeZone tzCustom = new TimeZone((VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE));
|
||||
dtStart = new DtStart("20150101T000000", tzCustom);
|
||||
iCalendar.validateTimeZone(dtStart);
|
||||
|
||||
final TimeZone tzDefault = DateUtils.tzRegistry.getTimeZone(java.util.TimeZone.getDefault().getID());
|
||||
assertNotNull(tzDefault);
|
||||
assertEquals(tzDefault.getID(), dtStart.getTimeZone().getID());
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,6 @@ import at.bitfire.davdroid.TestConstants;
|
||||
import at.bitfire.davdroid.resource.DavResourceFinder;
|
||||
import at.bitfire.davdroid.resource.ServerInfo;
|
||||
import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
|
||||
import ezvcard.VCardVersion;
|
||||
|
||||
public class DavResourceFinderTest extends InstrumentationTestCase {
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
|
||||
public void testPropfindCurrentUserPrincipal() throws Exception {
|
||||
davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
||||
assertEquals(new URI("/dav/principals/users/test"), davCollection.getCurrentUserPrincipal());
|
||||
assertEquals(new URI("/dav/principals/users/test"), davCollection.getProperties().getCurrentUserPrincipal());
|
||||
|
||||
WebDavResource simpleFile = new WebDavResource(davAssets, "test.random");
|
||||
try {
|
||||
@ -82,14 +82,14 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
|
||||
} catch(DavException ex) {
|
||||
}
|
||||
assertNull(simpleFile.getCurrentUserPrincipal());
|
||||
assertNull(simpleFile.getProperties().getCurrentUserPrincipal());
|
||||
}
|
||||
|
||||
public void testPropfindHomeSets() throws Exception {
|
||||
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
||||
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
||||
assertEquals(new URI("/dav/addressbooks/test/"), dav.getAddressbookHomeSet());
|
||||
assertEquals(new URI("/dav/calendars/test/"), dav.getCalendarHomeSet());
|
||||
assertEquals(new URI("/dav/addressbooks/test/"), dav.getProperties().getAddressbookHomeSet());
|
||||
assertEquals(new URI("/dav/calendars/test/"), dav.getProperties().getCalendarHomeSet());
|
||||
}
|
||||
|
||||
public void testPropfindAddressBooks() throws Exception {
|
||||
@ -103,34 +103,45 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
WebDavResource ab = dav.getMembers().get(0);
|
||||
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/useless-member"), ab.getLocation());
|
||||
assertEquals("useless-member", ab.getName());
|
||||
assertFalse(ab.isAddressBook());
|
||||
assertFalse(ab.getProperties().isAddressBook());
|
||||
|
||||
// the second one is an address book (referenced by relative URI)
|
||||
ab = dav.getMembers().get(1);
|
||||
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/default.vcf/"), ab.getLocation());
|
||||
assertEquals("default.vcf", ab.getName());
|
||||
assertTrue(ab.isAddressBook());
|
||||
assertTrue(ab.getProperties().isAddressBook());
|
||||
|
||||
// the third one is an address book (referenced by an absolute URI)
|
||||
ab = dav.getMembers().get(2);
|
||||
assertEquals(new URI("https://my.server/absolute:uri/my-address-book/"), ab.getLocation());
|
||||
assertEquals("my-address-book", ab.getName());
|
||||
assertTrue(ab.isAddressBook());
|
||||
assertTrue(ab.getProperties().isAddressBook());
|
||||
}
|
||||
|
||||
public void testPropfindCalendars() throws Exception {
|
||||
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
||||
dav.propfind(Mode.CALDAV_COLLECTIONS);
|
||||
assertEquals(3, dav.getMembers().size());
|
||||
assertEquals("0xFF00FF", dav.getMembers().get(2).getColor());
|
||||
assertEquals(new Integer(0xFFFF00FF), dav.getMembers().get(2).getProperties().getColor());
|
||||
for (WebDavResource member : dav.getMembers()) {
|
||||
if (member.getName().contains(".ics"))
|
||||
assertTrue(member.isCalendar());
|
||||
assertTrue(member.getProperties().isCalendar());
|
||||
else
|
||||
assertFalse(member.isCalendar());
|
||||
assertFalse(member.isAddressBook());
|
||||
assertFalse(member.getProperties().isCalendar());
|
||||
assertFalse(member.getProperties().isAddressBook());
|
||||
}
|
||||
}
|
||||
|
||||
public void testPropfindCollectionProperties() throws Exception {
|
||||
WebDavResource dav = new WebDavResource(davCollection, "propfind-collection-properties");
|
||||
dav.propfind(Mode.COLLECTION_PROPERTIES);
|
||||
assertTrue(dav.members.isEmpty());
|
||||
assertTrue(dav.properties.isCollection);
|
||||
assertTrue(dav.properties.isAddressBook);
|
||||
assertNull(dav.properties.displayName);
|
||||
assertNull(dav.properties.color);
|
||||
assertEquals(VCardVersion.V4_0, dav.properties.supportedVCardVersion);
|
||||
}
|
||||
|
||||
public void testPropfindTrailingSlashes() throws Exception {
|
||||
final String principalOK = "/principals/ok";
|
||||
@ -145,7 +156,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
for (String path : requestPaths) {
|
||||
WebDavResource davSlash = new WebDavResource(davCollection, new URI(path));
|
||||
davSlash.propfind(Mode.CARDDAV_COLLECTIONS);
|
||||
assertEquals(new URI(principalOK), davSlash.getCurrentUserPrincipal());
|
||||
assertEquals(new URI(principalOK), davSlash.getProperties().getCurrentUserPrincipal());
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +201,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf/");
|
||||
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2:3@my%40pc.vcf" });
|
||||
// queried address book has a name
|
||||
assertEquals("My Book", davAddressBook.getDisplayName());
|
||||
assertEquals("My Book", davAddressBook.getProperties().getDisplayName());
|
||||
// there are two contacts
|
||||
assertEquals(2, davAddressBook.getMembers().size());
|
||||
// contact file names should be unescaped (yes, it's really named ...%40pc... to check double-encoding)
|
||||
|
@ -81,6 +81,40 @@ exports.getBodyParts = function(conf) {
|
||||
}
|
||||
}
|
||||
}),
|
||||
new RoboHydraHeadDAV({
|
||||
path: "/dav/propfind-collection-properties",
|
||||
handler: function(req,res,next) {
|
||||
if (req.method == "PROPFIND") {
|
||||
res.statusCode = 207;
|
||||
res.write('\<?xml version="1.0" encoding="utf-8" ?>\
|
||||
<multistatus xmlns="DAV:" xmlns:CARD="urn:ietf:params:xml:ns:carddav">\
|
||||
<response>\
|
||||
<href>/dav/propfind-collection-properties</href> \
|
||||
<propstat>\
|
||||
<prop>\
|
||||
<resourcetype>\
|
||||
<collection/>\
|
||||
<CARD:addressbook/>\
|
||||
</resourcetype>\
|
||||
<CARD:supported-address-data>\
|
||||
<address-data-type content-type="text/vcard" version="4.0"/>\
|
||||
</CARD:supported-address-data>\
|
||||
</prop>\
|
||||
<status>HTTP/1.1 200 OK</status>\
|
||||
</propstat>\
|
||||
<propstat>\
|
||||
<prop>\
|
||||
<displayname/>\
|
||||
<A:calendar-color xmlns:A="http://apple.com/ns/ical/">0xFF00FF</A:calendar-color>\
|
||||
</prop>\
|
||||
<status>HTTP/1.1 404 Not Found</status>\
|
||||
</propstat>\
|
||||
</response>\
|
||||
</multistatus>\
|
||||
');
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/* principal URL */
|
||||
new RoboHydraHeadDAV({
|
||||
|
@ -16,8 +16,11 @@ import java.util.regex.Pattern;
|
||||
public class DAVUtils {
|
||||
private static final String TAG = "davdroid.DAVutils";
|
||||
|
||||
public static final int calendarGreen = 0xFFC3EA6E;
|
||||
|
||||
|
||||
public static int CalDAVtoARGBColor(String davColor) {
|
||||
int color = 0xFFC3EA6E; // fallback: "DAVdroid green"
|
||||
int color = calendarGreen; // fallback: "DAVdroid green"
|
||||
if (davColor != null) {
|
||||
Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?");
|
||||
Matcher m = p.matcher(davColor);
|
||||
|
@ -8,33 +8,25 @@
|
||||
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateList;
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||
import net.fortuna.ical4j.model.property.ExDate;
|
||||
import net.fortuna.ical4j.model.property.RDate;
|
||||
import net.fortuna.ical4j.util.TimeZones;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class DateUtils {
|
||||
private final static String TAG = "davdroid.DateUtils";
|
||||
@ -49,35 +41,35 @@ public class DateUtils {
|
||||
|
||||
// time zones
|
||||
|
||||
public static String findAndroidTimezoneID(String tzID) {
|
||||
String localTZ = null;
|
||||
public static String findAndroidTimezoneID(String tz) {
|
||||
String deviceTZ = null;
|
||||
String availableTZs[] = SimpleTimeZone.getAvailableIDs();
|
||||
|
||||
// first, try to find an exact match (case insensitive)
|
||||
for (String availableTZ : availableTZs)
|
||||
if (availableTZ.equalsIgnoreCase(tzID)) {
|
||||
localTZ = availableTZ;
|
||||
if (availableTZ.equalsIgnoreCase(tz)) {
|
||||
deviceTZ = availableTZ;
|
||||
break;
|
||||
}
|
||||
|
||||
// if that doesn't work, try to find something else that matches
|
||||
if (localTZ == null) {
|
||||
if (deviceTZ == null) {
|
||||
Log.w(TAG, "Coulnd't find time zone with matching identifiers, trying to guess");
|
||||
for (String availableTZ : availableTZs)
|
||||
if (StringUtils.indexOfIgnoreCase(tzID, availableTZ) != -1) {
|
||||
localTZ = availableTZ;
|
||||
if (StringUtils.indexOfIgnoreCase(tz, availableTZ) != -1) {
|
||||
deviceTZ = availableTZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if that doesn't work, use UTC as fallback
|
||||
if (localTZ == null) {
|
||||
Log.e(TAG, "Couldn't identify time zone, using UTC as fallback");
|
||||
localTZ = Time.TIMEZONE_UTC;
|
||||
if (deviceTZ == null) {
|
||||
final String defaultTZ = TimeZone.getDefault().getID();
|
||||
Log.e(TAG, "Couldn't identify time zone, using system default (" + defaultTZ + ") as fallback");
|
||||
deviceTZ = defaultTZ;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Assuming time zone " + localTZ + " for " + tzID);
|
||||
return localTZ;
|
||||
return deviceTZ;
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,7 +33,6 @@ import at.bitfire.davdroid.webdav.DavIncapableException;
|
||||
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
||||
import at.bitfire.davdroid.webdav.NotAuthorizedException;
|
||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||
import ezvcard.VCardVersion;
|
||||
|
||||
public class DavResourceFinder implements Closeable {
|
||||
private final static String TAG = "davdroid.ResourceFinder";
|
||||
@ -62,7 +61,7 @@ public class DavResourceFinder implements Closeable {
|
||||
URI uriAddressBookHomeSet = null;
|
||||
try {
|
||||
principal.propfind(Mode.HOME_SETS);
|
||||
uriAddressBookHomeSet = principal.getAddressbookHomeSet();
|
||||
uriAddressBookHomeSet = principal.getProperties().getAddressbookHomeSet();
|
||||
} catch (Exception e) {
|
||||
Log.i(TAG, "Couldn't find address-book home set", e);
|
||||
}
|
||||
@ -80,19 +79,21 @@ public class DavResourceFinder implements Closeable {
|
||||
possibleAddressBooks.addAll(homeSetAddressBooks.getMembers());
|
||||
|
||||
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
|
||||
for (WebDavResource resource : possibleAddressBooks)
|
||||
if (resource.isAddressBook()) {
|
||||
for (WebDavResource resource : possibleAddressBooks) {
|
||||
final WebDavResource.Properties properties = resource.getProperties();
|
||||
if (properties.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()
|
||||
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
||||
properties.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
properties.getDisplayName(),
|
||||
properties.getDescription(), properties.getColor()
|
||||
);
|
||||
|
||||
addressBooks.add(info);
|
||||
}
|
||||
}
|
||||
serverInfo.setAddressBooks(addressBooks);
|
||||
} else
|
||||
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
|
||||
@ -104,7 +105,7 @@ public class DavResourceFinder implements Closeable {
|
||||
URI uriCalendarHomeSet = null;
|
||||
try {
|
||||
principal.propfind(Mode.HOME_SETS);
|
||||
uriCalendarHomeSet = principal.getCalendarHomeSet();
|
||||
uriCalendarHomeSet = principal.getProperties().getCalendarHomeSet();
|
||||
} catch(Exception e) {
|
||||
Log.i(TAG, "Couldn't find calendar home set", e);
|
||||
}
|
||||
@ -124,27 +125,28 @@ public class DavResourceFinder implements Closeable {
|
||||
List<ServerInfo.ResourceInfo>
|
||||
calendars = new LinkedList<>(),
|
||||
todoLists = new LinkedList<>();
|
||||
for (WebDavResource resource : possibleCalendars)
|
||||
if (resource.isCalendar()) {
|
||||
for (WebDavResource resource : possibleCalendars) {
|
||||
final WebDavResource.Properties properties = resource.getProperties();
|
||||
if (properties.isCalendar()) {
|
||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||
resource.isReadOnly(),
|
||||
properties.isReadOnly(),
|
||||
resource.getLocation().toString(),
|
||||
resource.getDisplayName(),
|
||||
resource.getDescription(), resource.getColor()
|
||||
properties.getDisplayName(),
|
||||
properties.getDescription(), properties.getColor()
|
||||
);
|
||||
info.setTimezone(resource.getTimezone());
|
||||
info.setTimezone(properties.getTimeZone());
|
||||
|
||||
boolean isCalendar = false,
|
||||
isTodoList = false;
|
||||
if (resource.getSupportedComponents() == null) {
|
||||
if (properties.getSupportedComponents() == null) {
|
||||
// no info about supported components, assuming all components are supported
|
||||
isCalendar = true;
|
||||
isTodoList = true;
|
||||
} else {
|
||||
// CALDAV:supported-calendar-component-set available
|
||||
for (String supportedComponent : resource.getSupportedComponents())
|
||||
for (String supportedComponent : properties.getSupportedComponents())
|
||||
if ("VEVENT".equalsIgnoreCase(supportedComponent))
|
||||
isCalendar = true;
|
||||
else if ("VTODO".equalsIgnoreCase(supportedComponent))
|
||||
@ -162,6 +164,7 @@ public class DavResourceFinder implements Closeable {
|
||||
if (isTodoList)
|
||||
todoLists.add(new ServerInfo.ResourceInfo(info));
|
||||
}
|
||||
}
|
||||
|
||||
serverInfo.setCalendars(calendars);
|
||||
serverInfo.setTodoLists(todoLists);
|
||||
@ -271,8 +274,8 @@ public class DavResourceFinder implements Closeable {
|
||||
try {
|
||||
WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName);
|
||||
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||
if (wellKnown.getCurrentUserPrincipal() != null) {
|
||||
URI principal = wellKnown.getCurrentUserPrincipal();
|
||||
if (wellKnown.getProperties().getCurrentUserPrincipal() != null) {
|
||||
URI principal = wellKnown.getProperties().getCurrentUserPrincipal();
|
||||
Log.i(TAG, "Principal URL found from Well-Known URI: " + principal);
|
||||
return new WebDavResource(wellKnown, principal);
|
||||
}
|
||||
@ -293,8 +296,8 @@ public class DavResourceFinder implements Closeable {
|
||||
Log.d(TAG, "Well-known service detection failed, trying initial context path " + initialURL);
|
||||
try {
|
||||
base.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||
if (base.getCurrentUserPrincipal() != null) {
|
||||
URI principal = base.getCurrentUserPrincipal();
|
||||
if (base.getProperties().getCurrentUserPrincipal() != null) {
|
||||
URI principal = base.getProperties().getCurrentUserPrincipal();
|
||||
Log.i(TAG, "Principal URL found from initial context path: " + principal);
|
||||
return new WebDavResource(base, principal);
|
||||
}
|
||||
|
@ -17,15 +17,11 @@ import net.fortuna.ical4j.model.Component;
|
||||
import net.fortuna.ical4j.model.ComponentList;
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.Property;
|
||||
import net.fortuna.ical4j.model.PropertyList;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
import net.fortuna.ical4j.model.component.VAlarm;
|
||||
import net.fortuna.ical4j.model.component.VEvent;
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.Attendee;
|
||||
import net.fortuna.ical4j.model.property.Clazz;
|
||||
import net.fortuna.ical4j.model.property.DateProperty;
|
||||
@ -46,14 +42,11 @@ import net.fortuna.ical4j.model.property.Summary;
|
||||
import net.fortuna.ical4j.model.property.Transp;
|
||||
import net.fortuna.ical4j.model.property.Uid;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
import net.fortuna.ical4j.util.CompatibilityHints;
|
||||
import net.fortuna.ical4j.util.SimpleHostInfo;
|
||||
import net.fortuna.ical4j.util.UidGenerator;
|
||||
import net.fortuna.ical4j.util.TimeZones;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@ -63,7 +56,6 @@ import java.util.TimeZone;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
@ -156,15 +148,13 @@ public class Event extends iCalendar {
|
||||
if ((dtStart = event.getStartDate()) == null || (dtEnd = event.getEndDate()) == null)
|
||||
throw new InvalidResourceException("Invalid start time/end time/duration");
|
||||
|
||||
if (hasTime(dtStart)) {
|
||||
validateTimeZone(dtStart);
|
||||
validateTimeZone(dtEnd);
|
||||
}
|
||||
validateTimeZone(dtStart);
|
||||
validateTimeZone(dtEnd);
|
||||
|
||||
// all-day events and "events on that day":
|
||||
// * related UNIX times must be in UTC
|
||||
// * must have a duration (set to one day if missing)
|
||||
if (!hasTime(dtStart) && !dtEnd.getDate().after(dtStart.getDate())) {
|
||||
if (!isDateTime(dtStart) && !dtEnd.getDate().after(dtStart.getDate())) {
|
||||
Log.i(TAG, "Repairing iCal: DTEND := DTSTART+1");
|
||||
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(Time.TIMEZONE_UTC));
|
||||
c.setTime(dtStart.getDate());
|
||||
@ -304,8 +294,20 @@ public class Event extends iCalendar {
|
||||
|
||||
// time helpers
|
||||
|
||||
/**
|
||||
* Returns the time-zone ID for a given date-time, or TIMEZONE_UTC for dates (without time).
|
||||
* TIMEZONE_UTC is also returned for DATE-TIMEs in UTC representation.
|
||||
* @param date DateProperty (DATE or DATE-TIME) whose time-zone information is used
|
||||
*/
|
||||
protected static String getTzId(DateProperty date) {
|
||||
if (isDateTime(date) && !date.isUtc() && date.getTimeZone() != null)
|
||||
return date.getTimeZone().getID();
|
||||
else
|
||||
return TimeZones.UTC_ID;
|
||||
}
|
||||
|
||||
public boolean isAllDay() {
|
||||
return !hasTime(dtStart);
|
||||
return !isDateTime(dtStart);
|
||||
}
|
||||
|
||||
public long getDtStartInMillis() {
|
||||
|
@ -17,7 +17,6 @@ import android.content.Entity;
|
||||
import android.content.EntityIterator;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
@ -48,7 +47,6 @@ import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -127,9 +125,9 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetaData(WebDavResource resource)
|
||||
public void updateMetaData(WebDavResource.Properties properties)
|
||||
{
|
||||
final VCardVersion vCardVersion = resource.getVCardVersion();
|
||||
final VCardVersion vCardVersion = properties.getSupportedVCardVersion();
|
||||
accountSettings.setAddressBookVCardVersion(vCardVersion != null ? vCardVersion : VCardVersion.V3_0);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.CalendarContract.Attendees;
|
||||
@ -42,10 +41,8 @@ import net.fortuna.ical4j.model.parameter.Cn;
|
||||
import net.fortuna.ical4j.model.parameter.CuType;
|
||||
import net.fortuna.ical4j.model.parameter.PartStat;
|
||||
import net.fortuna.ical4j.model.parameter.Role;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.Action;
|
||||
import net.fortuna.ical4j.model.property.Attendee;
|
||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||
import net.fortuna.ical4j.model.property.Description;
|
||||
import net.fortuna.ical4j.model.property.Duration;
|
||||
import net.fortuna.ical4j.model.property.ExDate;
|
||||
@ -61,10 +58,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import at.bitfire.davdroid.DAVUtils;
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
@ -119,7 +114,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, DAVUtils.CalDAVtoARGBColor(info.getColor()));
|
||||
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);
|
||||
@ -139,7 +134,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
}
|
||||
|
||||
if (info.getTimezone() != null)
|
||||
values.put(Calendars.CALENDAR_TIME_ZONE, info.getTimezone());
|
||||
values.put(Calendars.CALENDAR_TIME_ZONE, DateUtils.findAndroidTimezoneID(info.getTimezone()));
|
||||
|
||||
Log.i(TAG, "Inserting calendar: " + values.toString());
|
||||
try {
|
||||
@ -196,16 +191,16 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetaData(WebDavResource resource) throws LocalStorageException {
|
||||
public void updateMetaData(WebDavResource.Properties properties) throws LocalStorageException {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
final String displayName = resource.getDisplayName();
|
||||
final String displayName = properties.getDisplayName();
|
||||
if (displayName != null)
|
||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, displayName);
|
||||
|
||||
final String color = resource.getColor();
|
||||
final Integer color = properties.getColor();
|
||||
if (color != null)
|
||||
values.put(Calendars.CALENDAR_COLOR, DAVUtils.CalDAVtoARGBColor(color));
|
||||
values.put(Calendars.CALENDAR_COLOR, color);
|
||||
|
||||
try {
|
||||
if (values.size() > 0)
|
||||
|
@ -18,7 +18,6 @@ import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.CalendarContract;
|
||||
import android.util.Log;
|
||||
@ -26,7 +25,6 @@ import android.util.Log;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@ -96,7 +94,7 @@ public abstract class LocalCollection<T extends Resource> {
|
||||
/** gets the CTag of the collection */
|
||||
abstract public String getCTag() throws LocalStorageException;
|
||||
/** update locally stored collection properties (e.g. display name and color) from a WebDavResource */
|
||||
abstract public void updateMetaData(WebDavResource resource) throws LocalStorageException;
|
||||
abstract public void updateMetaData(WebDavResource.Properties properties) throws LocalStorageException;
|
||||
|
||||
|
||||
// content provider (= database) querying
|
||||
|
@ -18,7 +18,6 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.CalendarContract;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
@ -36,7 +35,6 @@ import net.fortuna.ical4j.util.TimeZones;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dmfs.provider.tasks.TaskContract;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import at.bitfire.davdroid.DAVUtils;
|
||||
@ -77,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, DAVUtils.CalDAVtoARGBColor(info.getColor()));
|
||||
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);
|
||||
@ -135,16 +133,16 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetaData(WebDavResource resource) throws LocalStorageException {
|
||||
public void updateMetaData(WebDavResource.Properties properties) throws LocalStorageException {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
final String displayName = resource.getDisplayName();
|
||||
final String displayName = properties.getDisplayName();
|
||||
if (displayName != null)
|
||||
values.put(TaskContract.TaskLists.LIST_NAME, displayName);
|
||||
|
||||
final String color = resource.getColor();
|
||||
final Integer color = properties.getColor();
|
||||
if (color != null)
|
||||
values.put(TaskContract.TaskLists.LIST_COLOR, DAVUtils.CalDAVtoARGBColor(color));
|
||||
values.put(TaskContract.TaskLists.LIST_COLOR, color);
|
||||
|
||||
try {
|
||||
if (values.size() > 0)
|
||||
|
@ -13,7 +13,6 @@ import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ezvcard.VCardVersion;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@ -56,8 +55,8 @@ public class ServerInfo implements Serializable {
|
||||
|
||||
final String URL, // absolute URL of resource
|
||||
title,
|
||||
description,
|
||||
color;
|
||||
description;
|
||||
final Integer color;
|
||||
|
||||
String timezone;
|
||||
|
||||
|
@ -35,8 +35,6 @@ import net.fortuna.ical4j.model.property.Summary;
|
||||
import net.fortuna.ical4j.model.property.Uid;
|
||||
import net.fortuna.ical4j.model.property.Url;
|
||||
import net.fortuna.ical4j.model.property.Version;
|
||||
import net.fortuna.ical4j.util.SimpleHostInfo;
|
||||
import net.fortuna.ical4j.util.UidGenerator;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -45,7 +43,6 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
@ -88,7 +88,7 @@ public abstract class WebDavCollection<T extends Resource> {
|
||||
List<T> resources = new LinkedList<>();
|
||||
if (collection.getMembers() != null)
|
||||
for (WebDavResource member : collection.getMembers())
|
||||
resources.add(newResourceSkeleton(member.getName(), member.getETag()));
|
||||
resources.add(newResourceSkeleton(member.getName(), member.getProperties().getETag()));
|
||||
|
||||
return resources.toArray(new Resource[resources.size()]);
|
||||
|
||||
@ -113,7 +113,7 @@ public abstract class WebDavCollection<T extends Resource> {
|
||||
throw new DavNoContentException();
|
||||
|
||||
for (WebDavResource member : collection.getMembers()) {
|
||||
T resource = newResourceSkeleton(member.getName(), member.getETag());
|
||||
T resource = newResourceSkeleton(member.getName(), member.getProperties().getETag());
|
||||
try {
|
||||
if (member.getContent() != null) {
|
||||
@Cleanup InputStream is = new ByteArrayInputStream(member.getContent());
|
||||
@ -158,13 +158,13 @@ public abstract class WebDavCollection<T extends Resource> {
|
||||
// returns ETag of the created resource, if returned by server
|
||||
public String add(Resource res) throws URISyntaxException, IOException, HttpException {
|
||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||
member.setContentType(res.getMimeType());
|
||||
member.getProperties().setContentType(res.getMimeType());
|
||||
|
||||
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
||||
String eTag = member.put(os.toByteArray(), PutMode.ADD_DONT_OVERWRITE);
|
||||
|
||||
// after a successful upload, the collection has implicitely changed, too
|
||||
collection.invalidateCTag();
|
||||
collection.getProperties().invalidateCTag();
|
||||
|
||||
return eTag;
|
||||
}
|
||||
@ -173,19 +173,19 @@ public abstract class WebDavCollection<T extends Resource> {
|
||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||
member.delete();
|
||||
|
||||
collection.invalidateCTag();
|
||||
collection.getProperties().invalidateCTag();
|
||||
}
|
||||
|
||||
// returns ETag of the updated resource, if returned by server
|
||||
public String update(Resource res) throws URISyntaxException, IOException, HttpException {
|
||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||
member.setContentType(res.getMimeType());
|
||||
member.getProperties().setContentType(res.getMimeType());
|
||||
|
||||
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
||||
String eTag = member.put(os.toByteArray(), PutMode.UPDATE_DONT_OVERWRITE);
|
||||
|
||||
// after a successful upload, the collection has implicitely changed, too
|
||||
collection.invalidateCTag();
|
||||
collection.getProperties().invalidateCTag();
|
||||
|
||||
return eTag;
|
||||
}
|
||||
|
@ -8,29 +8,24 @@
|
||||
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.data.CalendarBuilder;
|
||||
import net.fortuna.ical4j.data.ParserException;
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.component.VTimeZone;
|
||||
import net.fortuna.ical4j.model.parameter.Value;
|
||||
import net.fortuna.ical4j.model.property.DateProperty;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
import net.fortuna.ical4j.util.CompatibilityHints;
|
||||
import net.fortuna.ical4j.util.SimpleHostInfo;
|
||||
import net.fortuna.ical4j.util.UidGenerator;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
public abstract class iCalendar extends Resource {
|
||||
static private final String TAG = "DAVdroid.iCal";
|
||||
@ -72,47 +67,46 @@ public abstract class iCalendar extends Resource {
|
||||
|
||||
// time zone helpers
|
||||
|
||||
protected static boolean hasTime(DateProperty date) {
|
||||
protected static boolean isDateTime(DateProperty date) {
|
||||
return date.getDate() instanceof DateTime;
|
||||
}
|
||||
|
||||
protected static String getTzId(DateProperty date) {
|
||||
if (date.isUtc() || !hasTime(date))
|
||||
return Time.TIMEZONE_UTC;
|
||||
else if (date.getTimeZone() != null)
|
||||
return date.getTimeZone().getID();
|
||||
else if (date.getParameter(Value.TZID) != null)
|
||||
return date.getParameter(Value.TZID).getValue();
|
||||
|
||||
// fallback
|
||||
return Time.TIMEZONE_UTC;
|
||||
}
|
||||
|
||||
/* guess matching Android timezone ID */
|
||||
/**
|
||||
* Ensures that a given DateProperty has a time zone with an ID that is available in Android.
|
||||
* @param date DateProperty to validate. Values which are not DATE-TIME will be ignored.
|
||||
*/
|
||||
protected static void validateTimeZone(DateProperty date) {
|
||||
if (date.isUtc() || !hasTime(date))
|
||||
return;
|
||||
if (isDateTime(date)) {
|
||||
final TimeZone tz = date.getTimeZone();
|
||||
if (tz == null)
|
||||
return;
|
||||
final String tzID = tz.getID();
|
||||
if (tzID == null)
|
||||
return;
|
||||
|
||||
String tzID = getTzId(date);
|
||||
if (tzID == null)
|
||||
return;
|
||||
|
||||
String localTZ = DateUtils.findAndroidTimezoneID(tzID);
|
||||
date.setTimeZone(DateUtils.tzRegistry.getTimeZone(localTZ));
|
||||
String deviceTzID = DateUtils.findAndroidTimezoneID(tzID);
|
||||
if (!tzID.equals(deviceTzID))
|
||||
date.setTimeZone(DateUtils.tzRegistry.getTimeZone(deviceTzID));
|
||||
}
|
||||
}
|
||||
|
||||
public static String TimezoneDefToTzId(String timezoneDef) throws IllegalArgumentException {
|
||||
/**
|
||||
* Takes a string with a timezone definition and returns the time zone ID.
|
||||
* @param timezoneDef time zone definition (VCALENDAR with VTIMEZONE component)
|
||||
* @return time zone id (TZID) if VTIMEZONE contains a TZID,
|
||||
* null otherwise
|
||||
*/
|
||||
public static String TimezoneDefToTzId(@NonNull String timezoneDef) {
|
||||
try {
|
||||
if (timezoneDef != null) {
|
||||
CalendarBuilder builder = new CalendarBuilder();
|
||||
net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef));
|
||||
VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE);
|
||||
CalendarBuilder builder = new CalendarBuilder();
|
||||
net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef));
|
||||
VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE);
|
||||
if (timezone != null && timezone.getTimeZoneId() != null)
|
||||
return timezone.getTimeZoneId().getValue();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.w(TAG, "Can't understand time zone definition, ignoring", ex);
|
||||
} catch (IOException|ParserException e) {
|
||||
Log.e(TAG, "Can't understand time zone definition", e);
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ import at.bitfire.davdroid.ArrayUtils;
|
||||
import at.bitfire.davdroid.resource.LocalCollection;
|
||||
import at.bitfire.davdroid.resource.LocalStorageException;
|
||||
import at.bitfire.davdroid.resource.RecordNotFoundException;
|
||||
import at.bitfire.davdroid.resource.WebDavCollection;
|
||||
import at.bitfire.davdroid.resource.Resource;
|
||||
import at.bitfire.davdroid.resource.WebDavCollection;
|
||||
import at.bitfire.davdroid.webdav.ConflictException;
|
||||
import at.bitfire.davdroid.webdav.DavException;
|
||||
import at.bitfire.davdroid.webdav.HttpException;
|
||||
@ -46,8 +46,8 @@ public class SyncManager {
|
||||
public void synchronize(boolean manualSync, SyncResult syncResult) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
|
||||
// PHASE 1: fetch collection properties
|
||||
remote.getProperties();
|
||||
final WebDavResource collectionResource = remote.getCollection();
|
||||
local.updateMetaData(collectionResource);
|
||||
final WebDavResource.Properties collectionProperties = remote.getCollection().getProperties();
|
||||
local.updateMetaData(collectionProperties);
|
||||
|
||||
// PHASE 2: push local changes to server
|
||||
int deletedRemotely = pushDeleted(),
|
||||
@ -62,7 +62,7 @@ public class SyncManager {
|
||||
}
|
||||
if (!syncMembers) {
|
||||
final String
|
||||
currentCTag = collectionResource.getCTag(),
|
||||
currentCTag = collectionProperties.getCTag(),
|
||||
lastCTag = local.getCTag();
|
||||
Log.d(TAG, "Last local CTag = " + lastCTag + "; current remote CTag = " + currentCTag);
|
||||
if (currentCTag == null || !currentCTag.equals(lastCTag))
|
||||
@ -101,7 +101,7 @@ public class SyncManager {
|
||||
|
||||
// update collection CTag
|
||||
Log.i(TAG, "Sync complete, fetching new CTag");
|
||||
local.setCTag(collectionResource.getCTag());
|
||||
local.setCTag(collectionProperties.getCTag());
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
package at.bitfire.davdroid.ui.setup;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
|
@ -41,18 +41,19 @@ import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import at.bitfire.davdroid.DAVUtils;
|
||||
import at.bitfire.davdroid.URIUtils;
|
||||
import at.bitfire.davdroid.resource.Event;
|
||||
import at.bitfire.davdroid.resource.iCalendar;
|
||||
import at.bitfire.davdroid.webdav.DavProp.Comp;
|
||||
import ezvcard.VCardVersion;
|
||||
import lombok.Cleanup;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@ -64,15 +65,6 @@ import lombok.ToString;
|
||||
public class WebDavResource {
|
||||
private static final String TAG = "davdroid.WebDavResource";
|
||||
|
||||
public enum Property {
|
||||
CURRENT_USER_PRINCIPAL, // resource detection
|
||||
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
||||
CONTENT_TYPE, READ_ONLY, // WebDAV (common)
|
||||
DISPLAY_NAME, DESCRIPTION, ETAG,
|
||||
IS_COLLECTION, CTAG, // collections
|
||||
IS_CALENDAR, COLOR, TIMEZONE, // CalDAV
|
||||
IS_ADDRESSBOOK, VCARD_VERSION // CardDAV
|
||||
}
|
||||
public enum PutMode {
|
||||
ADD_DONT_OVERWRITE,
|
||||
UPDATE_DONT_OVERWRITE
|
||||
@ -85,11 +77,8 @@ public class WebDavResource {
|
||||
protected Set<String> capabilities = new HashSet<>(),
|
||||
methods = new HashSet<>();
|
||||
|
||||
// DAV properties
|
||||
protected HashMap<Property, String> properties = new HashMap<>();
|
||||
@Getter protected List<String> supportedComponents;
|
||||
|
||||
// list of members (only for collections)
|
||||
@Getter Properties properties = new Properties();
|
||||
@Getter protected List<WebDavResource> members;
|
||||
|
||||
// content (available after GET)
|
||||
@ -151,9 +140,9 @@ public class WebDavResource {
|
||||
location = parent.location.resolve(new URI(null, null, "./" + member, null));
|
||||
}
|
||||
|
||||
public WebDavResource(WebDavResource parent, String member, String ETag) throws URISyntaxException {
|
||||
public WebDavResource(WebDavResource parent, String member, String eTag) throws URISyntaxException {
|
||||
this(parent, member);
|
||||
properties.put(Property.ETAG, ETag);
|
||||
properties.eTag = eTag;
|
||||
}
|
||||
|
||||
|
||||
@ -191,76 +180,6 @@ public class WebDavResource {
|
||||
}
|
||||
|
||||
|
||||
/* property methods */
|
||||
|
||||
public URI getCurrentUserPrincipal() throws URISyntaxException {
|
||||
String principal = properties.get(Property.CURRENT_USER_PRINCIPAL);
|
||||
return principal != null ? URIUtils.parseURI(principal, false) : null;
|
||||
}
|
||||
|
||||
public URI getAddressbookHomeSet() throws URISyntaxException {
|
||||
String homeset = properties.get(Property.ADDRESSBOOK_HOMESET);
|
||||
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
||||
}
|
||||
|
||||
public URI getCalendarHomeSet() throws URISyntaxException {
|
||||
String homeset = properties.get(Property.CALENDAR_HOMESET);
|
||||
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return properties.get(Property.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
public void setContentType(String mimeType) {
|
||||
properties.put(Property.CONTENT_TYPE, mimeType);
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return properties.containsKey(Property.READ_ONLY);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return properties.get(Property.DISPLAY_NAME);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return properties.get(Property.DESCRIPTION);
|
||||
}
|
||||
|
||||
public String getCTag() {
|
||||
return properties.get(Property.CTAG);
|
||||
}
|
||||
public void invalidateCTag() {
|
||||
properties.remove(Property.CTAG);
|
||||
}
|
||||
|
||||
public String getETag() {
|
||||
return properties.get(Property.ETAG);
|
||||
}
|
||||
|
||||
public boolean isCalendar() {
|
||||
return properties.containsKey(Property.IS_CALENDAR);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return properties.get(Property.COLOR);
|
||||
}
|
||||
|
||||
public String getTimezone() {
|
||||
return properties.get(Property.TIMEZONE);
|
||||
}
|
||||
|
||||
public boolean isAddressBook() {
|
||||
return properties.containsKey(Property.IS_ADDRESSBOOK);
|
||||
}
|
||||
|
||||
public VCardVersion getVCardVersion() {
|
||||
String versionStr = properties.get(Property.VCARD_VERSION);
|
||||
return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null;
|
||||
}
|
||||
|
||||
|
||||
/* collection operations */
|
||||
|
||||
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
||||
@ -372,12 +291,12 @@ public class WebDavResource {
|
||||
put.addHeader("If-None-Match", "*");
|
||||
break;
|
||||
case UPDATE_DONT_OVERWRITE:
|
||||
put.addHeader("If-Match", (getETag() != null) ? getETag() : "*");
|
||||
put.addHeader("If-Match", (properties.eTag != null) ? properties.eTag : "*");
|
||||
break;
|
||||
}
|
||||
|
||||
if (getContentType() != null)
|
||||
put.addHeader("Content-Type", getContentType());
|
||||
if (properties.contentType != null)
|
||||
put.addHeader("Content-Type", properties.contentType);
|
||||
|
||||
@Cleanup CloseableHttpResponse response = httpClient.execute(put, context);
|
||||
checkResponse(response);
|
||||
@ -392,8 +311,8 @@ public class WebDavResource {
|
||||
public void delete() throws URISyntaxException, IOException, HttpException {
|
||||
HttpDeleteHC4 delete = new HttpDeleteHC4(location);
|
||||
|
||||
if (getETag() != null)
|
||||
delete.addHeader("If-Match", getETag());
|
||||
if (properties.eTag != null)
|
||||
delete.addHeader("If-Match", properties.eTag);
|
||||
|
||||
@Cleanup CloseableHttpResponse response = httpClient.execute(delete, context);
|
||||
checkResponse(response);
|
||||
@ -473,8 +392,7 @@ public class WebDavResource {
|
||||
Log.d(TAG, "Processing multi-status element: " + href);
|
||||
|
||||
// process known properties
|
||||
HashMap<Property, String> properties = new HashMap<>();
|
||||
List<String> supportedComponents = null;
|
||||
Properties properties = new Properties();
|
||||
byte[] data = null;
|
||||
|
||||
// in <response>, either <status> or <propstat> must be present
|
||||
@ -488,84 +406,9 @@ public class WebDavResource {
|
||||
// ignore information about missing properties etc.
|
||||
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
||||
continue;
|
||||
|
||||
DavProp prop = singlePropstat.prop;
|
||||
|
||||
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
||||
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
||||
|
||||
if (prop.currentUserPrivilegeSet != null) {
|
||||
// privilege info available
|
||||
boolean mayAll = false,
|
||||
mayBind = false,
|
||||
mayUnbind = false,
|
||||
mayWrite = false,
|
||||
mayWriteContent = false;
|
||||
for (DavProp.Privilege privilege : prop.currentUserPrivilegeSet) {
|
||||
if (privilege.getAll() != null) mayAll = true;
|
||||
if (privilege.getBind() != null) mayBind = true;
|
||||
if (privilege.getUnbind() != null) mayUnbind = true;
|
||||
if (privilege.getWrite() != null) mayWrite = true;
|
||||
if (privilege.getWriteContent() != null) mayWriteContent = true;
|
||||
}
|
||||
if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind))
|
||||
properties.put(Property.READ_ONLY, "1");
|
||||
}
|
||||
|
||||
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
||||
properties.put(Property.ADDRESSBOOK_HOMESET, URIUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href));
|
||||
|
||||
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
||||
properties.put(Property.CALENDAR_HOMESET, URIUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href));
|
||||
|
||||
if (prop.displayname != null)
|
||||
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
|
||||
|
||||
if (prop.resourcetype != null) {
|
||||
if (prop.resourcetype.getCollection() != null) {
|
||||
properties.put(Property.IS_COLLECTION, "1");
|
||||
// is a collection, ensure trailing slash
|
||||
href = URIUtils.ensureTrailingSlash(href);
|
||||
}
|
||||
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
||||
properties.put(Property.IS_ADDRESSBOOK, "1");
|
||||
|
||||
if (prop.addressbookDescription != null)
|
||||
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) { // CalDAV collection propertioes
|
||||
properties.put(Property.IS_CALENDAR, "1");
|
||||
|
||||
if (prop.calendarDescription != null)
|
||||
properties.put(Property.DESCRIPTION, prop.calendarDescription.getDescription());
|
||||
|
||||
if (prop.calendarColor != null)
|
||||
properties.put(Property.COLOR, prop.calendarColor.getColor());
|
||||
|
||||
if (prop.calendarTimezone != null)
|
||||
try {
|
||||
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
||||
} catch(IllegalArgumentException e) {
|
||||
}
|
||||
|
||||
if (prop.supportedCalendarComponentSet != null) {
|
||||
supportedComponents = new LinkedList<>();
|
||||
for (Comp component : prop.supportedCalendarComponentSet)
|
||||
supportedComponents.add(component.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop.getctag != null)
|
||||
properties.put(Property.CTAG, prop.getctag.getCTag());
|
||||
|
||||
if (prop.getetag != null)
|
||||
properties.put(Property.ETAG, prop.getetag.getETag());
|
||||
properties.process(prop);
|
||||
|
||||
if (prop.calendarData != null && prop.calendarData.ical != null)
|
||||
data = prop.calendarData.ical.getBytes();
|
||||
@ -574,16 +417,16 @@ public class WebDavResource {
|
||||
}
|
||||
|
||||
// about which resource is this response?
|
||||
if (properties.isCollection) // ensure trailing slashs for collections
|
||||
href = URIUtils.ensureTrailingSlash(href);
|
||||
|
||||
if (location.equals(href) || URIUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves
|
||||
this.properties.putAll(properties);
|
||||
if (supportedComponents != null)
|
||||
this.supportedComponents = supportedComponents;
|
||||
this.properties = properties;
|
||||
this.content = data;
|
||||
|
||||
} else { // about a member
|
||||
WebDavResource member = new WebDavResource(this, href);
|
||||
member.properties = properties;
|
||||
member.supportedComponents = supportedComponents;
|
||||
member.content = data;
|
||||
|
||||
members.add(member);
|
||||
@ -593,4 +436,133 @@ public class WebDavResource {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
|
||||
public static class Properties {
|
||||
// DAV properties
|
||||
protected String
|
||||
currentUserPrincipal,
|
||||
addressBookHomeset,
|
||||
calendarHomeset,
|
||||
color;
|
||||
|
||||
@Getter protected String
|
||||
displayName,
|
||||
description,
|
||||
timeZone,
|
||||
eTag,
|
||||
cTag;
|
||||
|
||||
@Getter @Setter protected String contentType;
|
||||
|
||||
@Getter protected boolean
|
||||
readOnly,
|
||||
isCollection,
|
||||
isCalendar,
|
||||
isAddressBook;
|
||||
|
||||
@Getter protected List<String> supportedComponents;
|
||||
@Getter protected VCardVersion supportedVCardVersion;
|
||||
|
||||
// fill from DavProp
|
||||
|
||||
protected void process(DavProp prop) {
|
||||
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
||||
currentUserPrincipal = prop.currentUserPrincipal.getHref().href;
|
||||
|
||||
if (prop.currentUserPrivilegeSet != null) {
|
||||
// privilege info available
|
||||
boolean mayAll = false,
|
||||
mayBind = false,
|
||||
mayUnbind = false,
|
||||
mayWrite = false,
|
||||
mayWriteContent = false;
|
||||
for (DavProp.Privilege privilege : prop.currentUserPrivilegeSet) {
|
||||
if (privilege.getAll() != null) mayAll = true;
|
||||
if (privilege.getBind() != null) mayBind = true;
|
||||
if (privilege.getUnbind() != null) mayUnbind = true;
|
||||
if (privilege.getWrite() != null) mayWrite = true;
|
||||
if (privilege.getWriteContent() != null) mayWriteContent = true;
|
||||
}
|
||||
if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind))
|
||||
readOnly = true;
|
||||
}
|
||||
|
||||
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
||||
addressBookHomeset = URIUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href);
|
||||
|
||||
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
||||
calendarHomeset = URIUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href);
|
||||
|
||||
if (prop.displayname != null)
|
||||
displayName = prop.displayname.getDisplayName();
|
||||
|
||||
if (prop.resourcetype != null) {
|
||||
if (prop.resourcetype.getCollection() != null)
|
||||
isCollection = true;
|
||||
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
||||
isAddressBook = true;
|
||||
|
||||
if (prop.addressbookDescription != null)
|
||||
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()))
|
||||
supportedVCardVersion = VCardVersion.V4_0;
|
||||
}
|
||||
if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes
|
||||
isCalendar = true;
|
||||
|
||||
if (prop.calendarDescription != null)
|
||||
description = prop.calendarDescription.getDescription();
|
||||
|
||||
if (prop.calendarColor != null)
|
||||
color = prop.calendarColor.getColor();
|
||||
|
||||
if (prop.calendarTimezone != null)
|
||||
timeZone = prop.calendarTimezone.getTimezone();
|
||||
|
||||
if (prop.supportedCalendarComponentSet != null) {
|
||||
supportedComponents = new LinkedList<>();
|
||||
for (Comp component : prop.supportedCalendarComponentSet)
|
||||
supportedComponents.add(component.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop.getctag != null)
|
||||
cTag = prop.getctag.getCTag();
|
||||
|
||||
if (prop.getetag != null)
|
||||
eTag = prop.getetag.getETag();
|
||||
}
|
||||
|
||||
// getters / setters
|
||||
|
||||
public Integer getColor() {
|
||||
return color != null ? DAVUtils.CalDAVtoARGBColor(color) : null;
|
||||
}
|
||||
|
||||
public URI getCurrentUserPrincipal() throws URISyntaxException {
|
||||
return currentUserPrincipal != null ? URIUtils.parseURI(currentUserPrincipal, false) : null;
|
||||
}
|
||||
|
||||
public URI getAddressbookHomeSet() throws URISyntaxException {
|
||||
return addressBookHomeset != null ? URIUtils.parseURI(addressBookHomeset, false) : null;
|
||||
}
|
||||
|
||||
public URI getCalendarHomeSet() throws URISyntaxException {
|
||||
return calendarHomeset != null ? URIUtils.parseURI(calendarHomeset, false) : null;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone != null ? iCalendar.TimezoneDefToTzId(timeZone) : null;
|
||||
}
|
||||
|
||||
public void invalidateCTag() {
|
||||
cTag = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user