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
This commit is contained in:
mguessan 2010-06-30 21:38:01 +00:00
parent f52ea840e4
commit d7506d9f67
5 changed files with 167 additions and 89 deletions

View File

@ -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<Condition> 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<ExchangeSession.Contact> 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<Event> 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<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();
List<Contact> contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("contentclass", "urn:content-classes:person"), condition));
List<Contact> contacts = searchContacts(CONTACTS, CONTACT_ATTRIBUTES, and(equals("outlookmessageclass", "IPM.Contact"), condition));
Map<String, String> item;
for (Contact contact : contacts) {

View File

@ -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");
}
}

View File

@ -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<DavConstants> buildProperties() throws IOException {
ArrayList<DavConstants> list = new ArrayList<DavConstants>();
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]);

View File

@ -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<PropertyType, String> propertyTypeMap = new HashMap<PropertyType, String>();
@ -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");

View File

@ -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);
}