1
0
mirror of https://github.com/moparisthebest/davmail synced 2025-01-12 22:18:11 -05:00

EWS: still more WebDav code to DavExchangeSession

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1093 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-06-16 21:43:11 +00:00
parent cc454e388c
commit c8d0cda1b2
4 changed files with 137 additions and 171 deletions

View File

@ -30,18 +30,13 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.PostMethod;
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.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;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyIterator;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.log4j.Logger;
import org.htmlcleaner.CommentToken;
import org.htmlcleaner.HtmlCleaner;
@ -106,37 +101,6 @@ public abstract class ExchangeSession {
protected static final String JUNK = "Junk";
protected static final String UNSENT = "Unsent Messages";
protected static final Namespace EMPTY = Namespace.getNamespace("");
protected static final Namespace DAV = Namespace.getNamespace("DAV:");
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/");
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace.getNamespace("http://schemas.microsoft.com/mapi/proptag/");
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet();
static {
DISPLAY_NAME.add(DavPropertyName.DISPLAYNAME);
}
protected static final DavPropertyNameSet CONTENT_TAG = new DavPropertyNameSet();
static {
CONTENT_TAG.add(DavPropertyName.create("contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/")));
}
protected static final DavPropertyNameSet RESOURCE_TAG = new DavPropertyNameSet();
static {
RESOURCE_TAG.add(DavPropertyName.create("resourcetag", Namespace.getNamespace("http://schemas.microsoft.com/repl/")));
}
protected static final DavPropertyName DEFAULT_SCHEDULE_STATE_PROPERTY = DavPropertyName.create("schedule-state", Namespace.getNamespace("CALDAV:"));
protected DavPropertyName scheduleStateProperty = DEFAULT_SCHEDULE_STATE_PROPERTY;
protected static final DavPropertyName PR_INTERNET_CONTENT = DavPropertyName.create("x66590102", SCHEMAS_MAPI_PROPTAG);
/**
* Various standard mail boxes Urls
*/
@ -513,33 +477,6 @@ public abstract class ExchangeSession {
protected abstract void buildSessionInfo(HttpMethod method) throws DavMailException;
protected String getPropertyIfExists(DavPropertySet properties, DavPropertyName davPropertyName, String defaultValue) {
String value = getPropertyIfExists(properties, davPropertyName);
if (value == null) {
return defaultValue;
} else {
return value;
}
}
protected String getPropertyIfExists(DavPropertySet properties, DavPropertyName davPropertyName) {
DavProperty property = properties.get(davPropertyName);
if (property == null) {
return null;
} else {
return (String) property.getValue();
}
}
protected int getIntPropertyIfExists(DavPropertySet properties, String name, Namespace namespace) {
DavProperty property = properties.get(name, namespace);
if (property == null) {
return 0;
} else {
return Integer.parseInt((String) property.getValue());
}
}
/**
* Create message in specified folder.
* Will overwrite an existing message with same subject in the same folder
@ -3130,75 +3067,10 @@ public abstract class ExchangeSession {
}
}
protected final class VTimezone {
private String timezoneBody;
private String timezoneId;
public final class VTimezone {
public String timezoneBody;
public String timezoneId;
/**
* create a fake event to get VTIMEZONE body
*/
private void load() {
try {
// create temporary folder
String folderPath = ExchangeSession.this.getFolderPath("davmailtemp");
ExchangeSession.this.createCalendarFolder(folderPath);
PostMethod postMethod = new PostMethod(URIUtil.encodePath(folderPath));
postMethod.addParameter("Cmd", "saveappt");
postMethod.addParameter("FORMTYPE", "appointment");
String fakeEventUrl = null;
try {
// create fake event
int statusCode = ExchangeSession.this.httpClient.executeMethod(postMethod);
if (statusCode == HttpStatus.SC_OK) {
fakeEventUrl = StringUtil.getToken(postMethod.getResponseBodyAsString(), "<span id=\"itemHREF\">", "</span>");
}
} finally {
postMethod.releaseConnection();
}
// failover for Exchange 2007, use PROPPATCH with forced timezone
if (fakeEventUrl == null) {
ArrayList<DavProperty> propertyList = new ArrayList<DavProperty>();
propertyList.add(new DefaultDavProperty(DavPropertyName.create("contentclass", DAV), "urn:content-classes:appointment"));
propertyList.add(new DefaultDavProperty(DavPropertyName.create("outlookmessageclass", Namespace.getNamespace("http://schemas.microsoft.com/exchange/")), "IPM.Appointment"));
propertyList.add(new DefaultDavProperty(DavPropertyName.create("instancetype", Namespace.getNamespace("urn:schemas:calendar:")), "0"));
// get forced timezone id from settings
timezoneId = Settings.getProperty("davmail.timezoneId");
if (timezoneId != null) {
propertyList.add(new DefaultDavProperty(DavPropertyName.create("timezoneid", Namespace.getNamespace("urn:schemas:calendar:")), timezoneId));
}
String patchMethodUrl = URIUtil.encodePath(folderPath) + '/' + UUID.randomUUID().toString() + ".EML";
PropPatchMethod patchMethod = new PropPatchMethod(URIUtil.encodePath(patchMethodUrl), propertyList);
try {
int statusCode = httpClient.executeMethod(patchMethod);
if (statusCode == HttpStatus.SC_MULTI_STATUS) {
fakeEventUrl = patchMethodUrl;
}
} finally {
patchMethod.releaseConnection();
}
}
if (fakeEventUrl != null) {
// get fake event body
GetMethod getMethod = new GetMethod(URIUtil.encodePath(fakeEventUrl));
getMethod.setRequestHeader("Translate", "f");
try {
ExchangeSession.this.httpClient.executeMethod(getMethod);
timezoneBody = "BEGIN:VTIMEZONE" +
StringUtil.getToken(getMethod.getResponseBodyAsString(), "BEGIN:VTIMEZONE", "END:VTIMEZONE") +
"END:VTIMEZONE\r\n";
timezoneId = StringUtil.getToken(timezoneBody, "TZID:", "\r\n");
} finally {
getMethod.releaseConnection();
}
}
// delete temporary folder
ExchangeSession.this.deleteFolder("davmailtemp");
} catch (IOException e) {
LOGGER.warn("Unable to get VTIMEZONE info: " + e, e);
}
}
}
protected VTimezone vTimezone;
@ -3206,12 +3078,13 @@ public abstract class ExchangeSession {
protected VTimezone getVTimezone() {
if (vTimezone == null) {
// need to load Timezone info from OWA
vTimezone = new VTimezone();
vTimezone.load();
loadVtimezone();
}
return vTimezone;
}
protected abstract void loadVtimezone();
/**
* Return internal HttpClient instance
*

View File

@ -28,12 +28,10 @@ import davmail.exchange.ICSBufferedReader;
import davmail.exchange.ICSBufferedWriter;
import davmail.http.DavGatewayHttpClientFacade;
import davmail.ui.tray.DavGatewayTray;
import davmail.util.StringUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.DavException;
@ -178,7 +176,9 @@ public class DavExchangeSession extends ExchangeSession {
publicUri.setPath(PUBLIC_ROOT);
publicFolderUrl = publicUri.getURI();
}
PropFindMethod propFindMethod = new PropFindMethod(publicFolderUrl, CONTENT_TAG, 0);
DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet();
davPropertyNameSet.add(Field.getPropertyName("displayname"));
PropFindMethod propFindMethod = new PropFindMethod(publicFolderUrl, davPropertyNameSet, 0);
try {
DavGatewayHttpClientFacade.executeMethod(httpClient, propFindMethod);
} catch (IOException e) {
@ -218,8 +218,7 @@ public class DavExchangeSession extends ExchangeSession {
public boolean isExpired() throws NoRouteToHostException, UnknownHostException {
boolean isExpired = false;
try {
DavGatewayHttpClientFacade.executePropFindMethod(
httpClient, URIUtil.encodePath(inboxUrl), 0, DISPLAY_NAME);
getFolder("");
} catch (UnknownHostException exc) {
throw exc;
} catch (NoRouteToHostException exc) {
@ -461,19 +460,19 @@ public class DavExchangeSession extends ExchangeSession {
writer.write("UID:");
writer.writeLine(getUid());
writer.write("FN:");
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("cn", URN_SCHEMAS_CONTACTS), ""));
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, DavPropertyName.create("sn", URN_SCHEMAS_CONTACTS), ""));
writer.write(getPropertyIfExists(properties, "sn"));
writer.write(";");
writer.write(getPropertyIfExists(properties, DavPropertyName.create("givenName", URN_SCHEMAS_CONTACTS), ""));
writer.write(getPropertyIfExists(properties, "givenName"));
writer.write(";");
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("middlename", URN_SCHEMAS_CONTACTS), ""));
writer.writeLine(getPropertyIfExists(properties, "middlename"));
writer.write("TEL;TYPE=cell:");
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("mobile", URN_SCHEMAS_CONTACTS), ""));
writer.writeLine(getPropertyIfExists(properties, "mobile"));
writer.write("TEL;TYPE=work:");
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("telephoneNumber", URN_SCHEMAS_CONTACTS), ""));
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;
@ -481,7 +480,7 @@ public class DavExchangeSession extends ExchangeSession {
// 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, DavPropertyName.create("homepostaladdress", URN_SCHEMAS_CONTACTS), ""));
writer.write(getPropertyIfExists(properties, "homepostaladdress"));
writer.write(";;;");
writer.newLine();
writer.writeLine("END:VCARD");
@ -501,10 +500,10 @@ public class DavExchangeSession extends ExchangeSession {
}
protected List<DavProperty> buildProperties() throws IOException {
ArrayList<DavProperty> list = new ArrayList<DavProperty>();
list.add(new DefaultDavProperty(DavPropertyName.create("contentclass", DAV), contentClass));
list.add(new DefaultDavProperty(DavPropertyName.create("outlookmessageclass", SCHEMAS_EXCHANGE), "IPM.Contact"));
protected List<DavConstants> buildProperties() throws IOException {
ArrayList<DavConstants> list = new ArrayList<DavConstants>();
list.add(Field.createDavProperty("contentClass", contentClass));
list.add(Field.createDavProperty("outlookmessageclass", "IPM.Contact"));
ICSBufferedReader reader = new ICSBufferedReader(new StringReader(itemBody));
String line;
@ -514,24 +513,24 @@ public class DavExchangeSession extends ExchangeSession {
String key = line.substring(0, index);
String value = line.substring(index + 1);
if ("FN".equals(key)) {
list.add(new DefaultDavProperty(DavPropertyName.create("cn", URN_SCHEMAS_CONTACTS), value));
list.add(new DefaultDavProperty(DavPropertyName.create("subject", URN_SCHEMAS_HTTPMAIL), value));
list.add(new DefaultDavProperty(DavPropertyName.create("fileas", URN_SCHEMAS_CONTACTS), value));
list.add(Field.createDavProperty("cn", value));
list.add(Field.createDavProperty("subject", value));
list.add(Field.createDavProperty("fileas", value));
} else if ("N".equals(key)) {
String[] values = value.split(";");
if (values.length > 0) {
list.add(new DefaultDavProperty(DavPropertyName.create("sn", URN_SCHEMAS_CONTACTS), values[0]));
list.add(Field.createDavProperty("sn", values[0]));
}
if (values.length > 1) {
list.add(new DefaultDavProperty(DavPropertyName.create("givenName", URN_SCHEMAS_CONTACTS), values[1]));
list.add(Field.createDavProperty("givenName", values[1]));
}
} else if ("TEL;TYPE=cell".equals(key)) {
list.add(new DefaultDavProperty(DavPropertyName.create("mobile", URN_SCHEMAS_CONTACTS), value));
list.add(Field.createDavProperty("mobile", value));
} else if ("TEL;TYPE=work".equals(key)) {
list.add(new DefaultDavProperty(DavPropertyName.create("telephoneNumber", URN_SCHEMAS_CONTACTS), value));
list.add(Field.createDavProperty("telephoneNumber", value));
} else if ("TEL;TYPE=home".equals(key)) {
list.add(new DefaultDavProperty(DavPropertyName.create("homePhone", URN_SCHEMAS_CONTACTS), value));
list.add(Field.createDavProperty("homePhone", value));
}
}
}
@ -617,7 +616,7 @@ public class DavExchangeSession extends ExchangeSession {
*
* @param mimeInputStream mime message input stream
* @return mime message ics attachment body
* @throws IOException on error
* @throws IOException on error
* @throws javax.mail.MessagingException on error
*/
protected String getICS(InputStream mimeInputStream) throws IOException, MessagingException {
@ -651,16 +650,16 @@ public class DavExchangeSession extends ExchangeSession {
String result = null;
// PropFind PR_INTERNET_CONTENT
DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet();
davPropertyNameSet.add(PR_INTERNET_CONTENT);
davPropertyNameSet.add(Field.getPropertyName("internetContent"));
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());
String propertyValue = getPropertyIfExists(properties, "internetContent");
if (propertyValue != null) {
byte[] byteArray = Base64.decodeBase64(propertyValue.getBytes());
result = getICS(new ByteArrayInputStream(byteArray));
}
}
@ -1112,9 +1111,9 @@ public class DavExchangeSession extends ExchangeSession {
int status;
if (inboxUrl.endsWith(folderPath)) {
// do not delete calendar messages, mark read and processed
ArrayList<DavProperty> list = new ArrayList<DavProperty>();
list.add(new DefaultDavProperty(scheduleStateProperty, "CALDAV:schedule-processed"));
list.add(new DefaultDavProperty(DavPropertyName.create("read", URN_SCHEMAS_HTTPMAIL), "1"));
ArrayList<DavConstants> list = new ArrayList<DavConstants>();
list.add(Field.createDavProperty("processed", "1"));
list.add(Field.createDavProperty("read", "1"));
PropPatchMethod patchMethod = new PropPatchMethod(eventPath, list);
DavGatewayHttpClientFacade.executeMethod(httpClient, patchMethod);
status = HttpStatus.SC_OK;
@ -1153,6 +1152,75 @@ public class DavExchangeSession extends ExchangeSession {
return event.createOrUpdate();
}
/**
* create a fake event to get VTIMEZONE body
*/
@Override
protected void loadVtimezone() {
try {
VTimezone vTimezone = new VTimezone();
// create temporary folder
String folderPath = getFolderPath("davmailtemp");
createCalendarFolder(folderPath);
PostMethod postMethod = new PostMethod(URIUtil.encodePath(folderPath));
postMethod.addParameter("Cmd", "saveappt");
postMethod.addParameter("FORMTYPE", "appointment");
String fakeEventUrl = null;
try {
// create fake event
int statusCode = httpClient.executeMethod(postMethod);
if (statusCode == HttpStatus.SC_OK) {
fakeEventUrl = StringUtil.getToken(postMethod.getResponseBodyAsString(), "<span id=\"itemHREF\">", "</span>");
}
} finally {
postMethod.releaseConnection();
}
// failover for Exchange 2007, use PROPPATCH with forced timezone
if (fakeEventUrl == null) {
ArrayList<DavConstants> propertyList = new ArrayList<DavConstants>();
propertyList.add(Field.createDavProperty("contentclass", "urn:content-classes:appointment"));
propertyList.add(Field.createDavProperty("outlookmessageclass", "IPM.Appointment"));
propertyList.add(Field.createDavProperty("instancetype", "0"));
// get forced timezone id from settings
vTimezone.timezoneId = Settings.getProperty("davmail.timezoneId");
if (vTimezone.timezoneId != null) {
propertyList.add(Field.createDavProperty("timezoneid", vTimezone.timezoneId));
}
String patchMethodUrl = URIUtil.encodePath(folderPath) + '/' + UUID.randomUUID().toString() + ".EML";
PropPatchMethod patchMethod = new PropPatchMethod(URIUtil.encodePath(patchMethodUrl), propertyList);
try {
int statusCode = httpClient.executeMethod(patchMethod);
if (statusCode == HttpStatus.SC_MULTI_STATUS) {
fakeEventUrl = patchMethodUrl;
}
} finally {
patchMethod.releaseConnection();
}
}
if (fakeEventUrl != null) {
// get fake event body
GetMethod getMethod = new GetMethod(URIUtil.encodePath(fakeEventUrl));
getMethod.setRequestHeader("Translate", "f");
try {
httpClient.executeMethod(getMethod);
vTimezone.timezoneBody = "BEGIN:VTIMEZONE" +
StringUtil.getToken(getMethod.getResponseBodyAsString(), "BEGIN:VTIMEZONE", "END:VTIMEZONE") +
"END:VTIMEZONE\r\n";
vTimezone.timezoneId = StringUtil.getToken(vTimezone.timezoneBody, "TZID:", "\r\n");
} finally {
getMethod.releaseConnection();
}
}
// delete temporary folder
deleteFolder("davmailtemp");
this.vTimezone = vTimezone;
} catch (IOException e) {
LOGGER.warn("Unable to get VTIMEZONE info: " + e, e);
}
}
@Override
protected ItemResult internalCreateOrUpdateContact(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
Contact contact = new Contact(messageUrl, contentClass, icsBody, etag, noneMatch);
@ -1186,7 +1254,7 @@ public class DavExchangeSession extends ExchangeSession {
// TODO: need to test this
list.add(Field.createDavProperty("writedeleted", entry.getValue()));
} else if ("datereceived".equals(entry.getKey())) {
list.add(new DefaultDavProperty(DavPropertyName.create("datereceived", URN_SCHEMAS_HTTPMAIL), entry.getValue()));
list.add(Field.createDavProperty("datereceived", entry.getValue()));
}
}
return list;

View File

@ -125,6 +125,8 @@ public class Field {
createField(URN_SCHEMAS_HTTPMAIL, "date");//PR_CLIENT_SUBMIT_TIME, 0x0039
//createField("date", 0x0e06, PropertyType.SystemTime);//PR_MESSAGE_DELIVERY_TIME
createField(URN_SCHEMAS_MAILHEADER, "bcc");//PS_INTERNET_HEADERS/bcc
createField(URN_SCHEMAS_HTTPMAIL, "datereceived");//PR_MESSAGE_DELIVERY_TIME, 0x0E06
// IMAP search
@ -149,10 +151,28 @@ public class Field {
createField(URN_SCHEMAS_CALENDAR, "instancetype");
createField(URN_SCHEMAS_CALENDAR, "dtstart");
createField(SCHEMAS_EXCHANGE, "sensitivity");
createField(URN_SCHEMAS_CALENDAR, "timezoneid");
createField("processed", 0x65e8, PropertyType.Boolean);//PR_MESSAGE_PROCESSED
createField(DAV, "contentclass");
createField("internetContent", 0x6659, PropertyType.Binary);
// contact
createField(SCHEMAS_EXCHANGE, "outlookmessageclass");
createField(URN_SCHEMAS_HTTPMAIL, "subject");
createField(URN_SCHEMAS_CONTACTS, "cn"); // PR_DISPLAY_NAME 0x3001
createField(URN_SCHEMAS_CONTACTS, "sn"); // PR_SURNAME 0x3A11
createField(URN_SCHEMAS_CONTACTS, "givenName"); // PR_GIVEN_NAME 0x3A06
createField(URN_SCHEMAS_CONTACTS, "middlename"); // PR_MIDDLE_NAME 0x3A44
createField(URN_SCHEMAS_CONTACTS, "mobile"); // PR_MOBILE_TELEPHONE_NUMBER 0x3A1C
createField(URN_SCHEMAS_CONTACTS, "telephoneNumber"); // PR_BUSINESS_TELEPHONE_NUMBER 0x3A08
createField(URN_SCHEMAS_CONTACTS, "homepostaladdress"); // 0x0000801A DistinguishedPropertySetType.Address
createField(URN_SCHEMAS_CONTACTS, "fileas"); // urn:schemas:contacts:fileas PS_PUBLIC_STRINGS
createField(URN_SCHEMAS_CONTACTS, "homePhone"); // PR_HOME_TELEPHONE_NUMBER 0x3A09
}
protected static void createField(String alias, int propertyTag, PropertyType propertyType) {

View File

@ -436,6 +436,11 @@ public class EwsExchangeSession extends ExchangeSession {
throw new UnsupportedOperationException();
}
@Override
protected void loadVtimezone() {
throw new UnsupportedOperationException();
}
private FolderId getFolderId(String folderPath) throws IOException {
String[] folderNames;