From a2adb8f897d64e16e0de1a0943b8695a42cb32c2 Mon Sep 17 00:00:00 2001 From: mguessan Date: Sat, 17 Jul 2010 10:45:21 +0000 Subject: [PATCH] Carddav: implement photo handling over EWS git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1188 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- .../davmail/exchange/ExchangeSession.java | 2 + .../exchange/ews/CreateAttachmentMethod.java | 36 ++++++++ src/java/davmail/exchange/ews/EWSMethod.java | 90 +++++++++++++++++++ .../exchange/ews/EwsExchangeSession.java | 40 ++++++++- src/java/davmail/exchange/ews/Field.java | 3 + .../davmail/exchange/ews/FileAttachment.java | 69 ++++++++++++++ .../exchange/ews/GetAttachmentMethod.java | 33 +++++++ .../exchange/TestExchangeSessionContact.java | 17 ++++ 8 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 src/java/davmail/exchange/ews/CreateAttachmentMethod.java create mode 100644 src/java/davmail/exchange/ews/FileAttachment.java create mode 100644 src/java/davmail/exchange/ews/GetAttachmentMethod.java diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 9fc9f2ee..5d73f8e8 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -2776,6 +2776,8 @@ public abstract class ExchangeSession { properties.put("manager", property.getValue()); } else if ("X-SPOUSE".equals(property.getKey())) { properties.put("spousecn", property.getValue()); + } else if ("PHOTO".equals(property.getKey())) { + properties.put("photo", property.getValue()); } } // detect empty values diff --git a/src/java/davmail/exchange/ews/CreateAttachmentMethod.java b/src/java/davmail/exchange/ews/CreateAttachmentMethod.java new file mode 100644 index 00000000..31aa8913 --- /dev/null +++ b/src/java/davmail/exchange/ews/CreateAttachmentMethod.java @@ -0,0 +1,36 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange.ews; + +/** + * Create Attachment Method. + */ +public class CreateAttachmentMethod extends EWSMethod { + /** + * Create attachment method. + * + * @param parentItemId parent item id + * @param attachment attachment object + */ + public CreateAttachmentMethod(ItemId parentItemId, FileAttachment attachment) { + super("Item", "CreateAttachment"); + this.parentItemId = parentItemId; + this.attachment = attachment; + } +} diff --git a/src/java/davmail/exchange/ews/EWSMethod.java b/src/java/davmail/exchange/ews/EWSMethod.java index bfb27b0a..73603fcb 100644 --- a/src/java/davmail/exchange/ews/EWSMethod.java +++ b/src/java/davmail/exchange/ews/EWSMethod.java @@ -47,12 +47,17 @@ public abstract class EWSMethod extends PostMethod { protected FolderId toFolderId; protected FolderId parentFolderId; protected ItemId itemId; + protected ItemId parentItemId; protected Set additionalProperties; protected Disposal deleteType; protected Set methodOptions; protected Set updates; + protected FileAttachment attachment; + + protected String attachmentId; + protected final String itemType; protected final String methodName; protected final String responseCollectionName; @@ -162,6 +167,18 @@ public abstract class EWSMethod extends PostMethod { } } + protected void writeParentItemId(Writer writer) throws IOException { + if (parentItemId != null) { + writer.write(""); + } + } + protected void writeFolderId(Writer writer) throws IOException { if (folderId != null) { if (updates == null) { @@ -316,6 +333,9 @@ public abstract class EWSMethod extends PostMethod { writeParentFolderId(writer); writeToFolderId(writer); writeItemId(writer); + writeParentItemId(writer); + writeAttachments(writer); + writeAttachmentId(writer); writeFolderId(writer); writeSavedItemFolderId(writer); writeItem(writer); @@ -323,6 +343,28 @@ public abstract class EWSMethod extends PostMethod { endChanges(writer); } + private void writeAttachmentId(Writer writer) throws IOException { + if (attachmentId != null) { + writer.write(""); + writer.write("true"); + writer.write(""); + + writer.write(""); + writer.write(""); + writer.write(""); + } + } + + protected void writeAttachments(Writer writer) throws IOException { + if (attachment != null) { + writer.write(""); + attachment.write(writer); + writer.write(""); + } + } + /** * Build a new XMLInputFactory. * @@ -345,6 +387,7 @@ public abstract class EWSMethod extends PostMethod { public String type; protected byte[] mimeContent; protected Set fieldUpdates; + protected List attachments; @Override public String toString() { @@ -440,6 +483,18 @@ public abstract class EWSMethod extends PostMethod { return result; } + public FileAttachment getAttachmentByName(String attachmentName) { + FileAttachment result = null; + if (attachments != null) { + for (FileAttachment attachment:attachments) { + if (attachmentName.equals(attachment.name)) { + result = attachment; + break; + } + } + } + return result; + } } /** @@ -544,6 +599,8 @@ public abstract class EWSMethod extends PostMethod { addExtendedPropertyValue(reader, responseItem); } else if (tagLocalName.endsWith("MimeContent")) { handleMimeContent(reader, responseItem); + } else if (tagLocalName.equals("Attachments")) { + responseItem.attachments = handleAttachments(reader, responseItem); } else { if (tagLocalName.endsWith("Id")) { value = getAttributeValue(reader, "Id"); @@ -562,6 +619,39 @@ public abstract class EWSMethod extends PostMethod { return responseItem; } + protected List handleAttachments(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + List attachments = new ArrayList(); + while (reader.hasNext() && !(isEndTag(reader, "Attachments"))) { + int event = reader.next(); + if (event == XMLStreamConstants.START_ELEMENT) { + String tagLocalName = reader.getLocalName(); + if (tagLocalName.equals("FileAttachment")) { + attachments.add(handleFileAttachment(reader, responseItem)); + } + } + } + return attachments; + } + + protected FileAttachment handleFileAttachment(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + FileAttachment fileAttachment = new FileAttachment(); + while (reader.hasNext() && !(isEndTag(reader, "FileAttachment"))) { + int event = reader.next(); + if (event == XMLStreamConstants.START_ELEMENT) { + String tagLocalName = reader.getLocalName(); + if (tagLocalName.equals("AttachmentId")) { + fileAttachment.attachmentId = getAttributeValue(reader, "Id"); + } else if (tagLocalName.equals("Name")) { + fileAttachment.name = getTagContent(reader); + } else if (tagLocalName.equals("ContentType")) { + fileAttachment.contentType = getTagContent(reader); + } + } + } + return fileAttachment; + } + + protected void handleMimeContent(XMLStreamReader reader, Item responseItem) throws XMLStreamException { byte[] base64MimeContent = reader.getElementText().getBytes(); responseItem.mimeContent = Base64.decodeBase64(base64MimeContent); diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index f080a4a3..fe919924 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -676,7 +676,11 @@ public class EwsExchangeSession extends ExchangeSession { protected Set buildProperties() throws IOException { HashSet list = new HashSet(); for (Map.Entry entry : entrySet()) { - list.add(Field.createFieldUpdate(entry.getKey(), entry.getValue())); + if ("photo".equals(entry.getKey())) { + list.add(Field.createFieldUpdate("haspicture", "true")); + } else { + list.add(Field.createFieldUpdate(entry.getKey(), entry.getValue())); + } } // force urlcompname list.add(Field.createFieldUpdate("urlcompname", convertItemNameToEML(itemName))); @@ -691,6 +695,8 @@ public class EwsExchangeSession extends ExchangeSession { * @throws IOException on error */ public ItemResult createOrUpdate() throws IOException { + String photo = get("photo"); + ItemResult itemResult = new ItemResult(); EWSMethod createOrUpdateItemMethod; @@ -731,7 +737,6 @@ public class EwsExchangeSession extends ExchangeSession { newItem.setFieldUpdates(buildProperties()); createOrUpdateItemMethod = new CreateItemMethod(MessageDisposition.SaveOnly, getFolderId(folderPath), newItem); } - executeMethod(createOrUpdateItemMethod); itemResult.status = createOrUpdateItemMethod.getStatusCode(); @@ -746,6 +751,15 @@ public class EwsExchangeSession extends ExchangeSession { } ItemId newItemId = new ItemId(createOrUpdateItemMethod.getResponseItem()); + + if (photo != null) { + // TODO: handle photo update, fix attachment mapi properties (available only with Exchange 2010) + FileAttachment attachment = new FileAttachment("ContactPicture.jpg", "image/jpg", photo); + // update photo attachment + CreateAttachmentMethod createAttachmentMethod = new CreateAttachmentMethod(newItemId, attachment); + executeMethod(createAttachmentMethod); + } + GetItemMethod getItemMethod = new GetItemMethod(BaseShape.ID_ONLY, newItemId, false); getItemMethod.addAdditionalProperty(Field.get("etag")); executeMethod(getItemMethod); @@ -925,7 +939,27 @@ public class EwsExchangeSession extends ExchangeSession { @Override public ContactPhoto getContactPhoto(ExchangeSession.Contact contact) throws IOException { - throw new UnsupportedOperationException(); + ContactPhoto contactPhoto = null; + + GetItemMethod getItemMethod = new GetItemMethod(BaseShape.ID_ONLY, ((EwsExchangeSession.Contact)contact).itemId, false); + getItemMethod.addAdditionalProperty(Field.get("attachments")); + executeMethod(getItemMethod); + EWSMethod.Item item = getItemMethod.getResponseItem(); + if (item != null) { + FileAttachment attachment = item.getAttachmentByName("ContactPicture.jpg"); + if (attachment != null) { + // get attachment content + GetAttachmentMethod getAttachmentMethod = new GetAttachmentMethod(attachment.attachmentId); + executeMethod(getAttachmentMethod); + + contactPhoto = new ContactPhoto(); + contactPhoto.content = getAttachmentMethod.getResponseItem().get("Content"); + contactPhoto.type = attachment.contentType; + } + + } + + return contactPhoto; } @Override diff --git a/src/java/davmail/exchange/ews/Field.java b/src/java/davmail/exchange/ews/Field.java index 1e096780..144eabe8 100644 --- a/src/java/davmail/exchange/ews/Field.java +++ b/src/java/davmail/exchange/ews/Field.java @@ -141,6 +141,9 @@ public class Field { // calendar FIELD_MAP.put("processed", new ExtendedFieldURI(0x65e8, ExtendedFieldURI.PropertyType.Boolean)); + + // attachments + FIELD_MAP.put("attachments", new UnindexedFieldURI("item:Attachments")); } /** diff --git a/src/java/davmail/exchange/ews/FileAttachment.java b/src/java/davmail/exchange/ews/FileAttachment.java new file mode 100644 index 00000000..a4cf8160 --- /dev/null +++ b/src/java/davmail/exchange/ews/FileAttachment.java @@ -0,0 +1,69 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange.ews; + +import java.io.IOException; +import java.io.Writer; + +/** + * File Attachment. + */ +public class FileAttachment { + protected String name; + protected String contentType; + protected String content; + protected String attachmentId; + + public FileAttachment() { + // empty constructor + } + + public FileAttachment(String name, String contentType, String content) { + this.name = name; + this.contentType = contentType; + this.content = content; + } + + /** + * Write XML content to writer. + * + * @param writer writer + * @throws IOException on error + */ + public void write(Writer writer) throws IOException { + writer.write(""); + if (name != null) { + writer.write(""); + writer.write(name); + writer.write(""); + } + if (contentType != null) { + writer.write(""); + writer.write(contentType); + writer.write(""); + } + if (content != null) { + writer.write(""); + writer.write(content); + writer.write(""); + } + writer.write(""); + } + +} diff --git a/src/java/davmail/exchange/ews/GetAttachmentMethod.java b/src/java/davmail/exchange/ews/GetAttachmentMethod.java new file mode 100644 index 00000000..5a2f76e8 --- /dev/null +++ b/src/java/davmail/exchange/ews/GetAttachmentMethod.java @@ -0,0 +1,33 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange.ews; + +/** + * Get Attachment Method. + */ +public class GetAttachmentMethod extends EWSMethod { + + /** + * Get Attachment Method. + */ + public GetAttachmentMethod(String attachmentId) { + super("Attachment", "GetAttachment"); + this.attachmentId = attachmentId; + } +} diff --git a/src/test/davmail/exchange/TestExchangeSessionContact.java b/src/test/davmail/exchange/TestExchangeSessionContact.java index c8135e0c..301c991d 100644 --- a/src/test/davmail/exchange/TestExchangeSessionContact.java +++ b/src/test/davmail/exchange/TestExchangeSessionContact.java @@ -18,7 +18,12 @@ */ package davmail.exchange; +import org.apache.commons.codec.binary.Base64; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.UUID; /** @@ -101,6 +106,16 @@ public class TestExchangeSessionContact extends AbstractExchangeSessionTestCase vCardWriter.appendProperty("X-MANAGER", "manager"); vCardWriter.appendProperty("X-SPOUSE", "spousecn"); + // add photo + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream partInputStream = new FileInputStream("src/data/anonymous.jpg"); + byte[] bytes = new byte[8192]; + int length; + while ((length = partInputStream.read(bytes)) > 0) { + baos.write(bytes, 0, length); + } + vCardWriter.appendProperty("PHOTO;ENCODING=b;TYPE=JPEG", new String(Base64.encodeBase64(baos.toByteArray()))); + vCardWriter.endCard(); ExchangeSession.ItemResult result = session.createOrUpdateContact("testcontactfolder", itemName, vCardWriter.toString(), null, null); @@ -171,5 +186,7 @@ public class TestExchangeSessionContact extends AbstractExchangeSessionTestCase assertEquals("manager", contact.get("manager")); assertEquals("spousecn", contact.get("spousecn")); assertEquals("keywords", contact.get("keywords")); + + assertNotNull(session.getContactPhoto(contact)); } }