diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 19d4b0d2..9fc9f2ee 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -1751,6 +1751,9 @@ public abstract class ExchangeSession { writer.appendProperty("BDAY", get("bday")); + // categories + writer.appendProperty("CATEGORIES", get("keywords")); + writer.appendProperty("X-ASSISTANT", get("secretarycn")); writer.appendProperty("X-MANAGER", get("manager")); writer.appendProperty("X-SPOUSE", get("spousecn")); @@ -2672,6 +2675,8 @@ public abstract class ExchangeSession { protected static final String[] VCARD_ADR_OTHER_PROPERTIES = {"otherpostofficebox", null, "otherstreet", "othercity", "otherstate", "otherpostalcode", "othercountry"}; protected static final String[] VCARD_ORG_PROPERTIES = {"o", "department"}; + protected static final String[] EMPTY_PROPERTIES = {"description", "keywords"}; + protected void convertContactProperties(Map properties, String[] contactProperties, List values) { for (int i = 0; i < values.size() && i < contactProperties.length; i++) { if (contactProperties[i] != null) { @@ -2763,6 +2768,8 @@ public abstract class ExchangeSession { LOGGER.warn("Invalid date: " + value); } } + } else if ("CATEGORIES".equals(property.getKey())) { + properties.put("keywords", property.getValue()); } else if ("X-ASSISTANT".equals(property.getKey())) { properties.put("secretarycn", property.getValue()); } else if ("X-MANAGER".equals(property.getKey())) { @@ -2771,6 +2778,13 @@ public abstract class ExchangeSession { properties.put("spousecn", property.getValue()); } } + // detect empty values + for (String key : EMPTY_PROPERTIES) { + if (!properties.containsKey(key)) { + properties.put(key, null); + } + } + return internalCreateOrUpdateContact(folderPath, itemName, properties, etag, noneMatch); } @@ -3116,6 +3130,7 @@ public abstract class ExchangeSession { CONTACT_ATTRIBUTES.add("othercountry"); CONTACT_ATTRIBUTES.add("othercity"); CONTACT_ATTRIBUTES.add("haspicture"); + CONTACT_ATTRIBUTES.add("keywords"); } /** diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 0a737457..6f43142c 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -42,6 +42,7 @@ import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod; import org.apache.jackrabbit.webdav.property.DavProperty; import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; import org.apache.jackrabbit.webdav.property.DavPropertySet; +import org.w3c.dom.Node; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; @@ -967,7 +968,21 @@ public class DavExchangeSession extends ExchangeSession { if (property == null) { return null; } else { - return (String) property.getValue(); + Object value = property.getValue(); + if (value instanceof Node) { + return ((Node) value).getTextContent(); + } else if (value instanceof List) { + StringBuilder buffer = new StringBuilder(); + for (Object node : (List) value) { + if (buffer.length() > 0) { + buffer.append(','); + } + buffer.append(((Node) node).getTextContent()); + } + return buffer.toString(); + } else { + return (String) value; + } } } @@ -1166,7 +1181,7 @@ public class DavExchangeSession extends ExchangeSession { if (mimeMessage.getContent() instanceof MimeMultipart) { photoBodyPart = (MimePart) ((MimeMultipart) mimeMessage.getContent()).getBodyPart(1); } else { - photoBodyPart = (MimePart) mimeMessage; + photoBodyPart = mimeMessage; } contactPhoto = new ContactPhoto(); String contentType = photoBodyPart.getContentType(); diff --git a/src/java/davmail/exchange/dav/Field.java b/src/java/davmail/exchange/dav/Field.java index 759d9cb4..b532622c 100644 --- a/src/java/davmail/exchange/dav/Field.java +++ b/src/java/davmail/exchange/dav/Field.java @@ -21,9 +21,15 @@ package davmail.exchange.dav; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.property.DefaultDavProperty; +import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.xml.Namespace; +import org.apache.jackrabbit.webdav.xml.XmlSerializable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -45,12 +51,13 @@ public class Field { } protected static final Namespace EMPTY = Namespace.getNamespace(""); + protected static final Namespace XML = Namespace.getNamespace("xml:"); protected static final Namespace DAV = Namespace.getNamespace("DAV:"); protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:"); protected static final Namespace URN_SCHEMAS_MAILHEADER = Namespace.getNamespace("urn:schemas:mailheader:"); protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/"); - protected static final Namespace SCHEMAS_MAPI = Namespace.getNamespace("http://schemas.microsoft.com/mapi/"); + protected static final Namespace SCHEMAS_MAPI = Namespace.getNamespace("http://schemas.microsoft.com/mapi/"); protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace.getNamespace("http://schemas.microsoft.com/mapi/proptag/"); protected static final Namespace SCHEMAS_MAPI_ID = Namespace.getNamespace("http://schemas.microsoft.com/mapi/id/"); protected static final Namespace SCHEMAS_MAPI_STRING = Namespace.getNamespace("http://schemas.microsoft.com/mapi/string/"); @@ -239,6 +246,9 @@ public class Field { createField(URN_SCHEMAS_CONTACTS, "othercountry"); // PR_OTHER_ADDRESS_COUNTRY 0x3A60 String createField(URN_SCHEMAS_CONTACTS, "othercity"); // PR_OTHER_ADDRESS_CITY 0x3A5F String + createField("keywords", SCHEMAS_EXCHANGE, "keywords-utf8", PropertyType.StringArray); // PS_PUBLIC_STRINGS Keywords String + //createField("keywords", DistinguishedPropertySetType.PublicStrings, "Keywords", ); // PS_PUBLIC_STRINGS Keywords String + // contact private flags createField("private", DistinguishedPropertySetType.Common, 0x8506, "private"); // True/False createField("sensitivity", 0x0036, PropertyType.Long); // PR_SENSITIVITY SENSITIVITY_PRIVATE=2, SENSITIVITY_NONE = 0 @@ -307,6 +317,14 @@ public class Field { fieldMap.put(field.alias, field); } + protected static void createField(String alias, Namespace namespace, String name, PropertyType propertyType) { + Field field = new Field(alias, namespace, name); + if (propertyType == PropertyType.StringArray) { + field.isMultivalued = true; + } + fieldMap.put(field.alias, field); + } + protected final DavPropertyName davPropertyName; protected final String alias; protected final String uri; @@ -314,6 +332,7 @@ public class Field { protected final DavPropertyName responsePropertyName; protected final String cast; protected boolean isIntValue; + protected boolean isMultivalued; public Field(Namespace namespace, String name) { @@ -375,11 +394,24 @@ public class Field { } public static DavConstants createDavProperty(String alias, String value) { + Field field = Field.get(alias); if (value == null) { // return DavPropertyName to remove property - return Field.get(alias).davPropertyName; + return field.davPropertyName; + } else if (field.isMultivalued) { + List valueList = new ArrayList(); + String[] values = value.split("\n"); + for (final String singleValue : values) { + valueList.add(new XmlSerializable() { + public Element toXml(Document document) { + return DomUtil.createElement(document, "v", XML, singleValue); + } + }); + } + + return new DefaultDavProperty(field.davPropertyName, valueList); } else { - return new DefaultDavProperty(Field.get(alias).davPropertyName, value); + return new DefaultDavProperty(field.davPropertyName, value); } } diff --git a/src/java/davmail/exchange/ews/Field.java b/src/java/davmail/exchange/ews/Field.java index 642743ea..4f770a73 100644 --- a/src/java/davmail/exchange/ews/Field.java +++ b/src/java/davmail/exchange/ews/Field.java @@ -132,6 +132,8 @@ public class Field { FIELD_MAP.put("othercountry", new ExtendedFieldURI(0x3A60, ExtendedFieldURI.PropertyType.String)); FIELD_MAP.put("othercity", new ExtendedFieldURI(0x3A5F, ExtendedFieldURI.PropertyType.String)); + FIELD_MAP.put("keywords", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.PublicStrings, "Keywords")); + FIELD_MAP.put("private", new ExtendedFieldURI(ExtendedFieldURI.DistinguishedPropertySetType.Common, 0x8506, ExtendedFieldURI.PropertyType.Boolean)); FIELD_MAP.put("sensitivity", new ExtendedFieldURI(0x0036, ExtendedFieldURI.PropertyType.Long));