mirror of
https://github.com/etesync/android
synced 2024-11-23 00:18:19 +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 junit.framework.TestCase;
|
||||||
|
|
||||||
import net.fortuna.ical4j.model.Date;
|
|
||||||
import net.fortuna.ical4j.model.DateList;
|
import net.fortuna.ical4j.model.DateList;
|
||||||
import net.fortuna.ical4j.model.TimeZone;
|
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.parameter.Value;
|
||||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
|
||||||
import net.fortuna.ical4j.model.property.ExDate;
|
import net.fortuna.ical4j.model.property.ExDate;
|
||||||
import net.fortuna.ical4j.model.property.RDate;
|
import net.fortuna.ical4j.model.property.RDate;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ public class ContactTest extends InstrumentationTestCase {
|
|||||||
Contact c = new Contact("test.vcf", null);
|
Contact c = new Contact("test.vcf", null);
|
||||||
|
|
||||||
// should generate VCard 3.0 by default
|
// 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"));
|
assertTrue(new String(c.toEntity().toByteArray()).contains("VERSION:3.0"));
|
||||||
|
|
||||||
// now let's generate VCard 4.0
|
// now let's generate VCard 4.0
|
||||||
|
@ -9,16 +9,23 @@ package at.bitfire.davdroid.resource;
|
|||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
import android.text.format.Time;
|
|
||||||
|
|
||||||
import net.fortuna.ical4j.data.ParserException;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import at.bitfire.davdroid.DateUtils;
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
|
|
||||||
public class EventTest extends InstrumentationTestCase {
|
public class EventTest extends InstrumentationTestCase {
|
||||||
|
protected final TimeZone tzVienna = DateUtils.tzRegistry.getTimeZone("Europe/Vienna");
|
||||||
|
|
||||||
AssetManager assetMgr;
|
AssetManager assetMgr;
|
||||||
|
|
||||||
Event eOnThatDay, eAllDay1Day, eAllDay10Days, eAllDay0Sec;
|
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 {
|
public void testRecurringWithException() throws Exception {
|
||||||
Event event = parseCalendar("recurring-with-exception1.ics");
|
Event event = parseCalendar("recurring-with-exception1.ics");
|
||||||
assertTrue(event.isAllDay());
|
assertTrue(event.isAllDay());
|
||||||
@ -55,73 +77,29 @@ public class EventTest extends InstrumentationTestCase {
|
|||||||
public void testStartEndTimesAllDay() throws IOException, ParserException {
|
public void testStartEndTimesAllDay() throws IOException, ParserException {
|
||||||
// event with start date only
|
// event with start date only
|
||||||
assertEquals(868838400000L, eOnThatDay.getDtStartInMillis());
|
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
|
// DTEND missing in VEVENT, must have been set to DTSTART+1 day
|
||||||
assertEquals(868838400000L + 86400000, eOnThatDay.getDtEndInMillis());
|
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)
|
// event with start+end date for all-day event (one day)
|
||||||
assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis());
|
assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis());
|
||||||
assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtStartTzID());
|
assertEquals(TimeZones.UTC_ID, eAllDay1Day.getDtStartTzID());
|
||||||
assertEquals(868838400000L + 86400000, eAllDay1Day.getDtEndInMillis());
|
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)
|
// event with start+end date for all-day event (ten days)
|
||||||
assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis());
|
assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis());
|
||||||
assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtStartTzID());
|
assertEquals(TimeZones.UTC_ID, eAllDay10Days.getDtStartTzID());
|
||||||
assertEquals(868838400000L + 10*86400000, eAllDay10Days.getDtEndInMillis());
|
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)
|
// event with start+end date on some day (invalid 0 sec-event)
|
||||||
assertEquals(868838400000L, eAllDay0Sec.getDtStartInMillis());
|
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
|
// DTEND invalid in VEVENT, must have been set to DTSTART+1 day
|
||||||
assertEquals(868838400000L + 86400000, eAllDay0Sec.getDtEndInMillis());
|
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 {
|
public void testUnfolding() throws IOException, InvalidResourceException {
|
||||||
|
@ -25,25 +25,14 @@ import android.test.InstrumentationTestCase;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import net.fortuna.ical4j.model.Date;
|
import net.fortuna.ical4j.model.Date;
|
||||||
import net.fortuna.ical4j.model.DateList;
|
|
||||||
import net.fortuna.ical4j.model.Dur;
|
import net.fortuna.ical4j.model.Dur;
|
||||||
import net.fortuna.ical4j.model.TimeZone;
|
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.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.DtEnd;
|
||||||
import net.fortuna.ical4j.model.property.DtStart;
|
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.text.ParseException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import at.bitfire.davdroid.DateUtils;
|
import at.bitfire.davdroid.DateUtils;
|
||||||
import lombok.Cleanup;
|
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.DavResourceFinder;
|
||||||
import at.bitfire.davdroid.resource.ServerInfo;
|
import at.bitfire.davdroid.resource.ServerInfo;
|
||||||
import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
|
import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
|
||||||
import ezvcard.VCardVersion;
|
|
||||||
|
|
||||||
public class DavResourceFinderTest extends InstrumentationTestCase {
|
public class DavResourceFinderTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
public void testPropfindCurrentUserPrincipal() throws Exception {
|
public void testPropfindCurrentUserPrincipal() throws Exception {
|
||||||
davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
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");
|
WebDavResource simpleFile = new WebDavResource(davAssets, "test.random");
|
||||||
try {
|
try {
|
||||||
@ -82,14 +82,14 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
} catch(DavException ex) {
|
} catch(DavException ex) {
|
||||||
}
|
}
|
||||||
assertNull(simpleFile.getCurrentUserPrincipal());
|
assertNull(simpleFile.getProperties().getCurrentUserPrincipal());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindHomeSets() throws Exception {
|
public void testPropfindHomeSets() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
||||||
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
||||||
assertEquals(new URI("/dav/addressbooks/test/"), dav.getAddressbookHomeSet());
|
assertEquals(new URI("/dav/addressbooks/test/"), dav.getProperties().getAddressbookHomeSet());
|
||||||
assertEquals(new URI("/dav/calendars/test/"), dav.getCalendarHomeSet());
|
assertEquals(new URI("/dav/calendars/test/"), dav.getProperties().getCalendarHomeSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindAddressBooks() throws Exception {
|
public void testPropfindAddressBooks() throws Exception {
|
||||||
@ -103,35 +103,46 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
WebDavResource ab = dav.getMembers().get(0);
|
WebDavResource ab = dav.getMembers().get(0);
|
||||||
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/useless-member"), ab.getLocation());
|
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/useless-member"), ab.getLocation());
|
||||||
assertEquals("useless-member", ab.getName());
|
assertEquals("useless-member", ab.getName());
|
||||||
assertFalse(ab.isAddressBook());
|
assertFalse(ab.getProperties().isAddressBook());
|
||||||
|
|
||||||
// the second one is an address book (referenced by relative URI)
|
// the second one is an address book (referenced by relative URI)
|
||||||
ab = dav.getMembers().get(1);
|
ab = dav.getMembers().get(1);
|
||||||
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/default.vcf/"), ab.getLocation());
|
assertEquals(TestConstants.roboHydra.resolve("/dav/addressbooks/test/default.vcf/"), ab.getLocation());
|
||||||
assertEquals("default.vcf", ab.getName());
|
assertEquals("default.vcf", ab.getName());
|
||||||
assertTrue(ab.isAddressBook());
|
assertTrue(ab.getProperties().isAddressBook());
|
||||||
|
|
||||||
// the third one is an address book (referenced by an absolute URI)
|
// the third one is an address book (referenced by an absolute URI)
|
||||||
ab = dav.getMembers().get(2);
|
ab = dav.getMembers().get(2);
|
||||||
assertEquals(new URI("https://my.server/absolute:uri/my-address-book/"), ab.getLocation());
|
assertEquals(new URI("https://my.server/absolute:uri/my-address-book/"), ab.getLocation());
|
||||||
assertEquals("my-address-book", ab.getName());
|
assertEquals("my-address-book", ab.getName());
|
||||||
assertTrue(ab.isAddressBook());
|
assertTrue(ab.getProperties().isAddressBook());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindCalendars() throws Exception {
|
public void testPropfindCalendars() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
||||||
dav.propfind(Mode.CALDAV_COLLECTIONS);
|
dav.propfind(Mode.CALDAV_COLLECTIONS);
|
||||||
assertEquals(3, dav.getMembers().size());
|
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()) {
|
for (WebDavResource member : dav.getMembers()) {
|
||||||
if (member.getName().contains(".ics"))
|
if (member.getName().contains(".ics"))
|
||||||
assertTrue(member.isCalendar());
|
assertTrue(member.getProperties().isCalendar());
|
||||||
else
|
else
|
||||||
assertFalse(member.isCalendar());
|
assertFalse(member.getProperties().isCalendar());
|
||||||
assertFalse(member.isAddressBook());
|
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 {
|
public void testPropfindTrailingSlashes() throws Exception {
|
||||||
final String principalOK = "/principals/ok";
|
final String principalOK = "/principals/ok";
|
||||||
|
|
||||||
@ -145,7 +156,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
for (String path : requestPaths) {
|
for (String path : requestPaths) {
|
||||||
WebDavResource davSlash = new WebDavResource(davCollection, new URI(path));
|
WebDavResource davSlash = new WebDavResource(davCollection, new URI(path));
|
||||||
davSlash.propfind(Mode.CARDDAV_COLLECTIONS);
|
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/");
|
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf/");
|
||||||
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2:3@my%40pc.vcf" });
|
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2:3@my%40pc.vcf" });
|
||||||
// queried address book has a name
|
// queried address book has a name
|
||||||
assertEquals("My Book", davAddressBook.getDisplayName());
|
assertEquals("My Book", davAddressBook.getProperties().getDisplayName());
|
||||||
// there are two contacts
|
// there are two contacts
|
||||||
assertEquals(2, davAddressBook.getMembers().size());
|
assertEquals(2, davAddressBook.getMembers().size());
|
||||||
// contact file names should be unescaped (yes, it's really named ...%40pc... to check double-encoding)
|
// 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 */
|
/* principal URL */
|
||||||
new RoboHydraHeadDAV({
|
new RoboHydraHeadDAV({
|
||||||
|
@ -16,8 +16,11 @@ import java.util.regex.Pattern;
|
|||||||
public class DAVUtils {
|
public class DAVUtils {
|
||||||
private static final String TAG = "davdroid.DAVutils";
|
private static final String TAG = "davdroid.DAVutils";
|
||||||
|
|
||||||
|
public static final int calendarGreen = 0xFFC3EA6E;
|
||||||
|
|
||||||
|
|
||||||
public static int CalDAVtoARGBColor(String davColor) {
|
public static int CalDAVtoARGBColor(String davColor) {
|
||||||
int color = 0xFFC3EA6E; // fallback: "DAVdroid green"
|
int color = calendarGreen; // fallback: "DAVdroid green"
|
||||||
if (davColor != null) {
|
if (davColor != null) {
|
||||||
Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?");
|
Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?");
|
||||||
Matcher m = p.matcher(davColor);
|
Matcher m = p.matcher(davColor);
|
||||||
|
@ -8,33 +8,25 @@
|
|||||||
|
|
||||||
package at.bitfire.davdroid;
|
package at.bitfire.davdroid;
|
||||||
|
|
||||||
import android.text.format.Time;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import net.fortuna.ical4j.model.Date;
|
import net.fortuna.ical4j.model.Date;
|
||||||
import net.fortuna.ical4j.model.DateList;
|
import net.fortuna.ical4j.model.DateList;
|
||||||
import net.fortuna.ical4j.model.DateTime;
|
import net.fortuna.ical4j.model.DateTime;
|
||||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
|
||||||
import net.fortuna.ical4j.model.TimeZone;
|
import net.fortuna.ical4j.model.TimeZone;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||||
import net.fortuna.ical4j.model.parameter.Value;
|
import net.fortuna.ical4j.model.parameter.Value;
|
||||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
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 org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.SimpleTimeZone;
|
import java.util.SimpleTimeZone;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
public class DateUtils {
|
public class DateUtils {
|
||||||
private final static String TAG = "davdroid.DateUtils";
|
private final static String TAG = "davdroid.DateUtils";
|
||||||
@ -49,35 +41,35 @@ public class DateUtils {
|
|||||||
|
|
||||||
// time zones
|
// time zones
|
||||||
|
|
||||||
public static String findAndroidTimezoneID(String tzID) {
|
public static String findAndroidTimezoneID(String tz) {
|
||||||
String localTZ = null;
|
String deviceTZ = null;
|
||||||
String availableTZs[] = SimpleTimeZone.getAvailableIDs();
|
String availableTZs[] = SimpleTimeZone.getAvailableIDs();
|
||||||
|
|
||||||
// first, try to find an exact match (case insensitive)
|
// first, try to find an exact match (case insensitive)
|
||||||
for (String availableTZ : availableTZs)
|
for (String availableTZ : availableTZs)
|
||||||
if (availableTZ.equalsIgnoreCase(tzID)) {
|
if (availableTZ.equalsIgnoreCase(tz)) {
|
||||||
localTZ = availableTZ;
|
deviceTZ = availableTZ;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if that doesn't work, try to find something else that matches
|
// 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");
|
Log.w(TAG, "Coulnd't find time zone with matching identifiers, trying to guess");
|
||||||
for (String availableTZ : availableTZs)
|
for (String availableTZ : availableTZs)
|
||||||
if (StringUtils.indexOfIgnoreCase(tzID, availableTZ) != -1) {
|
if (StringUtils.indexOfIgnoreCase(tz, availableTZ) != -1) {
|
||||||
localTZ = availableTZ;
|
deviceTZ = availableTZ;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if that doesn't work, use UTC as fallback
|
// if that doesn't work, use UTC as fallback
|
||||||
if (localTZ == null) {
|
if (deviceTZ == null) {
|
||||||
Log.e(TAG, "Couldn't identify time zone, using UTC as fallback");
|
final String defaultTZ = TimeZone.getDefault().getID();
|
||||||
localTZ = Time.TIMEZONE_UTC;
|
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 deviceTZ;
|
||||||
return localTZ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ import at.bitfire.davdroid.webdav.DavIncapableException;
|
|||||||
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
||||||
import at.bitfire.davdroid.webdav.NotAuthorizedException;
|
import at.bitfire.davdroid.webdav.NotAuthorizedException;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
import ezvcard.VCardVersion;
|
|
||||||
|
|
||||||
public class DavResourceFinder implements Closeable {
|
public class DavResourceFinder implements Closeable {
|
||||||
private final static String TAG = "davdroid.ResourceFinder";
|
private final static String TAG = "davdroid.ResourceFinder";
|
||||||
@ -62,7 +61,7 @@ public class DavResourceFinder implements Closeable {
|
|||||||
URI uriAddressBookHomeSet = null;
|
URI uriAddressBookHomeSet = null;
|
||||||
try {
|
try {
|
||||||
principal.propfind(Mode.HOME_SETS);
|
principal.propfind(Mode.HOME_SETS);
|
||||||
uriAddressBookHomeSet = principal.getAddressbookHomeSet();
|
uriAddressBookHomeSet = principal.getProperties().getAddressbookHomeSet();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.i(TAG, "Couldn't find address-book home set", 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());
|
possibleAddressBooks.addAll(homeSetAddressBooks.getMembers());
|
||||||
|
|
||||||
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
|
List<ServerInfo.ResourceInfo> addressBooks = new LinkedList<>();
|
||||||
for (WebDavResource resource : possibleAddressBooks)
|
for (WebDavResource resource : possibleAddressBooks) {
|
||||||
if (resource.isAddressBook()) {
|
final WebDavResource.Properties properties = resource.getProperties();
|
||||||
|
if (properties.isAddressBook()) {
|
||||||
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
|
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
|
||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
||||||
resource.isReadOnly(),
|
properties.isReadOnly(),
|
||||||
resource.getLocation().toString(),
|
resource.getLocation().toString(),
|
||||||
resource.getDisplayName(),
|
properties.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
properties.getDescription(), properties.getColor()
|
||||||
);
|
);
|
||||||
|
|
||||||
addressBooks.add(info);
|
addressBooks.add(info);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
serverInfo.setAddressBooks(addressBooks);
|
serverInfo.setAddressBooks(addressBooks);
|
||||||
} else
|
} else
|
||||||
Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support");
|
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;
|
URI uriCalendarHomeSet = null;
|
||||||
try {
|
try {
|
||||||
principal.propfind(Mode.HOME_SETS);
|
principal.propfind(Mode.HOME_SETS);
|
||||||
uriCalendarHomeSet = principal.getCalendarHomeSet();
|
uriCalendarHomeSet = principal.getProperties().getCalendarHomeSet();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
Log.i(TAG, "Couldn't find calendar home set", e);
|
Log.i(TAG, "Couldn't find calendar home set", e);
|
||||||
}
|
}
|
||||||
@ -124,27 +125,28 @@ public class DavResourceFinder implements Closeable {
|
|||||||
List<ServerInfo.ResourceInfo>
|
List<ServerInfo.ResourceInfo>
|
||||||
calendars = new LinkedList<>(),
|
calendars = new LinkedList<>(),
|
||||||
todoLists = new LinkedList<>();
|
todoLists = new LinkedList<>();
|
||||||
for (WebDavResource resource : possibleCalendars)
|
for (WebDavResource resource : possibleCalendars) {
|
||||||
if (resource.isCalendar()) {
|
final WebDavResource.Properties properties = resource.getProperties();
|
||||||
|
if (properties.isCalendar()) {
|
||||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||||
resource.isReadOnly(),
|
properties.isReadOnly(),
|
||||||
resource.getLocation().toString(),
|
resource.getLocation().toString(),
|
||||||
resource.getDisplayName(),
|
properties.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
properties.getDescription(), properties.getColor()
|
||||||
);
|
);
|
||||||
info.setTimezone(resource.getTimezone());
|
info.setTimezone(properties.getTimeZone());
|
||||||
|
|
||||||
boolean isCalendar = false,
|
boolean isCalendar = false,
|
||||||
isTodoList = false;
|
isTodoList = false;
|
||||||
if (resource.getSupportedComponents() == null) {
|
if (properties.getSupportedComponents() == null) {
|
||||||
// no info about supported components, assuming all components are supported
|
// no info about supported components, assuming all components are supported
|
||||||
isCalendar = true;
|
isCalendar = true;
|
||||||
isTodoList = true;
|
isTodoList = true;
|
||||||
} else {
|
} else {
|
||||||
// CALDAV:supported-calendar-component-set available
|
// CALDAV:supported-calendar-component-set available
|
||||||
for (String supportedComponent : resource.getSupportedComponents())
|
for (String supportedComponent : properties.getSupportedComponents())
|
||||||
if ("VEVENT".equalsIgnoreCase(supportedComponent))
|
if ("VEVENT".equalsIgnoreCase(supportedComponent))
|
||||||
isCalendar = true;
|
isCalendar = true;
|
||||||
else if ("VTODO".equalsIgnoreCase(supportedComponent))
|
else if ("VTODO".equalsIgnoreCase(supportedComponent))
|
||||||
@ -162,6 +164,7 @@ public class DavResourceFinder implements Closeable {
|
|||||||
if (isTodoList)
|
if (isTodoList)
|
||||||
todoLists.add(new ServerInfo.ResourceInfo(info));
|
todoLists.add(new ServerInfo.ResourceInfo(info));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverInfo.setCalendars(calendars);
|
serverInfo.setCalendars(calendars);
|
||||||
serverInfo.setTodoLists(todoLists);
|
serverInfo.setTodoLists(todoLists);
|
||||||
@ -271,8 +274,8 @@ public class DavResourceFinder implements Closeable {
|
|||||||
try {
|
try {
|
||||||
WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName);
|
WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName);
|
||||||
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
if (wellKnown.getCurrentUserPrincipal() != null) {
|
if (wellKnown.getProperties().getCurrentUserPrincipal() != null) {
|
||||||
URI principal = wellKnown.getCurrentUserPrincipal();
|
URI principal = wellKnown.getProperties().getCurrentUserPrincipal();
|
||||||
Log.i(TAG, "Principal URL found from Well-Known URI: " + principal);
|
Log.i(TAG, "Principal URL found from Well-Known URI: " + principal);
|
||||||
return new WebDavResource(wellKnown, 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);
|
Log.d(TAG, "Well-known service detection failed, trying initial context path " + initialURL);
|
||||||
try {
|
try {
|
||||||
base.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
base.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
if (base.getCurrentUserPrincipal() != null) {
|
if (base.getProperties().getCurrentUserPrincipal() != null) {
|
||||||
URI principal = base.getCurrentUserPrincipal();
|
URI principal = base.getProperties().getCurrentUserPrincipal();
|
||||||
Log.i(TAG, "Principal URL found from initial context path: " + principal);
|
Log.i(TAG, "Principal URL found from initial context path: " + principal);
|
||||||
return new WebDavResource(base, 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.ComponentList;
|
||||||
import net.fortuna.ical4j.model.Date;
|
import net.fortuna.ical4j.model.Date;
|
||||||
import net.fortuna.ical4j.model.DateTime;
|
import net.fortuna.ical4j.model.DateTime;
|
||||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
|
||||||
import net.fortuna.ical4j.model.Property;
|
import net.fortuna.ical4j.model.Property;
|
||||||
import net.fortuna.ical4j.model.PropertyList;
|
import net.fortuna.ical4j.model.PropertyList;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
|
||||||
import net.fortuna.ical4j.model.ValidationException;
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
import net.fortuna.ical4j.model.component.VAlarm;
|
import net.fortuna.ical4j.model.component.VAlarm;
|
||||||
import net.fortuna.ical4j.model.component.VEvent;
|
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.Attendee;
|
||||||
import net.fortuna.ical4j.model.property.Clazz;
|
import net.fortuna.ical4j.model.property.Clazz;
|
||||||
import net.fortuna.ical4j.model.property.DateProperty;
|
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.Transp;
|
||||||
import net.fortuna.ical4j.model.property.Uid;
|
import net.fortuna.ical4j.model.property.Uid;
|
||||||
import net.fortuna.ical4j.model.property.Version;
|
import net.fortuna.ical4j.model.property.Version;
|
||||||
import net.fortuna.ical4j.util.CompatibilityHints;
|
import net.fortuna.ical4j.util.TimeZones;
|
||||||
import net.fortuna.ical4j.util.SimpleHostInfo;
|
|
||||||
import net.fortuna.ical4j.util.UidGenerator;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -63,7 +56,6 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
import at.bitfire.davdroid.Constants;
|
import at.bitfire.davdroid.Constants;
|
||||||
import at.bitfire.davdroid.DateUtils;
|
import at.bitfire.davdroid.DateUtils;
|
||||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@ -156,15 +148,13 @@ public class Event extends iCalendar {
|
|||||||
if ((dtStart = event.getStartDate()) == null || (dtEnd = event.getEndDate()) == null)
|
if ((dtStart = event.getStartDate()) == null || (dtEnd = event.getEndDate()) == null)
|
||||||
throw new InvalidResourceException("Invalid start time/end time/duration");
|
throw new InvalidResourceException("Invalid start time/end time/duration");
|
||||||
|
|
||||||
if (hasTime(dtStart)) {
|
validateTimeZone(dtStart);
|
||||||
validateTimeZone(dtStart);
|
validateTimeZone(dtEnd);
|
||||||
validateTimeZone(dtEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// all-day events and "events on that day":
|
// all-day events and "events on that day":
|
||||||
// * related UNIX times must be in UTC
|
// * related UNIX times must be in UTC
|
||||||
// * must have a duration (set to one day if missing)
|
// * 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");
|
Log.i(TAG, "Repairing iCal: DTEND := DTSTART+1");
|
||||||
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(Time.TIMEZONE_UTC));
|
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(Time.TIMEZONE_UTC));
|
||||||
c.setTime(dtStart.getDate());
|
c.setTime(dtStart.getDate());
|
||||||
@ -304,8 +294,20 @@ public class Event extends iCalendar {
|
|||||||
|
|
||||||
// time helpers
|
// 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() {
|
public boolean isAllDay() {
|
||||||
return !hasTime(dtStart);
|
return !isDateTime(dtStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDtStartInMillis() {
|
public long getDtStartInMillis() {
|
||||||
|
@ -17,7 +17,6 @@ import android.content.Entity;
|
|||||||
import android.content.EntityIterator;
|
import android.content.EntityIterator;
|
||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
@ -48,7 +47,6 @@ import java.io.InputStream;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -127,9 +125,9 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
accountSettings.setAddressBookVCardVersion(vCardVersion != null ? vCardVersion : VCardVersion.V3_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import android.database.Cursor;
|
|||||||
import android.database.DatabaseUtils;
|
import android.database.DatabaseUtils;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
import android.provider.CalendarContract.Attendees;
|
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.CuType;
|
||||||
import net.fortuna.ical4j.model.parameter.PartStat;
|
import net.fortuna.ical4j.model.parameter.PartStat;
|
||||||
import net.fortuna.ical4j.model.parameter.Role;
|
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.Action;
|
||||||
import net.fortuna.ical4j.model.property.Attendee;
|
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.Description;
|
||||||
import net.fortuna.ical4j.model.property.Duration;
|
import net.fortuna.ical4j.model.property.Duration;
|
||||||
import net.fortuna.ical4j.model.property.ExDate;
|
import net.fortuna.ical4j.model.property.ExDate;
|
||||||
@ -61,10 +58,8 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import at.bitfire.davdroid.DAVUtils;
|
import at.bitfire.davdroid.DAVUtils;
|
||||||
import at.bitfire.davdroid.DateUtils;
|
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.ACCOUNT_TYPE, account.type);
|
||||||
values.put(Calendars.NAME, info.getURL());
|
values.put(Calendars.NAME, info.getURL());
|
||||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
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.OWNER_ACCOUNT, account.name);
|
||||||
values.put(Calendars.SYNC_EVENTS, 1);
|
values.put(Calendars.SYNC_EVENTS, 1);
|
||||||
values.put(Calendars.VISIBLE, 1);
|
values.put(Calendars.VISIBLE, 1);
|
||||||
@ -139,7 +134,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (info.getTimezone() != null)
|
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());
|
Log.i(TAG, "Inserting calendar: " + values.toString());
|
||||||
try {
|
try {
|
||||||
@ -196,16 +191,16 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMetaData(WebDavResource resource) throws LocalStorageException {
|
public void updateMetaData(WebDavResource.Properties properties) throws LocalStorageException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
final String displayName = resource.getDisplayName();
|
final String displayName = properties.getDisplayName();
|
||||||
if (displayName != null)
|
if (displayName != null)
|
||||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, displayName);
|
values.put(Calendars.CALENDAR_DISPLAY_NAME, displayName);
|
||||||
|
|
||||||
final String color = resource.getColor();
|
final Integer color = properties.getColor();
|
||||||
if (color != null)
|
if (color != null)
|
||||||
values.put(Calendars.CALENDAR_COLOR, DAVUtils.CalDAVtoARGBColor(color));
|
values.put(Calendars.CALENDAR_COLOR, color);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (values.size() > 0)
|
if (values.size() > 0)
|
||||||
|
@ -18,7 +18,6 @@ import android.content.OperationApplicationException;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
import android.database.DatabaseUtils;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -26,7 +25,6 @@ import android.util.Log;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -96,7 +94,7 @@ public abstract class LocalCollection<T extends Resource> {
|
|||||||
/** gets the CTag of the collection */
|
/** gets the CTag of the collection */
|
||||||
abstract public String getCTag() throws LocalStorageException;
|
abstract public String getCTag() throws LocalStorageException;
|
||||||
/** update locally stored collection properties (e.g. display name and color) from a WebDavResource */
|
/** 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
|
// content provider (= database) querying
|
||||||
|
@ -18,7 +18,6 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import net.fortuna.ical4j.model.Date;
|
import net.fortuna.ical4j.model.Date;
|
||||||
@ -36,7 +35,6 @@ import net.fortuna.ical4j.util.TimeZones;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dmfs.provider.tasks.TaskContract;
|
import org.dmfs.provider.tasks.TaskContract;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import at.bitfire.davdroid.DAVUtils;
|
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.ACCOUNT_TYPE, account.type);
|
||||||
values.put(TaskContract.TaskLists._SYNC_ID, info.getURL());
|
values.put(TaskContract.TaskLists._SYNC_ID, info.getURL());
|
||||||
values.put(TaskContract.TaskLists.LIST_NAME, info.getTitle());
|
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.OWNER, account.name);
|
||||||
values.put(TaskContract.TaskLists.ACCESS_LEVEL, 0);
|
values.put(TaskContract.TaskLists.ACCESS_LEVEL, 0);
|
||||||
values.put(TaskContract.TaskLists.SYNC_ENABLED, 1);
|
values.put(TaskContract.TaskLists.SYNC_ENABLED, 1);
|
||||||
@ -135,16 +133,16 @@ public class LocalTaskList extends LocalCollection<Task> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMetaData(WebDavResource resource) throws LocalStorageException {
|
public void updateMetaData(WebDavResource.Properties properties) throws LocalStorageException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
final String displayName = resource.getDisplayName();
|
final String displayName = properties.getDisplayName();
|
||||||
if (displayName != null)
|
if (displayName != null)
|
||||||
values.put(TaskContract.TaskLists.LIST_NAME, displayName);
|
values.put(TaskContract.TaskLists.LIST_NAME, displayName);
|
||||||
|
|
||||||
final String color = resource.getColor();
|
final Integer color = properties.getColor();
|
||||||
if (color != null)
|
if (color != null)
|
||||||
values.put(TaskContract.TaskLists.LIST_COLOR, DAVUtils.CalDAVtoARGBColor(color));
|
values.put(TaskContract.TaskLists.LIST_COLOR, color);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (values.size() > 0)
|
if (values.size() > 0)
|
||||||
|
@ -13,7 +13,6 @@ import java.net.URI;
|
|||||||
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;
|
||||||
|
|
||||||
@ -56,8 +55,8 @@ public class ServerInfo implements Serializable {
|
|||||||
|
|
||||||
final String URL, // absolute URL of resource
|
final String URL, // absolute URL of resource
|
||||||
title,
|
title,
|
||||||
description,
|
description;
|
||||||
color;
|
final Integer color;
|
||||||
|
|
||||||
String timezone;
|
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.Uid;
|
||||||
import net.fortuna.ical4j.model.property.Url;
|
import net.fortuna.ical4j.model.property.Url;
|
||||||
import net.fortuna.ical4j.model.property.Version;
|
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.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -45,7 +43,6 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
import at.bitfire.davdroid.Constants;
|
import at.bitfire.davdroid.Constants;
|
||||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ public abstract class WebDavCollection<T extends Resource> {
|
|||||||
List<T> resources = new LinkedList<>();
|
List<T> resources = new LinkedList<>();
|
||||||
if (collection.getMembers() != null)
|
if (collection.getMembers() != null)
|
||||||
for (WebDavResource member : collection.getMembers())
|
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()]);
|
return resources.toArray(new Resource[resources.size()]);
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ public abstract class WebDavCollection<T extends Resource> {
|
|||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
|
|
||||||
for (WebDavResource member : collection.getMembers()) {
|
for (WebDavResource member : collection.getMembers()) {
|
||||||
T resource = newResourceSkeleton(member.getName(), member.getETag());
|
T resource = newResourceSkeleton(member.getName(), member.getProperties().getETag());
|
||||||
try {
|
try {
|
||||||
if (member.getContent() != null) {
|
if (member.getContent() != null) {
|
||||||
@Cleanup InputStream is = new ByteArrayInputStream(member.getContent());
|
@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
|
// returns ETag of the created resource, if returned by server
|
||||||
public String add(Resource res) throws URISyntaxException, IOException, HttpException {
|
public String add(Resource res) throws URISyntaxException, IOException, HttpException {
|
||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.setContentType(res.getMimeType());
|
member.getProperties().setContentType(res.getMimeType());
|
||||||
|
|
||||||
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
||||||
String eTag = member.put(os.toByteArray(), PutMode.ADD_DONT_OVERWRITE);
|
String eTag = member.put(os.toByteArray(), PutMode.ADD_DONT_OVERWRITE);
|
||||||
|
|
||||||
// after a successful upload, the collection has implicitely changed, too
|
// after a successful upload, the collection has implicitely changed, too
|
||||||
collection.invalidateCTag();
|
collection.getProperties().invalidateCTag();
|
||||||
|
|
||||||
return eTag;
|
return eTag;
|
||||||
}
|
}
|
||||||
@ -173,19 +173,19 @@ public abstract class WebDavCollection<T extends Resource> {
|
|||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.delete();
|
member.delete();
|
||||||
|
|
||||||
collection.invalidateCTag();
|
collection.getProperties().invalidateCTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns ETag of the updated resource, if returned by server
|
// returns ETag of the updated resource, if returned by server
|
||||||
public String update(Resource res) throws URISyntaxException, IOException, HttpException {
|
public String update(Resource res) throws URISyntaxException, IOException, HttpException {
|
||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.setContentType(res.getMimeType());
|
member.getProperties().setContentType(res.getMimeType());
|
||||||
|
|
||||||
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
@Cleanup ByteArrayOutputStream os = res.toEntity();
|
||||||
String eTag = member.put(os.toByteArray(), PutMode.UPDATE_DONT_OVERWRITE);
|
String eTag = member.put(os.toByteArray(), PutMode.UPDATE_DONT_OVERWRITE);
|
||||||
|
|
||||||
// after a successful upload, the collection has implicitely changed, too
|
// after a successful upload, the collection has implicitely changed, too
|
||||||
collection.invalidateCTag();
|
collection.getProperties().invalidateCTag();
|
||||||
|
|
||||||
return eTag;
|
return eTag;
|
||||||
}
|
}
|
||||||
|
@ -8,29 +8,24 @@
|
|||||||
|
|
||||||
package at.bitfire.davdroid.resource;
|
package at.bitfire.davdroid.resource;
|
||||||
|
|
||||||
import android.text.format.Time;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import net.fortuna.ical4j.data.CalendarBuilder;
|
import net.fortuna.ical4j.data.CalendarBuilder;
|
||||||
|
import net.fortuna.ical4j.data.ParserException;
|
||||||
import net.fortuna.ical4j.model.DateTime;
|
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.component.VTimeZone;
|
||||||
import net.fortuna.ical4j.model.parameter.Value;
|
|
||||||
import net.fortuna.ical4j.model.property.DateProperty;
|
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.CompatibilityHints;
|
||||||
import net.fortuna.ical4j.util.SimpleHostInfo;
|
import net.fortuna.ical4j.util.SimpleHostInfo;
|
||||||
import net.fortuna.ical4j.util.UidGenerator;
|
import net.fortuna.ical4j.util.UidGenerator;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import at.bitfire.davdroid.DateUtils;
|
import at.bitfire.davdroid.DateUtils;
|
||||||
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
|
||||||
import lombok.Getter;
|
import lombok.NonNull;
|
||||||
|
|
||||||
public abstract class iCalendar extends Resource {
|
public abstract class iCalendar extends Resource {
|
||||||
static private final String TAG = "DAVdroid.iCal";
|
static private final String TAG = "DAVdroid.iCal";
|
||||||
@ -72,47 +67,46 @@ public abstract class iCalendar extends Resource {
|
|||||||
|
|
||||||
// time zone helpers
|
// time zone helpers
|
||||||
|
|
||||||
protected static boolean hasTime(DateProperty date) {
|
protected static boolean isDateTime(DateProperty date) {
|
||||||
return date.getDate() instanceof DateTime;
|
return date.getDate() instanceof DateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static String getTzId(DateProperty date) {
|
/**
|
||||||
if (date.isUtc() || !hasTime(date))
|
* Ensures that a given DateProperty has a time zone with an ID that is available in Android.
|
||||||
return Time.TIMEZONE_UTC;
|
* @param date DateProperty to validate. Values which are not DATE-TIME will be ignored.
|
||||||
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 */
|
|
||||||
protected static void validateTimeZone(DateProperty date) {
|
protected static void validateTimeZone(DateProperty date) {
|
||||||
if (date.isUtc() || !hasTime(date))
|
if (isDateTime(date)) {
|
||||||
return;
|
final TimeZone tz = date.getTimeZone();
|
||||||
|
if (tz == null)
|
||||||
|
return;
|
||||||
|
final String tzID = tz.getID();
|
||||||
|
if (tzID == null)
|
||||||
|
return;
|
||||||
|
|
||||||
String tzID = getTzId(date);
|
String deviceTzID = DateUtils.findAndroidTimezoneID(tzID);
|
||||||
if (tzID == null)
|
if (!tzID.equals(deviceTzID))
|
||||||
return;
|
date.setTimeZone(DateUtils.tzRegistry.getTimeZone(deviceTzID));
|
||||||
|
}
|
||||||
String localTZ = DateUtils.findAndroidTimezoneID(tzID);
|
|
||||||
date.setTimeZone(DateUtils.tzRegistry.getTimeZone(localTZ));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
if (timezoneDef != null) {
|
CalendarBuilder builder = new CalendarBuilder();
|
||||||
CalendarBuilder builder = new CalendarBuilder();
|
net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef));
|
||||||
net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef));
|
VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE);
|
||||||
VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE);
|
if (timezone != null && timezone.getTimeZoneId() != null)
|
||||||
return timezone.getTimeZoneId().getValue();
|
return timezone.getTimeZoneId().getValue();
|
||||||
}
|
} catch (IOException|ParserException e) {
|
||||||
} catch (Exception ex) {
|
Log.e(TAG, "Can't understand time zone definition", e);
|
||||||
Log.w(TAG, "Can't understand time zone definition, ignoring", ex);
|
|
||||||
}
|
}
|
||||||
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.LocalCollection;
|
||||||
import at.bitfire.davdroid.resource.LocalStorageException;
|
import at.bitfire.davdroid.resource.LocalStorageException;
|
||||||
import at.bitfire.davdroid.resource.RecordNotFoundException;
|
import at.bitfire.davdroid.resource.RecordNotFoundException;
|
||||||
import at.bitfire.davdroid.resource.WebDavCollection;
|
|
||||||
import at.bitfire.davdroid.resource.Resource;
|
import at.bitfire.davdroid.resource.Resource;
|
||||||
|
import at.bitfire.davdroid.resource.WebDavCollection;
|
||||||
import at.bitfire.davdroid.webdav.ConflictException;
|
import at.bitfire.davdroid.webdav.ConflictException;
|
||||||
import at.bitfire.davdroid.webdav.DavException;
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
import at.bitfire.davdroid.webdav.HttpException;
|
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 {
|
public void synchronize(boolean manualSync, SyncResult syncResult) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
|
||||||
// PHASE 1: fetch collection properties
|
// PHASE 1: fetch collection properties
|
||||||
remote.getProperties();
|
remote.getProperties();
|
||||||
final WebDavResource collectionResource = remote.getCollection();
|
final WebDavResource.Properties collectionProperties = remote.getCollection().getProperties();
|
||||||
local.updateMetaData(collectionResource);
|
local.updateMetaData(collectionProperties);
|
||||||
|
|
||||||
// PHASE 2: push local changes to server
|
// PHASE 2: push local changes to server
|
||||||
int deletedRemotely = pushDeleted(),
|
int deletedRemotely = pushDeleted(),
|
||||||
@ -62,7 +62,7 @@ public class SyncManager {
|
|||||||
}
|
}
|
||||||
if (!syncMembers) {
|
if (!syncMembers) {
|
||||||
final String
|
final String
|
||||||
currentCTag = collectionResource.getCTag(),
|
currentCTag = collectionProperties.getCTag(),
|
||||||
lastCTag = local.getCTag();
|
lastCTag = local.getCTag();
|
||||||
Log.d(TAG, "Last local CTag = " + lastCTag + "; current remote CTag = " + currentCTag);
|
Log.d(TAG, "Last local CTag = " + lastCTag + "; current remote CTag = " + currentCTag);
|
||||||
if (currentCTag == null || !currentCTag.equals(lastCTag))
|
if (currentCTag == null || !currentCTag.equals(lastCTag))
|
||||||
@ -101,7 +101,7 @@ public class SyncManager {
|
|||||||
|
|
||||||
// update collection CTag
|
// update collection CTag
|
||||||
Log.i(TAG, "Sync complete, fetching new 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;
|
package at.bitfire.davdroid.ui.setup;
|
||||||
|
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentManager;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
|
@ -41,18 +41,19 @@ import java.io.StringWriter;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import at.bitfire.davdroid.DAVUtils;
|
||||||
import at.bitfire.davdroid.URIUtils;
|
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 at.bitfire.davdroid.webdav.DavProp.Comp;
|
||||||
import ezvcard.VCardVersion;
|
import ezvcard.VCardVersion;
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
|
|
||||||
@ -64,15 +65,6 @@ import lombok.ToString;
|
|||||||
public class WebDavResource {
|
public class WebDavResource {
|
||||||
private static final String TAG = "davdroid.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 {
|
public enum PutMode {
|
||||||
ADD_DONT_OVERWRITE,
|
ADD_DONT_OVERWRITE,
|
||||||
UPDATE_DONT_OVERWRITE
|
UPDATE_DONT_OVERWRITE
|
||||||
@ -85,11 +77,8 @@ public class WebDavResource {
|
|||||||
protected Set<String> capabilities = new HashSet<>(),
|
protected Set<String> capabilities = new HashSet<>(),
|
||||||
methods = 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)
|
// list of members (only for collections)
|
||||||
|
@Getter Properties properties = new Properties();
|
||||||
@Getter protected List<WebDavResource> members;
|
@Getter protected List<WebDavResource> members;
|
||||||
|
|
||||||
// content (available after GET)
|
// content (available after GET)
|
||||||
@ -151,9 +140,9 @@ public class WebDavResource {
|
|||||||
location = parent.location.resolve(new URI(null, null, "./" + member, null));
|
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);
|
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 */
|
/* collection operations */
|
||||||
|
|
||||||
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
@ -372,12 +291,12 @@ public class WebDavResource {
|
|||||||
put.addHeader("If-None-Match", "*");
|
put.addHeader("If-None-Match", "*");
|
||||||
break;
|
break;
|
||||||
case UPDATE_DONT_OVERWRITE:
|
case UPDATE_DONT_OVERWRITE:
|
||||||
put.addHeader("If-Match", (getETag() != null) ? getETag() : "*");
|
put.addHeader("If-Match", (properties.eTag != null) ? properties.eTag : "*");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getContentType() != null)
|
if (properties.contentType != null)
|
||||||
put.addHeader("Content-Type", getContentType());
|
put.addHeader("Content-Type", properties.contentType);
|
||||||
|
|
||||||
@Cleanup CloseableHttpResponse response = httpClient.execute(put, context);
|
@Cleanup CloseableHttpResponse response = httpClient.execute(put, context);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
@ -392,8 +311,8 @@ public class WebDavResource {
|
|||||||
public void delete() throws URISyntaxException, IOException, HttpException {
|
public void delete() throws URISyntaxException, IOException, HttpException {
|
||||||
HttpDeleteHC4 delete = new HttpDeleteHC4(location);
|
HttpDeleteHC4 delete = new HttpDeleteHC4(location);
|
||||||
|
|
||||||
if (getETag() != null)
|
if (properties.eTag != null)
|
||||||
delete.addHeader("If-Match", getETag());
|
delete.addHeader("If-Match", properties.eTag);
|
||||||
|
|
||||||
@Cleanup CloseableHttpResponse response = httpClient.execute(delete, context);
|
@Cleanup CloseableHttpResponse response = httpClient.execute(delete, context);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
@ -473,8 +392,7 @@ public class WebDavResource {
|
|||||||
Log.d(TAG, "Processing multi-status element: " + href);
|
Log.d(TAG, "Processing multi-status element: " + href);
|
||||||
|
|
||||||
// process known properties
|
// process known properties
|
||||||
HashMap<Property, String> properties = new HashMap<>();
|
Properties properties = new Properties();
|
||||||
List<String> supportedComponents = null;
|
|
||||||
byte[] data = null;
|
byte[] data = null;
|
||||||
|
|
||||||
// in <response>, either <status> or <propstat> must be present
|
// in <response>, either <status> or <propstat> must be present
|
||||||
@ -488,84 +406,9 @@ public class WebDavResource {
|
|||||||
// ignore information about missing properties etc.
|
// ignore information about missing properties etc.
|
||||||
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DavProp prop = singlePropstat.prop;
|
DavProp prop = singlePropstat.prop;
|
||||||
|
properties.process(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());
|
|
||||||
|
|
||||||
if (prop.calendarData != null && prop.calendarData.ical != null)
|
if (prop.calendarData != null && prop.calendarData.ical != null)
|
||||||
data = prop.calendarData.ical.getBytes();
|
data = prop.calendarData.ical.getBytes();
|
||||||
@ -574,16 +417,16 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// about which resource is this response?
|
// 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
|
if (location.equals(href) || URIUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves
|
||||||
this.properties.putAll(properties);
|
this.properties = properties;
|
||||||
if (supportedComponents != null)
|
|
||||||
this.supportedComponents = supportedComponents;
|
|
||||||
this.content = data;
|
this.content = data;
|
||||||
|
|
||||||
} else { // about a member
|
} else { // about a member
|
||||||
WebDavResource member = new WebDavResource(this, href);
|
WebDavResource member = new WebDavResource(this, href);
|
||||||
member.properties = properties;
|
member.properties = properties;
|
||||||
member.supportedComponents = supportedComponents;
|
|
||||||
member.content = data;
|
member.content = data;
|
||||||
|
|
||||||
members.add(member);
|
members.add(member);
|
||||||
@ -593,4 +436,133 @@ public class WebDavResource {
|
|||||||
this.members = members;
|
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