From 735542734e38791ede8d950dfc2492c4df1dc435 Mon Sep 17 00:00:00 2001 From: mguessan Date: Fri, 27 Nov 2009 23:27:50 +0000 Subject: [PATCH] Caldav: a brand new ActiveSync fix, set contentclass to make ActiveSync happy, but also set PR_INTERNET_CONTENT to preserve custom properties. Also get etag from updated event. git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@864 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- .../davmail/exchange/ExchangeSession.java | 123 ++++++++++++++---- 1 file changed, 95 insertions(+), 28 deletions(-) diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index cc6cf80f..74a64757 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -20,11 +20,12 @@ package davmail.exchange; import davmail.BundleMessage; import davmail.Settings; -import davmail.util.StringUtil; import davmail.exception.DavMailAuthenticationException; import davmail.exception.DavMailException; +import davmail.exception.HttpNotFoundException; import davmail.http.DavGatewayHttpClientFacade; import davmail.http.DavGatewayOTPPrompt; +import davmail.util.StringUtil; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; @@ -35,9 +36,12 @@ import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.util.URIUtil; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.MultiStatusResponse; import org.apache.jackrabbit.webdav.client.methods.CopyMethod; import org.apache.jackrabbit.webdav.client.methods.MoveMethod; +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod; import org.apache.jackrabbit.webdav.property.*; import org.apache.jackrabbit.webdav.xml.Namespace; @@ -1660,6 +1664,60 @@ public class ExchangeSession { return bodyPart; } + /** + * Load ICS content from MIME message input stream + * + * @param mimeInputStream mime message input stream + * @return mime message ics attachment body + * @throws IOException on error + * @throws MessagingException on error + */ + protected String getICS(InputStream mimeInputStream) throws IOException, MessagingException { + String result = null; + MimeMessage mimeMessage = new MimeMessage(null, mimeInputStream); + Object mimeBody = mimeMessage.getContent(); + MimePart bodyPart; + if (mimeBody instanceof MimeMultipart) { + bodyPart = getCalendarMimePart((MimeMultipart) mimeBody); + } else { + // no multipart, single body + bodyPart = mimeMessage; + } + + if (bodyPart != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bodyPart.getDataHandler().writeTo(baos); + baos.close(); + result = fixICS(new String(baos.toByteArray(), "UTF-8"), true); + } + return result; + } + + final DavPropertyName PR_INTERNET_CONTENT = DavPropertyName.create("x66590102", SCHEMAS_MAPI_PROPTAG); + + protected String getICSFromInternetContentProperty() throws IOException, DavException, MessagingException { + String result = null; + // PropFind PR_INTERNET_CONTENT + DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet(); + davPropertyNameSet.add(PR_INTERNET_CONTENT); + PropFindMethod propFindMethod = new PropFindMethod(URIUtil.encodePath(permanentUrl), davPropertyNameSet, 0); + try { + DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propFindMethod); + MultiStatus responses = propFindMethod.getResponseBodyAsMultiStatus(); + if (responses.getResponses().length > 0) { + DavPropertySet properties = responses.getResponses()[0].getProperties(HttpStatus.SC_OK); + DavProperty property = properties.get(PR_INTERNET_CONTENT); + if (property != null) { + byte[] byteArray = Base64.decodeBase64(((String) property.getValue()).getBytes()); + result = getICS(new ByteArrayInputStream(byteArray)); + } + } + } finally { + propFindMethod.releaseConnection(); + } + return result; + } + /** * Load ICS content from Exchange server. * User Translate: f header to get MIME event content and get ICS attachment from it @@ -1670,38 +1728,26 @@ public class ExchangeSession { public String getICS() throws IOException { String result = null; LOGGER.debug("Get event: " + permanentUrl); - GetMethod method = new GetMethod(permanentUrl); - method.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); - method.setRequestHeader("Translate", "f"); + // try to get PR_INTERNET_CONTENT try { - DavGatewayHttpClientFacade.executeGetMethod(httpClient, method, false); - - MimeMessage mimeMessage = new MimeMessage(null, method.getResponseBodyAsStream()); - Object mimeBody = mimeMessage.getContent(); - MimePart bodyPart; - if (mimeBody instanceof MimeMultipart) { - bodyPart = getCalendarMimePart((MimeMultipart) mimeBody); - } else { - // no multipart, single body - bodyPart = mimeMessage; + result = getICSFromInternetContentProperty(); + if (result == null) { + GetMethod method = new GetMethod(permanentUrl); + method.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); + method.setRequestHeader("Translate", "f"); + try { + DavGatewayHttpClientFacade.executeGetMethod(httpClient, method, false); + result = getICS(method.getResponseBodyAsStream()); + } finally { + method.releaseConnection(); + } } - - if (bodyPart == null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - mimeMessage.getDataHandler().writeTo(baos); - baos.close(); - throw new DavMailException("EXCEPTION_INVALID_MESSAGE_CONTENT", new String(baos.toByteArray(), "UTF-8")); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - bodyPart.getDataHandler().writeTo(baos); - baos.close(); - result = fixICS(new String(baos.toByteArray(), "UTF-8"), true); + } catch (DavException e) { + LOGGER.warn("Unable to get event at " + permanentUrl + ": " + e.getMessage()); } catch (IOException e) { LOGGER.warn("Unable to get event at " + permanentUrl + ": " + e.getMessage()); } catch (MessagingException e) { LOGGER.warn("Unable to get event at " + permanentUrl + ": " + e.getMessage()); - } finally { - method.releaseConnection(); } return result; } @@ -2336,11 +2382,18 @@ public class ExchangeSession { if ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) && (Settings.getBooleanProperty("davmail.forceActiveSyncUpdate"))) { ArrayList propertyList = new ArrayList(); + // Set contentclass to make ActiveSync happy propertyList.add(new DefaultDavProperty(DavPropertyName.create("contentclass", Namespace.getNamespace("DAV:")), contentClass)); + // ... but also set PR_INTERNET_CONTENT to preserve custom properties + propertyList.add(new DefaultDavProperty(PR_INTERNET_CONTENT, new String(Base64.encodeBase64(baos.toByteArray())))); PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(href), propertyList); int patchStatus = DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propPatchMethod); if (patchStatus != HttpStatus.SC_MULTI_STATUS) { LOGGER.warn("Unable to patch event to trigger activeSync push"); + } else { + // need to retrieve new etag + Event newEvent = getEvent(href); + eventResult.etag = newEvent.etag; } } return eventResult; @@ -2433,7 +2486,21 @@ public class ExchangeSession { */ public Event getEvent(String folderPath, String eventName) throws IOException { String eventPath = folderPath + '/' + eventName; - return getEvent(eventPath); + Event event; + try { + event = getEvent(eventPath); + } catch (HttpNotFoundException hnfe) { + // failover for Exchange 2007 plus encoding issue + String decodedEventName = eventName.replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''"); + ExchangeSession.MessageList messages = searchMessages(folderPath, " AND \"DAV:displayname\"='" + decodedEventName + '\''); + if (!messages.isEmpty()) { + event = getEvent(messages.get(0).getPermanentUrl()); + } else { + throw hnfe; + } + } + + return event; } /**