diff --git a/src/java/davmail/exchange/VCalendar.java b/src/java/davmail/exchange/VCalendar.java index a09f262b..c4991587 100644 --- a/src/java/davmail/exchange/VCalendar.java +++ b/src/java/davmail/exchange/VCalendar.java @@ -84,8 +84,15 @@ public class VCalendar extends VObject { this(new ICSBufferedReader(new InputStreamReader(new ByteArrayInputStream(vCalendarContent), "UTF-8")), email, vTimezone); } + /** + * Empty constructor + */ + public VCalendar() { + type="VCALENDAR"; + } + @Override - protected void addVObject(VObject vObject) { + public void addVObject(VObject vObject) { super.addVObject(vObject); if (firstVevent == null && "VEVENT".equals(vObject.type)) { firstVevent = vObject; diff --git a/src/java/davmail/exchange/VObject.java b/src/java/davmail/exchange/VObject.java index 0d946eae..4b150e57 100644 --- a/src/java/davmail/exchange/VObject.java +++ b/src/java/davmail/exchange/VObject.java @@ -84,6 +84,9 @@ public class VObject { this(new ICSBufferedReader(new StringReader(itemBody))); } + public VObject() { + } + protected void handleLine(String line, BufferedReader reader) throws IOException { // skip empty lines @@ -98,14 +101,14 @@ public class VObject { } } - protected void addVObject(VObject vObject) { + public void addVObject(VObject vObject) { if (vObjects == null) { vObjects = new ArrayList(); } vObjects.add(vObject); } - protected void addProperty(VProperty property) { + public void addProperty(VProperty property) { if (property.getValue() != null) { if (properties == null) { properties = new ArrayList(); diff --git a/src/java/davmail/exchange/VProperty.java b/src/java/davmail/exchange/VProperty.java index 507321ba..294b9661 100644 --- a/src/java/davmail/exchange/VProperty.java +++ b/src/java/davmail/exchange/VProperty.java @@ -230,7 +230,7 @@ public class VProperty { addParam(paramName, (String) null); } - protected void addParam(String paramName, String paramValue) { + public void addParam(String paramName, String paramValue) { List paramValues = new ArrayList(); paramValues.add(paramValue); addParam(paramName, paramValues); diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 5ae2a1ef..65306f87 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -24,9 +24,7 @@ import davmail.exception.DavMailAuthenticationException; import davmail.exception.DavMailException; import davmail.exception.HttpNotFoundException; import davmail.exception.InsufficientStorageException; -import davmail.exchange.ExchangeSession; -import davmail.exchange.VObject; -import davmail.exchange.XMLStreamUtil; +import davmail.exchange.*; import davmail.http.DavGatewayHttpClientFacade; import davmail.ui.tray.DavGatewayTray; import davmail.util.IOUtil; @@ -49,6 +47,7 @@ import org.apache.jackrabbit.webdav.property.DavPropertySet; import org.w3c.dom.Node; import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimePart; @@ -1279,7 +1278,7 @@ public class DavExchangeSession extends ExchangeSession { */ @Override public byte[] getEventContent() throws IOException { - byte[] result; + byte[] result = null; LOGGER.debug("Get event subject: " + subject + " permanentUrl: " + permanentUrl); // try to get PR_INTERNET_CONTENT try { @@ -1298,11 +1297,105 @@ public class DavExchangeSession extends ExchangeSession { } catch (DavException e) { throw buildHttpException(e); } catch (IOException e) { - deleteBroken(); - throw buildHttpException(e); + LOGGER.warn(e.getMessage()); } catch (MessagingException e) { throw buildHttpException(e); } + + // failover: rebuild event from MAPI properties + if (result == null) { + result = getICSFromItemProperties(); + } + // debug code + //if (new String(result).indexOf("VTODO") < 0) { + // LOGGER.debug("Rebuilt body: " + new String(getICSFromItemProperties())); + //} + + return result; + } + + private byte[] getICSFromItemProperties() throws IOException { + byte[] result = null; + + // experimental: build VCALENDAR from properties + DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet(); + davPropertyNameSet.add(Field.getPropertyName("method")); + + davPropertyNameSet.add(Field.getPropertyName("created")); + davPropertyNameSet.add(Field.getPropertyName("calendarlastmodified")); + davPropertyNameSet.add(Field.getPropertyName("dtstamp")); + davPropertyNameSet.add(Field.getPropertyName("calendaruid")); + davPropertyNameSet.add(Field.getPropertyName("subject")); + davPropertyNameSet.add(Field.getPropertyName("dtstart")); + davPropertyNameSet.add(Field.getPropertyName("dtend")); + davPropertyNameSet.add(Field.getPropertyName("transparent")); + davPropertyNameSet.add(Field.getPropertyName("organizer")); + davPropertyNameSet.add(Field.getPropertyName("to")); + davPropertyNameSet.add(Field.getPropertyName("description")); + davPropertyNameSet.add(Field.getPropertyName("alldayevent")); + davPropertyNameSet.add(Field.getPropertyName("busystatus")); + + PropFindMethod propFindMethod = new PropFindMethod(permanentUrl, davPropertyNameSet, 0); + try { + MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeMethod(httpClient, propFindMethod); + if (responses.length == 0) { + throw new HttpNotFoundException(permanentUrl + " not found"); + } + DavPropertySet davPropertySet = responses[0].getProperties(HttpStatus.SC_OK); + VCalendar localVCalendar = new VCalendar(); + localVCalendar.setPropertyValue("PRODID", "-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN"); + localVCalendar.setPropertyValue("VERSION", "2.0"); + localVCalendar.setPropertyValue("METHOD", getPropertyIfExists(davPropertySet, "method")); + VObject vEvent = new VObject(); + vEvent.type = "VEVENT"; + vEvent.setPropertyValue("CREATED", convertDateFromExchange(getPropertyIfExists(davPropertySet, "created"))); + vEvent.setPropertyValue("LAST-MODIFIED", convertDateFromExchange(getPropertyIfExists(davPropertySet, "calendarlastmodified"))); + vEvent.setPropertyValue("DTSTAMP", convertDateFromExchange(getPropertyIfExists(davPropertySet, "dtstamp"))); + vEvent.setPropertyValue("UID", getPropertyIfExists(davPropertySet, "calendaruid")); + vEvent.setPropertyValue("SUMMARY", getPropertyIfExists(davPropertySet, "subject")); + vEvent.setPropertyValue("DTSTART", convertDateFromExchange(getPropertyIfExists(davPropertySet, "dtstart"))); + vEvent.setPropertyValue("DTEND", convertDateFromExchange(getPropertyIfExists(davPropertySet, "dtend"))); + vEvent.setPropertyValue("TRANSP", getPropertyIfExists(davPropertySet, "transparent")); + String organizer = getPropertyIfExists(davPropertySet, "organizer"); + String organizerEmail = null; + if (organizer != null) { + InternetAddress organizerAddress = new InternetAddress(organizer); + organizerEmail = organizerAddress.getAddress(); + vEvent.setPropertyValue("ORGANIZER", "MAILTO:" + organizerEmail); + } + + // Parse attendee list + String toHeader = getPropertyIfExists(davPropertySet, "to"); + if (toHeader != null && !organizerEmail.equals(toHeader)) { + InternetAddress[] attendees = InternetAddress.parseHeader(toHeader, false); + for (InternetAddress attendee : attendees) { + if (!attendee.getAddress().equalsIgnoreCase(organizerEmail)) { + VProperty vProperty = new VProperty("ATTENDEE", attendee.getAddress()); + if (attendee.getPersonal() != null) { + vProperty.addParam("CN", attendee.getPersonal()); + } + vEvent.addProperty(vProperty); + } + } + + } + vEvent.setPropertyValue("DESCRIPTION", getPropertyIfExists(davPropertySet, "description")); + vEvent.setPropertyValue("X-MICROSOFT-CDO-ALLDAYEVENT", + "1".equals(getPropertyIfExists(davPropertySet, "alldayevent")) ? "TRUE" : "FALSE"); + vEvent.setPropertyValue("X-MICROSOFT-CDO-BUSYSTATUS", getPropertyIfExists(davPropertySet, "busystatus")); + + localVCalendar.addVObject(vEvent); + result = localVCalendar.toString().getBytes("UTF-8"); + } catch (MessagingException e) { + LOGGER.warn("Unable to rebuild event content: " + e.getMessage(), e); + throw buildHttpException(e); + } catch (IOException e) { + LOGGER.warn("Unable to rebuild event content: " + e.getMessage(), e); + throw buildHttpException(e); + } finally { + propFindMethod.releaseConnection(); + } + return result; } diff --git a/src/java/davmail/exchange/dav/Field.java b/src/java/davmail/exchange/dav/Field.java index f15989f2..bcfcda38 100644 --- a/src/java/davmail/exchange/dav/Field.java +++ b/src/java/davmail/exchange/dav/Field.java @@ -160,8 +160,43 @@ public class Field { createField(URN_SCHEMAS_CALENDAR, "instancetype"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:instancetype/Integer createField(URN_SCHEMAS_CALENDAR, "dtstart"); // 0x10C3 SystemTime createField(URN_SCHEMAS_CALENDAR, "dtend"); // 0x10C4 SystemTime + + //createField(URN_SCHEMAS_CALENDAR, "prodid"); // // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:prodid/String + createField("calendarversion", URN_SCHEMAS_CALENDAR, "version"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:version/String + createField(URN_SCHEMAS_CALENDAR, "method"); // // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:method/String + + createField("calendarlastmodified", URN_SCHEMAS_CALENDAR, "lastmodified"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:isorganizer/Boolean + createField(URN_SCHEMAS_CALENDAR, "dtstamp"); // PidLidOwnerCriticalChange + createField("calendaruid", URN_SCHEMAS_CALENDAR, "uid"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:uid/String + createField(URN_SCHEMAS_CALENDAR, "transparent"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:transparent/String + + createField(URN_SCHEMAS_CALENDAR, "organizer"); + createField(URN_SCHEMAS_CALENDAR, "created"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:created/SystemTime + createField(URN_SCHEMAS_CALENDAR, "alldayevent"); // DistinguishedPropertySetType.Appointment/0x8215 Boolean + + // TODO + createField(SCHEMAS_MAPI, "allattendeesstring"); // PidLidAllAttendeesString + createField(SCHEMAS_MAPI, "required_attendees"); // PidLidRequiredAttendees + createField(SCHEMAS_MAPI, "apptendtime"); // PidLidAppointmentEndTime + createField(SCHEMAS_MAPI, "apptstateflags"); // PidLidAppointmentStateFlags 1: Meeting, 2: Received, 4: Cancelled + + createField(URN_SCHEMAS_CALENDAR, "isorganizer"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:isorganizer/Boolean + createField(URN_SCHEMAS_CALENDAR, "location"); // DistinguishedPropertySetType.Appointment/0x8208 String + createField(URN_SCHEMAS_CALENDAR, "attendeerole"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:attendeerole/Integer + createField(URN_SCHEMAS_CALENDAR, "busystatus"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:busystatus/String + createField(URN_SCHEMAS_CALENDAR, "exdate"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:exdate/PtypMultipleTime + createField(URN_SCHEMAS_CALENDAR, "exrule"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:exrule/PtypMultipleString + createField(URN_SCHEMAS_CALENDAR, "recurrenceidrange"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:recurrenceidrange/String + createField(URN_SCHEMAS_CALENDAR, "rdate"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:rdate/PtypMultipleTime + createField(URN_SCHEMAS_CALENDAR, "rrule"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:rrule/PtypMultipleString + createField(URN_SCHEMAS_CALENDAR, "reminderoffset"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:reminderoffset/Integer + createField(URN_SCHEMAS_CALENDAR, "timezone"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:timezone/String + + + createField(SCHEMAS_EXCHANGE, "sensitivity"); // PR_SENSITIVITY 0x0036 Integer createField(URN_SCHEMAS_CALENDAR, "timezoneid"); // DistinguishedPropertySetType.PublicStrings/urn:schemas:calendar:timezoneid/Integer + // should use PidLidServerProcessed ? createField("processed", 0x65e8, PropertyType.Boolean);// PR_MESSAGE_PROCESSED createField(DAV, "contentclass");