diff --git a/src/java/davmail/exchange/VCalendar.java b/src/java/davmail/exchange/VCalendar.java index 9c6699c3..4746706b 100644 --- a/src/java/davmail/exchange/VCalendar.java +++ b/src/java/davmail/exchange/VCalendar.java @@ -573,4 +573,18 @@ public class VCalendar extends VObject { return status; } + public List getModifiedOccurrences() { + boolean first = true; + ArrayList results = new ArrayList(); + for (VObject vObject:vObjects) { + if ("VEVENT".equals(vObject.type)) { + if (first) { + first = false; + } else { + results.add(vObject); + } + } + } + return results; + } } diff --git a/src/java/davmail/exchange/ews/EWSMethod.java b/src/java/davmail/exchange/ews/EWSMethod.java index c8918040..654dbc7d 100644 --- a/src/java/davmail/exchange/ews/EWSMethod.java +++ b/src/java/davmail/exchange/ews/EWSMethod.java @@ -443,6 +443,13 @@ public abstract class EWSMethod extends PostMethod { public String name; } + /** + * Recurring event occurrence + */ + public static class Occurrence { + public String originalStart; + } + /** * Item */ @@ -456,6 +463,7 @@ public abstract class EWSMethod extends PostMethod { protected List attachments; protected List attendees; protected final List fieldNames = new ArrayList(); + protected List occurrences; @Override public String toString() { @@ -613,6 +621,22 @@ public abstract class EWSMethod extends PostMethod { } attendees.add(attendee); } + + /** + * Add occurrence. + * + * @param attendee attendee object + */ + public void addOccurrence(Occurrence occurrence) { + if (occurrences == null) { + occurrences = new ArrayList(); + } + occurrences.add(occurrence); + } + + public List getOccurrences() { + return occurrences; + } } /** @@ -738,6 +762,8 @@ public abstract class EWSMethod extends PostMethod { handleEmailAddresses(reader, responseItem); } else if ("RequiredAttendees".equals(tagLocalName) || "OptionalAttendees".equals(tagLocalName)) { handleAttendees(reader, responseItem, tagLocalName); + } else if ("ModifiedOccurrences".equals(tagLocalName)) { + handleModifiedOccurrences(reader, responseItem); } else { if (tagLocalName.endsWith("Id")) { value = getAttributeValue(reader, "Id"); @@ -780,6 +806,32 @@ public abstract class EWSMethod extends PostMethod { } } + protected void handleModifiedOccurrences(XMLStreamReader reader, Item item) throws XMLStreamException { + while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "ModifiedOccurrences"))) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Occurrence".equals(tagLocalName)) { + handleOccurrence(reader, item); + } + } + } + } + + protected void handleOccurrence(XMLStreamReader reader, Item item) throws XMLStreamException { + while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "Occurrence"))) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("OriginalStart".equals(tagLocalName)) { + Occurrence occurrence = new Occurrence(); + occurrence.originalStart = XMLStreamUtil.getElementText(reader); + item.addOccurrence(occurrence); + } + } + } + } + protected void handleAttendee(XMLStreamReader reader, Item item, String attendeeType) throws XMLStreamException { Attendee attendee = new Attendee(); if ("RequiredAttendees".equals(attendeeType)) { diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index e218f95a..df69afa5 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -24,6 +24,7 @@ import davmail.exception.DavMailException; import davmail.exception.HttpNotFoundException; import davmail.exchange.ExchangeSession; import davmail.exchange.VCalendar; +import davmail.exchange.VObject; import davmail.exchange.VProperty; import davmail.http.DavGatewayHttpClientFacade; import davmail.util.IOUtil; @@ -1306,6 +1307,7 @@ public class EwsExchangeSession extends ExchangeSession { getItemMethod.addAdditionalProperty(Field.get("calendaruid")); getItemMethod.addAdditionalProperty(Field.get("requiredattendees")); getItemMethod.addAdditionalProperty(Field.get("optionalattendees")); + getItemMethod.addAdditionalProperty(Field.get("modifiedoccurrences")); getItemMethod.addAdditionalProperty(Field.get("xmozlastack")); getItemMethod.addAdditionalProperty(Field.get("xmozsnoozetime")); getItemMethod.addAdditionalProperty(Field.get("xmozsendinvitations")); @@ -1339,6 +1341,25 @@ public class EwsExchangeSession extends ExchangeSession { localVCalendar.addFirstVeventProperty(attendeeProperty); } } + // fix UID and RECURRENCE-ID, broken at least on Exchange 2007 + List occurences = getItemMethod.getResponseItem().getOccurrences(); + if (occurences != null) { + Iterator modifiedOccurrencesIterator = localVCalendar.getModifiedOccurrences().iterator(); + for (EWSMethod.Occurrence occurrence : occurences) { + if (modifiedOccurrencesIterator.hasNext()) { + VObject modifiedOccurrence = modifiedOccurrencesIterator.next(); + // fix uid, should be the same as main VEVENT + if (calendaruid != null) { + modifiedOccurrence.setPropertyValue("UID", calendaruid); + } + VProperty recurrenceId = modifiedOccurrence.getProperty("RECURRENCE-ID"); + if (recurrenceId != null) { + recurrenceId.removeParam("TZID"); + recurrenceId.getValues().set(0, convertDateFromExchange(occurrence.originalStart)); + } + } + } + } // restore mozilla invitations option localVCalendar.setFirstVeventPropertyValue("X-MOZ-SEND-INVITATIONS", getItemMethod.getResponseItem().get(Field.get("xmozsendinvitations").getResponseName())); diff --git a/src/java/davmail/exchange/ews/Field.java b/src/java/davmail/exchange/ews/Field.java index ed4b5d24..440a1af7 100644 --- a/src/java/davmail/exchange/ews/Field.java +++ b/src/java/davmail/exchange/ews/Field.java @@ -197,6 +197,7 @@ public final class Field { FIELD_MAP.put("requiredattendees", new UnindexedFieldURI("calendar:RequiredAttendees")); FIELD_MAP.put("optionalattendees", new UnindexedFieldURI("calendar:OptionalAttendees")); + FIELD_MAP.put("modifiedoccurrences", new UnindexedFieldURI("calendar:ModifiedOccurrences")); FIELD_MAP.put("xmozlastack", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.PublicStrings, "xmozlastack")); FIELD_MAP.put("xmozsnoozetime", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.PublicStrings, "xmozsnoozetime")); diff --git a/src/test/davmail/caldav/TestCaldav.java b/src/test/davmail/caldav/TestCaldav.java index 71171834..4cf09d5f 100644 --- a/src/test/davmail/caldav/TestCaldav.java +++ b/src/test/davmail/caldav/TestCaldav.java @@ -135,6 +135,14 @@ public class TestCaldav extends AbstractDavMailTestCase { assertEquals(HttpStatus.SC_OK, method.getStatusCode()); } + public void testPropfindCalendar() throws IOException { + //Settings.setLoggingLevel("httpclient.wire", Level.DEBUG); + PropFindMethod method = new PropFindMethod("/users/" + session.getAlias() + "/calendar/", null, 1); + httpClient.executeMethod(method); + assertEquals(HttpStatus.SC_OK, method.getStatusCode()); + } + + public void testGetOtherUserCalendar() throws IOException { Settings.setLoggingLevel("httpclient.wire", Level.DEBUG); PropFindMethod method = new PropFindMethod("/principals/users/" + Settings.getProperty("davmail.to") + "/calendar/");