From faeb3b7dd0bcddf54cf34017c00f3f2eea6a174f Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Mon, 10 Aug 2015 00:33:26 +0200 Subject: [PATCH] Refactoring * VEvent: don't set LAST-MODIFIED to sync time (should be last modification time which is not available) * ignore 403 Forbidden when uploading (can happen on certain scheduling conditions) --- .../at/bitfire/davdroid/resource/Event.java | 2 -- .../davdroid/resource/LocalCalendar.java | 30 ++++++++++--------- .../davdroid/syncadapter/SyncManager.java | 14 +++++---- .../davdroid/webdav/ForbiddenException.java | 19 ++++++++++++ .../davdroid/webdav/WebDavResource.java | 2 ++ app/src/main/res/values-de/strings.xml | 2 +- 6 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/at/bitfire/davdroid/webdav/ForbiddenException.java diff --git a/app/src/main/java/at/bitfire/davdroid/resource/Event.java b/app/src/main/java/at/bitfire/davdroid/resource/Event.java index 767c78bc..126203e1 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/Event.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/Event.java @@ -293,8 +293,6 @@ public class Event extends iCalendar { event.getProperties().add(forPublic ? Clazz.PUBLIC : Clazz.PRIVATE); event.getAlarms().addAll(alarms); - - props.add(new LastModified()); return event; } diff --git a/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.java b/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.java index 56a8d426..13245c8f 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.java @@ -425,12 +425,13 @@ public class LocalCalendar extends LocalCollection { // availability e.opaque = values.getAsInteger(Events.AVAILABILITY) != Events.AVAILABILITY_FREE; - // set ORGANIZER - try { - e.organizer = new Organizer(new URI("mailto", values.getAsString(Events.ORGANIZER), null)); - } catch (URISyntaxException ex) { - Log.e(TAG, "Error when creating ORGANIZER mailto URI, ignoring", ex); - } + // set ORGANIZER if there's attendee data + if (values.getAsInteger(Events.HAS_ATTENDEE_DATA) != 0) + try { + e.organizer = new Organizer(new URI("mailto", values.getAsString(Events.ORGANIZER), null)); + } catch (URISyntaxException ex) { + Log.e(TAG, "Error when creating ORGANIZER mailto URI, ignoring", ex); + } // classification switch (values.getAsInteger(Events.ACCESS_LEVEL)) { @@ -553,8 +554,8 @@ public class LocalCalendar extends LocalCollection { .withValue(entryColumnETag(), event.getETag()) .withValue(entryColumnUID(), event.getUid()); } else { + // event is an exception builder.withValue(Events.ORIGINAL_SYNC_ID, event.getName()); - // ORIGINAL_INSTANCE_TIME and ORIGINAL_ALL_DAY is set in buildExceptions. // It's not possible to use only the RECURRENCE-ID to calculate // ORIGINAL_INSTANCE_TIME and ORIGINAL_ALL_DAY because iCloud sends DATE-TIME @@ -616,7 +617,6 @@ public class LocalCalendar extends LocalCollection { builder.withValue(Events.ORGANIZER, email != null ? email : uri.toString()); } - //Status status = event.getStatus(); if (event.status!= null) { int statusCode = Events.STATUS_TENTATIVE; if (event.status == Status.VEVENT_CONFIRMED) @@ -639,15 +639,15 @@ public class LocalCalendar extends LocalCollection { protected void addDataRows(Resource resource, long localID, int backrefIdx) { final Event event = (Event)resource; - // add exceptions - for (Event exception : event.getExceptions()) - pendingOperations.add(buildException(newDataInsertBuilder(Events.CONTENT_URI, Events.ORIGINAL_ID, localID, backrefIdx), event, exception).build()); // add attendees for (Attendee attendee : event.getAttendees()) pendingOperations.add(buildAttendee(newDataInsertBuilder(Attendees.CONTENT_URI, Attendees.EVENT_ID, localID, backrefIdx), attendee).build()); // add reminders for (VAlarm alarm : event.getAlarms()) pendingOperations.add(buildReminder(newDataInsertBuilder(Reminders.CONTENT_URI, Reminders.EVENT_ID, localID, backrefIdx), alarm).build()); + // add exceptions + for (Event exception : event.getExceptions()) + pendingOperations.add(buildException(newDataInsertBuilder(Events.CONTENT_URI, Events.ORIGINAL_ID, localID, backrefIdx), event, exception).build()); } @Override @@ -668,7 +668,6 @@ public class LocalCalendar extends LocalCollection { protected Builder buildException(Builder builder, Event master, Event exception) { buildEntry(builder, exception, false); - builder.withValue(Events.ORIGINAL_SYNC_ID, exception.getName()); final boolean originalAllDay = master.isAllDay(); @@ -685,12 +684,16 @@ public class LocalCalendar extends LocalCollection { builder.withValue(Events.ORIGINAL_INSTANCE_TIME, date.getTime()); builder.withValue(Events.ORIGINAL_ALL_DAY, originalAllDay ? 1 : 0); + + /* TODO reminders and attendees for exceptions are currently not built because + * there's no backref index available */ + return builder; } @SuppressLint("InlinedApi") protected Builder buildAttendee(Builder builder, Attendee attendee) { - final Uri member = Uri.parse(attendee.getValue()); + final URI member = attendee.getCalAddress(); if ("mailto".equalsIgnoreCase(member.getScheme())) // attendee identified by email builder = builder.withValue(Attendees.ATTENDEE_EMAIL, member.getSchemeSpecificPart()); @@ -760,7 +763,6 @@ public class LocalCalendar extends LocalCollection { } Log.d(TAG, "Adding alarm " + minutes + " minutes before"); - return builder .withValue(Reminders.METHOD, Reminders.METHOD_ALERT) .withValue(Reminders.MINUTES, minutes); diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java index be0193a4..b4ca1aa1 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java @@ -23,6 +23,7 @@ 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.ForbiddenException; import at.bitfire.davdroid.webdav.HttpException; import at.bitfire.davdroid.webdav.NotFoundException; import at.bitfire.davdroid.webdav.PreconditionFailedException; @@ -119,11 +120,10 @@ public class SyncManager { remote.delete(res); } catch(NotFoundException e) { Log.i(TAG, "Locally-deleted resource has already been removed from server"); - } catch(PreconditionFailedException e) { + } catch(PreconditionFailedException|ConflictException e) { Log.i(TAG, "Locally-deleted resource has been changed on the server in the meanwhile"); } - - // always delete locally so that the record with the DELETED flag doesn't cause another deletion attempt + local.delete(res); count++; @@ -149,7 +149,7 @@ public class SyncManager { local.updateETag(res, eTag); local.clearDirty(res); count++; - } catch (PreconditionFailedException|ConflictException e) { + } catch (ConflictException|PreconditionFailedException e) { Log.i(TAG, "Didn't overwrite existing resource with other content"); } catch (RecordNotFoundException e) { Log.wtf(TAG, "Couldn't read new record", e); @@ -173,8 +173,10 @@ public class SyncManager { local.updateETag(res, eTag); local.clearDirty(res); count++; - } catch (PreconditionFailedException|ConflictException e) { - Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile"); + } catch (ForbiddenException e) { + Log.w(TAG, "Server has rejected local changes, server wins", e); + } catch (ConflictException|PreconditionFailedException e) { + Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile", e); } catch (RecordNotFoundException e) { Log.e(TAG, "Couldn't read dirty record", e); } diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/ForbiddenException.java b/app/src/main/java/at/bitfire/davdroid/webdav/ForbiddenException.java new file mode 100644 index 00000000..599c12e7 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/webdav/ForbiddenException.java @@ -0,0 +1,19 @@ +/* + * 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.webdav; + +import org.apache.http.HttpStatus; + +public class ForbiddenException extends HttpException { + private static final long serialVersionUID = 102282229174086113L; + + public ForbiddenException(String reason) { + super(HttpStatus.SC_FORBIDDEN, reason); + } +} diff --git a/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java b/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java index 25d0e983..1d47bc4c 100644 --- a/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java +++ b/app/src/main/java/at/bitfire/davdroid/webdav/WebDavResource.java @@ -345,6 +345,8 @@ public class WebDavResource { switch (code) { case HttpStatus.SC_UNAUTHORIZED: throw new NotAuthorizedException(reason); + case HttpStatus.SC_FORBIDDEN: + throw new ForbiddenException(reason); case HttpStatus.SC_NOT_FOUND: throw new NotFoundException(reason); case HttpStatus.SC_CONFLICT: diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0e10d646..0e34a073 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -76,7 +76,7 @@ für DAVdroid spenden oder die App kaufen.

Lizenz

-

Copyright © 2013 – 2014 Ricki Hirner, Bernhard Stockmann (bitfire web engineering), alle Rechte +

Copyright © 2013 – 2015 Ricki Hirner, Bernhard Stockmann (bitfire web engineering), alle Rechte vorbehalten. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU General Public License Version 3, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren. Sofern Google Play oder Samsung Store andere Bedingungen benötigen, gelten für über den jeweiligen Markt heruntergeladene