From d7506d9f67a38c1b47dd41c2be963560646ea28f Mon Sep 17 00:00:00 2001 From: mguessan Date: Wed, 30 Jun 2010 21:38:01 +0000 Subject: [PATCH] CardDav: move Contact getBody to ExchangeSession and add more attributes support git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1113 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- .../davmail/exchange/ExchangeSession.java | 95 ++++++++++++++++--- src/java/davmail/exchange/VCardWriter.java | 70 ++++++++++++++ .../exchange/dav/DavExchangeSession.java | 70 +------------- src/java/davmail/exchange/dav/Field.java | 11 ++- .../exchange/ews/SearchExpression.java | 10 +- 5 files changed, 167 insertions(+), 89 deletions(-) create mode 100644 src/java/davmail/exchange/VCardWriter.java diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index e3afd213..8190e6a2 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -183,31 +183,31 @@ public abstract class ExchangeSession { LOGGER.debug("Session " + this + " created"); } - protected String formatSearchDate(Date date) { + protected static String formatSearchDate(Date date) { SimpleDateFormat dateFormatter = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS, Locale.ENGLISH); dateFormatter.setTimeZone(GMT_TIMEZONE); return dateFormatter.format(date); } - protected SimpleDateFormat getZuluDateFormat() { + protected static SimpleDateFormat getZuluDateFormat() { SimpleDateFormat dateFormat = new SimpleDateFormat(YYYYMMDD_T_HHMMSS_Z, Locale.ENGLISH); dateFormat.setTimeZone(GMT_TIMEZONE); return dateFormat; } - protected SimpleDateFormat getExchangeZuluDateFormat() { + protected static SimpleDateFormat getExchangeZuluDateFormat() { SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD_T_HHMMSS_Z, Locale.ENGLISH); dateFormat.setTimeZone(GMT_TIMEZONE); return dateFormat; } - protected SimpleDateFormat getExchangeZuluDateFormatMillisecond() { + protected static SimpleDateFormat getExchangeZuluDateFormatMillisecond() { SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD_T_HHMMSS_SSS_Z, Locale.ENGLISH); dateFormat.setTimeZone(GMT_TIMEZONE); return dateFormat; } - protected Date parseDate(String dateString) throws ParseException { + protected static Date parseDate(String dateString) throws ParseException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); dateFormat.setTimeZone(GMT_TIMEZONE); return dateFormat.parse(dateString); @@ -594,7 +594,7 @@ public abstract class ExchangeSession { /** * Exchange search filter. */ - public abstract static class Condition { + public static interface Condition { /** * Append condition to buffer. * @@ -606,7 +606,7 @@ public abstract class ExchangeSession { /** * Attribute condition. */ - public abstract static class AttributeCondition extends Condition { + public abstract static class AttributeCondition implements Condition { protected String attributeName; protected Operator operator; protected String value; @@ -621,7 +621,7 @@ public abstract class ExchangeSession { /** * Multiple condition. */ - public abstract static class MultiCondition extends Condition { + public abstract static class MultiCondition implements Condition { protected Operator operator; protected List conditions; @@ -645,7 +645,7 @@ public abstract class ExchangeSession { /** * Not condition. */ - public abstract static class NotCondition extends Condition { + public abstract static class NotCondition implements Condition { protected Condition condition; protected NotCondition(Condition condition) { @@ -656,7 +656,7 @@ public abstract class ExchangeSession { /** * Single search filter condition. */ - public abstract static class MonoCondition extends Condition { + public abstract static class MonoCondition implements Condition { protected String attributeName; protected Operator operator; @@ -1689,6 +1689,70 @@ public abstract class ExchangeSession { return "text/vcard"; } + + @Override + public String getBody() throws HttpException { + // build RFC 2426 VCard from contact information + VCardWriter writer = new VCardWriter(); + writer.startCard(); + writer.appendProperty("UID", getUid()); + // common name + writer.appendProperty("FN", get("cn")); + // RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes + writer.appendProperty("N", get("sn"), get("givenName"), get("middlename"), get("personaltitle"), get("namesuffix")); + + writer.appendProperty("TEL;TYPE=cell", get("mobile")); + writer.appendProperty("TEL;TYPE=work", get("telephoneNumber")); + writer.appendProperty("TEL;TYPE=home", get("homePhone")); + writer.appendProperty("TEL;TYPE=fax", get("facsimiletelephonenumber")); + writer.appendProperty("TEL;TYPE=pager", get("pager")); + + // The structured type value corresponds, in sequence, to the post office box; the extended address; + // the street address; the locality (e.g., city); the region (e.g., state or province); + // the postal code; the country name + writer.appendProperty("ADR;TYPE=home", + null, null, get("homeStreet"), get("homeCity"), get("homeState"), get("homePostalCode"), get("homeCountry")); + writer.appendProperty("ADR;TYPE=work", + get("postofficebox"), get("roomnumber"), get("street"), get("l"), get("st"), get("postalcode"), get("co")); + + writer.appendProperty("EMAIL;TYPE=work", get("email1")); + writer.appendProperty("EMAIL;TYPE=home", get("email2")); + writer.appendProperty("EMAIL;TYPE=other", get("email3")); + + writer.appendProperty("ORG", get("o"), get("department")); + writer.appendProperty("URL;WORK", get("businesshomepage")); + writer.appendProperty("TITLE", get("title")); + writer.appendProperty("NOTE", get("textdescription")); + + writer.appendProperty("CUSTOM1", get("extensionattribute1")); + writer.appendProperty("CUSTOM2", get("extensionattribute2")); + writer.appendProperty("CUSTOM3", get("extensionattribute3")); + writer.appendProperty("CUSTOM4", get("extensionattribute4")); + + writer.appendProperty("ROLE", get("profession")); + writer.appendProperty("NICKNAME", get("nickname")); + writer.appendProperty("X-AIM", get("im")); + + writer.appendProperty("BDAY", get("bday")); + + writer.appendProperty("X-EVOLUTION-ASSISTANT", get("secretarycn")); + writer.appendProperty("X-EVOLUTION-MANAGER", get("manager")); + writer.appendProperty("X-EVOLUTION-SPOUSE", get("spousecn")); + + + + String lastModified = get("lastmodified"); + if (lastModified != null) { + try { + writer.appendProperty("REV", getZuluDateFormat().format(getExchangeZuluDateFormatMillisecond().parse(lastModified))); + } catch (ParseException e) { + LOGGER.warn("Invalid date: "+lastModified); + } + } + writer.endCard(); + return writer.toString(); + } + } /** @@ -2344,7 +2408,7 @@ public abstract class ExchangeSession { * @throws IOException on error */ public List getAllContacts(String folderPath) throws IOException { - return searchContacts(folderPath, ITEM_PROPERTIES, equals("contentclass", "urn:content-classes:person")); + return searchContacts(folderPath, ITEM_PROPERTIES, equals("outlookmessageclass", "IPM.Contact")); } @@ -2368,7 +2432,7 @@ public abstract class ExchangeSession { */ public List getEventMessages(String folderPath) throws IOException { return searchEvents(folderPath, ITEM_PROPERTIES, - and(equals("contentclass", "urn:content-classes:calendarmessage"), + and(equals("outlookmessageclass", "IPM.Schedule.Meeting.Request"), or(isNull("processed"), isFalse("processed")))); } @@ -2399,7 +2463,7 @@ public abstract class ExchangeSession { and(or(isNull("instancetype"), equals("instancetype", 1), and(equals("instancetype", 0), dateCondition)), - equals("contentclass", "urn:content-classes:appointment"), + equals("outlookmessageclass", "IPM.Appointment"), privateCondition)); } @@ -2820,6 +2884,7 @@ public abstract class ExchangeSession { CONTACT_ATTRIBUTES.add("department"); CONTACT_ATTRIBUTES.add("email1"); CONTACT_ATTRIBUTES.add("email2"); + CONTACT_ATTRIBUTES.add("email3"); CONTACT_ATTRIBUTES.add("facsimiletelephonenumber"); CONTACT_ATTRIBUTES.add("givenName"); CONTACT_ATTRIBUTES.add("homeCity"); @@ -2849,6 +2914,8 @@ public abstract class ExchangeSession { CONTACT_ATTRIBUTES.add("title"); CONTACT_ATTRIBUTES.add("textdescription"); CONTACT_ATTRIBUTES.add("im"); + CONTACT_ATTRIBUTES.add("middlename"); + CONTACT_ATTRIBUTES.add("lastmodified"); } @@ -2887,7 +2954,7 @@ public abstract class ExchangeSession { Map> results = new HashMap>(); - List contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("contentclass", "urn:content-classes:person"), condition)); + List contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("outlookmessageclass", "IPM.Contact"), condition)); Map item; for (Contact contact : contacts) { diff --git a/src/java/davmail/exchange/VCardWriter.java b/src/java/davmail/exchange/VCardWriter.java new file mode 100644 index 00000000..032fa63d --- /dev/null +++ b/src/java/davmail/exchange/VCardWriter.java @@ -0,0 +1,70 @@ +/* + * 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; + +/** + * VCard Writer + */ +public class VCardWriter extends ICSBufferedWriter { + public void startCard() { + writeLine("BEGIN:VCARD"); + writeLine("VERSION:3.0"); + } + + public void appendProperty(String propertyName, String propertyValue) { + if (propertyValue != null && propertyValue.length() > 0) { + write(propertyName); + write(":"); + if (propertyValue.indexOf('\n') >= 0) { + writeLine(propertyValue.replaceAll("\\n", "\\\\n")); + } + } + } + + public void appendProperty(String propertyName, String... propertyValue) { + boolean hasValue = false; + for (String value : propertyValue) { + if ((value != null) && (value.length() > 0)) { + hasValue = true; + break; + } + } + if (hasValue) { + write(propertyName); + write(":"); + boolean first = true; + StringBuilder valueBuffer = new StringBuilder(); + for (String value : propertyValue) { + if (first) { + first = false; + } else { + valueBuffer.append(';'); + } + if (value != null) { + valueBuffer.append(value); + } + } + writeLine(valueBuffer.toString()); + } + } + + public void endCard() { + writeLine("END:VCARD"); + } +} diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index 2e616951..6e0ad5de 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -25,7 +25,6 @@ import davmail.exception.DavMailException; import davmail.exception.HttpNotFoundException; import davmail.exchange.ExchangeSession; import davmail.exchange.ICSBufferedReader; -import davmail.exchange.ICSBufferedWriter; import davmail.http.DavGatewayHttpClientFacade; import davmail.ui.tray.DavGatewayTray; import davmail.util.StringUtil; @@ -357,7 +356,6 @@ public class DavExchangeSession extends ExchangeSession { super(operator, condition); } - @Override public void appendTo(StringBuilder buffer) { boolean first = true; @@ -384,7 +382,6 @@ public class DavExchangeSession extends ExchangeSession { super(condition); } - @Override public void appendTo(StringBuilder buffer) { buffer.append("(Not "); condition.appendTo(buffer); @@ -417,7 +414,6 @@ public class DavExchangeSession extends ExchangeSession { isIntValue = true; } - @Override public void appendTo(StringBuilder buffer) { buffer.append('"').append(Field.get(attributeName).getUri()).append('"'); buffer.append(operatorMap.get(operator)); @@ -464,7 +460,6 @@ public class DavExchangeSession extends ExchangeSession { super(attributeName, operator); } - @Override public void appendTo(StringBuilder buffer) { buffer.append('"').append(Field.get(attributeName).getUri()).append('"'); buffer.append(operatorMap.get(operator)); @@ -576,68 +571,9 @@ public class DavExchangeSession extends ExchangeSession { super(messageUrl, contentClass, itemBody, etag, noneMatch); } - @Override - public String getBody() throws HttpException { - // first retrieve contact details - String result = null; - - PropFindMethod propFindMethod = null; - try { - propFindMethod = new PropFindMethod(URIUtil.encodePath(permanentUrl)); - DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propFindMethod); - MultiStatus responses = propFindMethod.getResponseBodyAsMultiStatus(); - if (responses.getResponses().length > 0) { - DavPropertySet properties = responses.getResponses()[0].getProperties(HttpStatus.SC_OK); - - ICSBufferedWriter writer = new ICSBufferedWriter(); - writer.writeLine("BEGIN:VCARD"); - writer.writeLine("VERSION:3.0"); - writer.write("UID:"); - writer.writeLine(getUid()); - writer.write("FN:"); - writer.writeLine(getPropertyIfExists(properties, "cn")); - // RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes - writer.write("N:"); - writer.write(getPropertyIfExists(properties, "sn")); - writer.write(";"); - writer.write(getPropertyIfExists(properties, "givenName")); - writer.write(";"); - writer.writeLine(getPropertyIfExists(properties, "middlename")); - - writer.write("TEL;TYPE=cell:"); - writer.writeLine(getPropertyIfExists(properties, "mobile")); - writer.write("TEL;TYPE=work:"); - writer.writeLine(getPropertyIfExists(properties, "telephoneNumber")); - //writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("initials", URN_SCHEMAS_CONTACTS), "")); - - // The structured type value corresponds, in sequence, to the post office box; the extended address; - // the street address; the locality (e.g., city); the region (e.g., state or province); - // the postal code; the country name - // ADR;TYPE=dom,home,postal,parcel:;;123 Main Street;Any Town;CA;91921-1234 - writer.write("ADR;TYPE=home:;;"); - writer.write(getPropertyIfExists(properties, "homepostaladdress")); - writer.write(";;;"); - writer.newLine(); - writer.writeLine("END:VCARD"); - result = writer.toString(); - - } - } catch (DavException e) { - throw buildHttpException(e); - } catch (IOException e) { - throw buildHttpException(e); - } finally { - if (propFindMethod != null) { - propFindMethod.releaseConnection(); - } - } - return result; - - } - protected List buildProperties() throws IOException { ArrayList list = new ArrayList(); - list.add(Field.createDavProperty("contentClass", contentClass)); + list.add(Field.createDavProperty("contentclass", contentClass)); list.add(Field.createDavProperty("outlookmessageclass", "IPM.Contact")); ICSBufferedReader reader = new ICSBufferedReader(new StringReader(itemBody)); @@ -1299,7 +1235,9 @@ public class DavExchangeSession extends ExchangeSession { } String contentClass = getPropertyIfExists(responses[0].getProperties(HttpStatus.SC_OK), "contentclass"); if ("urn:content-classes:person".equals(contentClass)) { - return new Contact(responses[0]); + // retrieve Contact properties + // TODO: need to check list size + return searchContacts(itemPath.substring(0, itemPath.lastIndexOf('/')), CONTACT_ATTRIBUTES, equals("urlcompname", itemPath.substring(itemPath.lastIndexOf('/')+1))).get(0); } else if ("urn:content-classes:appointment".equals(contentClass) || "urn:content-classes:calendarmessage".equals(contentClass)) { return new Event(responses[0]); diff --git a/src/java/davmail/exchange/dav/Field.java b/src/java/davmail/exchange/dav/Field.java index e681bb19..d2621ad3 100644 --- a/src/java/davmail/exchange/dav/Field.java +++ b/src/java/davmail/exchange/dav/Field.java @@ -69,7 +69,7 @@ public class Field { ApplicationTime, ApplicationTimeArray, Binary, BinaryArray, Boolean, CLSID, CLSIDArray, Currency, CurrencyArray, Double, DoubleArray, Error, Float, FloatArray, Integer, IntegerArray, Long, LongArray, Null, Object, ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray, - Custom + String10 } protected static final Map propertyTypeMap = new HashMap(); @@ -78,9 +78,9 @@ public class Field { propertyTypeMap.put(PropertyType.Long, "0003"); propertyTypeMap.put(PropertyType.Boolean, "000b"); propertyTypeMap.put(PropertyType.SystemTime, "0040"); - propertyTypeMap.put(PropertyType.String, "001f"); + propertyTypeMap.put(PropertyType.String, "001f"); // 001f is PT_UNICODE_STRING, 001E is PT_STRING propertyTypeMap.put(PropertyType.Binary, "0102"); - propertyTypeMap.put(PropertyType.Custom, "0030"); + propertyTypeMap.put(PropertyType.String10, "0030"); // decimal PT_STRING } protected static enum DistinguishedPropertySetType { @@ -123,7 +123,7 @@ public class Field { createField(URN_SCHEMAS_HTTPMAIL, "read"); //createField("read", 0x0e69, PropertyType.Boolean);//PR_READ createField("deleted", DistinguishedPropertySetType.Common, 0x8570, "deleted"); - createField("writedeleted", DistinguishedPropertySetType.Common, 0x8570, PropertyType.Custom); + createField("writedeleted", DistinguishedPropertySetType.Common, 0x8570, PropertyType.String10); createField(URN_SCHEMAS_HTTPMAIL, "date");//PR_CLIENT_SUBMIT_TIME, 0x0039 //createField("date", 0x0e06, PropertyType.SystemTime);//PR_MESSAGE_DELIVERY_TIME @@ -141,10 +141,11 @@ public class Field { createField(URN_SCHEMAS_MAILHEADER, "to"); // DistinguishedPropertySetType.InternetHeaders/To/String createField(URN_SCHEMAS_MAILHEADER, "cc"); // DistinguishedPropertySetType.InternetHeaders/To/String - createField("lastmodified", 0x3008, PropertyType.SystemTime);//PR_LAST_MODIFICATION_TIME DAV:getlastmodified + createField("lastmodified", DAV, "getlastmodified"); // PR_LAST_MODIFICATION_TIME 0x3008 SystemTime // failover search createField(DAV, "displayname"); + createField("urlcompname", 0x10f3, PropertyType.String); //PR_URL_COMP_NAME // items createField("etag", DAV, "getetag"); diff --git a/src/java/davmail/exchange/ews/SearchExpression.java b/src/java/davmail/exchange/ews/SearchExpression.java index d5b61430..ce060dec 100644 --- a/src/java/davmail/exchange/ews/SearchExpression.java +++ b/src/java/davmail/exchange/ews/SearchExpression.java @@ -18,12 +18,14 @@ */ package davmail.exchange.ews; -import java.io.IOException; -import java.io.Writer; - /** * EWS Search Expression. */ public interface SearchExpression { - public void appendTo(StringBuilder buffer); + /** + * Append search expression to buffer. + * + * @param buffer search buffer + */ + public void appendTo(StringBuilder buffer); }