mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-13 11:12:22 -05:00
EWS: in progress refactoring of contacts and events handling
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1090 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
7b7fa90edf
commit
d344693d1e
@ -115,15 +115,6 @@ public abstract class ExchangeSession {
|
|||||||
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
|
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
|
||||||
|
|
||||||
|
|
||||||
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES = new DavPropertyNameSet();
|
|
||||||
|
|
||||||
static {
|
|
||||||
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("permanenturl", SCHEMAS_EXCHANGE));
|
|
||||||
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.GETETAG);
|
|
||||||
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("contentclass", DAV));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet();
|
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -1552,8 +1543,8 @@ public abstract class ExchangeSession {
|
|||||||
public abstract class Item {
|
public abstract class Item {
|
||||||
protected String href;
|
protected String href;
|
||||||
protected String permanentUrl;
|
protected String permanentUrl;
|
||||||
protected String displayName;
|
public String displayName;
|
||||||
protected String etag;
|
public String etag;
|
||||||
protected String contentClass;
|
protected String contentClass;
|
||||||
protected String noneMatch;
|
protected String noneMatch;
|
||||||
/**
|
/**
|
||||||
@ -1578,6 +1569,12 @@ public abstract class ExchangeSession {
|
|||||||
this.noneMatch = noneMatch;
|
this.noneMatch = noneMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
protected Item() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return item content type
|
* Return item content type
|
||||||
*
|
*
|
||||||
@ -1593,19 +1590,6 @@ public abstract class ExchangeSession {
|
|||||||
*/
|
*/
|
||||||
public abstract String getBody() throws HttpException;
|
public abstract String getBody() throws HttpException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Build Item instance from multistatusResponse info
|
|
||||||
*
|
|
||||||
* @param multiStatusResponse response
|
|
||||||
* @throws URIException on error
|
|
||||||
*/
|
|
||||||
public Item(MultiStatusResponse multiStatusResponse) throws URIException {
|
|
||||||
href = URIUtil.decode(multiStatusResponse.getHref());
|
|
||||||
permanentUrl = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "permanenturl", SCHEMAS_EXCHANGE);
|
|
||||||
etag = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "getetag", DAV);
|
|
||||||
displayName = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "displayname", DAV);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get event name (file name part in URL).
|
* Get event name (file name part in URL).
|
||||||
*
|
*
|
||||||
@ -1639,24 +1623,21 @@ public abstract class ExchangeSession {
|
|||||||
/**
|
/**
|
||||||
* Calendar event object
|
* Calendar event object
|
||||||
*/
|
*/
|
||||||
public class Contact extends Item {
|
public abstract class Contact extends Item {
|
||||||
/**
|
|
||||||
* Build Contact instance from multistatusResponse info
|
|
||||||
*
|
|
||||||
* @param multiStatusResponse response
|
|
||||||
* @throws URIException on error
|
|
||||||
*/
|
|
||||||
public Contact(MultiStatusResponse multiStatusResponse) throws URIException {
|
|
||||||
super(multiStatusResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public Contact(String messageUrl, String contentClass, String itemBody, String etag, String noneMatch) {
|
public Contact(String messageUrl, String contentClass, String itemBody, String etag, String noneMatch) {
|
||||||
super(messageUrl.endsWith(".vcf") ? messageUrl.substring(0, messageUrl.length() - 3) + "EML" : messageUrl, contentClass, itemBody, etag, noneMatch);
|
super(messageUrl.endsWith(".vcf") ? messageUrl.substring(0, messageUrl.length() - 3) + "EML" : messageUrl, contentClass, itemBody, etag, noneMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected Contact() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert EML extension to vcf.
|
* Convert EML extension to vcf.
|
||||||
*
|
*
|
||||||
@ -1787,7 +1768,7 @@ public abstract class ExchangeSession {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ItemResult createOrUpdate() throws IOException {
|
public ItemResult createOrUpdate() throws IOException {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(href), buildProperties());
|
PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(href), buildProperties());
|
||||||
propPatchMethod.setRequestHeader("Translate", "f");
|
propPatchMethod.setRequestHeader("Translate", "f");
|
||||||
@ -1844,16 +1825,7 @@ public abstract class ExchangeSession {
|
|||||||
/**
|
/**
|
||||||
* Calendar event object.
|
* Calendar event object.
|
||||||
*/
|
*/
|
||||||
public class Event extends Item {
|
public abstract class Event extends Item {
|
||||||
/**
|
|
||||||
* Build Event instance from response info.
|
|
||||||
*
|
|
||||||
* @param multiStatusResponse response
|
|
||||||
* @throws URIException on error
|
|
||||||
*/
|
|
||||||
public Event(MultiStatusResponse multiStatusResponse) throws URIException {
|
|
||||||
super(multiStatusResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
@ -1862,6 +1834,12 @@ public abstract class ExchangeSession {
|
|||||||
super(messageUrl, contentClass, itemBody, etag, noneMatch);
|
super(messageUrl, contentClass, itemBody, etag, noneMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected Event() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return "text/calendar;charset=UTF-8";
|
return "text/calendar;charset=UTF-8";
|
||||||
@ -2453,21 +2431,11 @@ public abstract class ExchangeSession {
|
|||||||
return icsMethod;
|
return icsMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ItemResult createOrUpdate() throws IOException {
|
public ItemResult createOrUpdate() throws IOException {
|
||||||
String boundary = UUID.randomUUID().toString();
|
String boundary = UUID.randomUUID().toString();
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
MimeOutputStreamWriter writer = new MimeOutputStreamWriter(baos);
|
MimeOutputStreamWriter writer = new MimeOutputStreamWriter(baos);
|
||||||
int status = 0;
|
int status = 0;
|
||||||
PutMethod putmethod = new PutMethod(URIUtil.encodePath(href));
|
|
||||||
putmethod.setRequestHeader("Translate", "f");
|
|
||||||
putmethod.setRequestHeader("Overwrite", "f");
|
|
||||||
if (etag != null) {
|
|
||||||
putmethod.setRequestHeader("If-Match", etag);
|
|
||||||
}
|
|
||||||
if (noneMatch != null) {
|
|
||||||
putmethod.setRequestHeader("If-None-Match", noneMatch);
|
|
||||||
}
|
|
||||||
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
|
||||||
String method = getICSMethod(itemBody);
|
String method = getICSMethod(itemBody);
|
||||||
|
|
||||||
writer.writeHeader("Content-Transfer-Encoding", "7bit");
|
writer.writeHeader("Content-Transfer-Encoding", "7bit");
|
||||||
@ -2560,54 +2528,30 @@ public abstract class ExchangeSession {
|
|||||||
writer.writeLn();
|
writer.writeLn();
|
||||||
writer.writeLn("------=_NextPart_" + boundary + "--");
|
writer.writeLn("------=_NextPart_" + boundary + "--");
|
||||||
writer.close();
|
writer.close();
|
||||||
putmethod.setRequestEntity(new ByteArrayRequestEntity(baos.toByteArray(), "message/rfc822"));
|
|
||||||
try {
|
ItemResult itemResult;
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
status = httpClient.executeMethod(putmethod);
|
itemResult = createOrUpdate(baos.toByteArray());
|
||||||
if (status == HttpURLConnection.HTTP_OK) {
|
|
||||||
if (etag != null) {
|
|
||||||
LOGGER.debug("Updated event " + href);
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Overwritten event " + href);
|
itemResult = new ItemResult();
|
||||||
}
|
|
||||||
} else if (status != HttpURLConnection.HTTP_CREATED) {
|
|
||||||
LOGGER.warn("Unable to create or update message " + status + ' ' + putmethod.getStatusLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
putmethod.releaseConnection();
|
|
||||||
}
|
|
||||||
ItemResult itemResult = new ItemResult();
|
|
||||||
// 440 means forbidden on Exchange
|
|
||||||
if (status == 440) {
|
|
||||||
status = HttpStatus.SC_FORBIDDEN;
|
|
||||||
}
|
|
||||||
itemResult.status = status;
|
itemResult.status = status;
|
||||||
if (putmethod.getResponseHeader("GetETag") != null) {
|
|
||||||
itemResult.etag = putmethod.getResponseHeader("GetETag").getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger activeSync push event, only if davmail.forceActiveSyncUpdate setting is true
|
|
||||||
if ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) &&
|
|
||||||
(Settings.getBooleanProperty("davmail.forceActiveSyncUpdate"))) {
|
|
||||||
ArrayList<DavProperty> propertyList = new ArrayList<DavProperty>();
|
|
||||||
// Set contentclass to make ActiveSync happy
|
|
||||||
propertyList.add(new DefaultDavProperty(DavPropertyName.create("contentclass", DAV), contentClass));
|
|
||||||
// ... but also set PR_INTERNET_CONTENT to preserve custom properties
|
|
||||||
propertyList.add(new DefaultDavProperty(PR_INTERNET_CONTENT, new String(Base64.encodeBase64(baos.toByteArray()))));
|
|
||||||
PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(href), propertyList);
|
|
||||||
int patchStatus = DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propPatchMethod);
|
|
||||||
if (patchStatus != HttpStatus.SC_MULTI_STATUS) {
|
|
||||||
LOGGER.warn("Unable to patch event to trigger activeSync push");
|
|
||||||
} else {
|
|
||||||
// need to retrieve new etag
|
|
||||||
Item newItem = getItem(href);
|
|
||||||
itemResult.etag = newItem.etag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return itemResult;
|
return itemResult;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract ItemResult createOrUpdate(byte[] content) throws IOException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static final List<String> ITEM_PROPERTIES = new ArrayList<String>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
ITEM_PROPERTIES.add("etag");
|
||||||
|
ITEM_PROPERTIES.add("displayname");
|
||||||
|
// calendar CdoInstanceType
|
||||||
|
ITEM_PROPERTIES.add("instancetype");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2617,14 +2561,11 @@ public abstract class ExchangeSession {
|
|||||||
* @return list of contacts
|
* @return list of contacts
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public List<Contact> getAllContacts(String folderPath) throws IOException {
|
public List<ExchangeSession.Contact> getAllContacts(String folderName) throws IOException {
|
||||||
|
return searchContacts(folderName, ITEM_PROPERTIES, equals("contentclass", "urn:content-classes:person"));
|
||||||
String searchQuery = "Select \"DAV:getetag\", \"http://schemas.microsoft.com/exchange/permanenturl\", \"DAV:displayname\"" +
|
|
||||||
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderPath + "\"')\n" +
|
|
||||||
" WHERE \"DAV:contentclass\" = 'urn:content-classes:person'\n";
|
|
||||||
return getContacts(folderPath, searchQuery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search contacts in provided folder matching the search query.
|
* Search contacts in provided folder matching the search query.
|
||||||
*
|
*
|
||||||
@ -2633,14 +2574,7 @@ public abstract class ExchangeSession {
|
|||||||
* @return list of contacts
|
* @return list of contacts
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
protected List<Contact> getContacts(String folderPath, String searchQuery) throws IOException {
|
protected abstract List<Contact> searchContacts(String folderName, List<String> attributes, Condition condition) throws IOException;
|
||||||
List<Contact> contacts = new ArrayList<Contact>();
|
|
||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(httpClient, URIUtil.encodePath(folderPath), searchQuery);
|
|
||||||
for (MultiStatusResponse response : responses) {
|
|
||||||
contacts.add(new Contact(response));
|
|
||||||
}
|
|
||||||
return contacts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search calendar messages in provided folder.
|
* Search calendar messages in provided folder.
|
||||||
@ -2650,25 +2584,9 @@ public abstract class ExchangeSession {
|
|||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public List<Event> getEventMessages(String folderPath) throws IOException {
|
public List<Event> getEventMessages(String folderPath) throws IOException {
|
||||||
List<Event> result;
|
return searchEvents(folderPath, ITEM_PROPERTIES,
|
||||||
try {
|
and(equals("contentclass", "urn:content-classes:calendarmessage"),
|
||||||
String scheduleStatePropertyName = scheduleStateProperty.getNamespace().getURI() + scheduleStateProperty.getName();
|
isFalse("processed")));
|
||||||
String searchQuery = "Select \"DAV:getetag\", \"http://schemas.microsoft.com/exchange/permanenturl\", \"urn:schemas:calendar:instancetype\", \"DAV:displayname\"" +
|
|
||||||
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderPath + "\"')\n" +
|
|
||||||
" WHERE \"DAV:contentclass\" = 'urn:content-classes:calendarmessage'\n" +
|
|
||||||
" AND (\"" + scheduleStatePropertyName + "\" IS NULL OR NOT \"" + scheduleStatePropertyName + "\" = 'CALDAV:schedule-processed')\n" +
|
|
||||||
" ORDER BY \"urn:schemas:calendar:dtstart\" DESC\n";
|
|
||||||
result = getEvents(folderPath, searchQuery);
|
|
||||||
} catch (HttpException e) {
|
|
||||||
// failover to DAV:comment property on some Exchange servers
|
|
||||||
if (DEFAULT_SCHEDULE_STATE_PROPERTY.equals(scheduleStateProperty)) {
|
|
||||||
scheduleStateProperty = DavPropertyName.create("comment", DAV);
|
|
||||||
result = getEventMessages(folderPath);
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2680,31 +2598,26 @@ public abstract class ExchangeSession {
|
|||||||
*/
|
*/
|
||||||
public List<Event> getAllEvents(String folderPath) throws IOException {
|
public List<Event> getAllEvents(String folderPath) throws IOException {
|
||||||
int caldavPastDelay = Settings.getIntProperty("davmail.caldavPastDelay");
|
int caldavPastDelay = Settings.getIntProperty("davmail.caldavPastDelay");
|
||||||
String dateCondition = "";
|
Condition dateCondition = null;
|
||||||
if (caldavPastDelay != 0) {
|
if (caldavPastDelay != 0) {
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
cal.add(Calendar.DAY_OF_MONTH, -caldavPastDelay);
|
cal.add(Calendar.DAY_OF_MONTH, -caldavPastDelay);
|
||||||
dateCondition = " AND \"urn:schemas:calendar:dtstart\" > '" + formatSearchDate(cal.getTime()) + "'\n";
|
dateCondition = gt("dtstart", formatSearchDate(cal.getTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String privateCondition = "";
|
Condition privateCondition = null;
|
||||||
if (isSharedFolder(folderPath)) {
|
if (isSharedFolder(folderPath)) {
|
||||||
LOGGER.debug("Shared or public calendar: exclude private events");
|
LOGGER.debug("Shared or public calendar: exclude private events");
|
||||||
privateCondition = " AND \"http://schemas.microsoft.com/exchange/sensitivity\" = 0\n";
|
privateCondition = equals("sensitivity", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
String searchQuery = "Select \"DAV:getetag\", \"http://schemas.microsoft.com/exchange/permanenturl\", \"urn:schemas:calendar:instancetype\", \"DAV:displayname\"" +
|
|
||||||
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderPath + "\"')\n" +
|
return searchEvents(folderPath, ITEM_PROPERTIES,
|
||||||
" WHERE (" +
|
and(or(isNull("instancetype"),
|
||||||
// VTODO events have a null instancetype
|
equals("instancetype", "1"),
|
||||||
" \"urn:schemas:calendar:instancetype\" is null OR" +
|
and(equals("instancetype", "0"), dateCondition)),
|
||||||
" \"urn:schemas:calendar:instancetype\" = 1\n" +
|
equals("contentclass", "urn:content-classes:appointment"),
|
||||||
" OR (\"urn:schemas:calendar:instancetype\" = 0\n" +
|
privateCondition));
|
||||||
dateCondition +
|
|
||||||
" )) AND \"DAV:contentclass\" = 'urn:content-classes:appointment'\n" +
|
|
||||||
privateCondition +
|
|
||||||
" ORDER BY \"urn:schemas:calendar:dtstart\" DESC\n";
|
|
||||||
return getEvents(folderPath, searchQuery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2716,29 +2629,7 @@ public abstract class ExchangeSession {
|
|||||||
* @return list of calendar messages as Event objects
|
* @return list of calendar messages as Event objects
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
protected List<Event> getEvents(String folderPath, String searchQuery) throws IOException {
|
protected abstract List<Event> searchEvents(String folderPath, List<String> attributes, Condition condition) throws IOException;
|
||||||
List<Event> events = new ArrayList<Event>();
|
|
||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(httpClient, URIUtil.encodePath(folderPath), searchQuery);
|
|
||||||
for (MultiStatusResponse response : responses) {
|
|
||||||
String instancetype = getPropertyIfExists(response.getProperties(HttpStatus.SC_OK), "instancetype", Namespace.getNamespace("urn:schemas:calendar:"));
|
|
||||||
Event event = new Event(response);
|
|
||||||
//noinspection VariableNotUsedInsideIf
|
|
||||||
if (instancetype == null) {
|
|
||||||
// check ics content
|
|
||||||
try {
|
|
||||||
event.getBody();
|
|
||||||
// getBody success => add event or task
|
|
||||||
events.add(event);
|
|
||||||
} catch (HttpException e) {
|
|
||||||
// invalid event: exclude from list
|
|
||||||
LOGGER.warn("Invalid event " + event.displayName + " found at " + response.getHref(), e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
events.add(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return events;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert vcf extension to EML.
|
* convert vcf extension to EML.
|
||||||
@ -2762,49 +2653,7 @@ public abstract class ExchangeSession {
|
|||||||
* @return event object
|
* @return event object
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public Item getItem(String folderPath, String itemName) throws IOException {
|
public abstract Item getItem(String folderPath, String itemName) throws IOException;
|
||||||
String itemPath = folderPath + '/' + convertItemNameToEML(itemName);
|
|
||||||
Item item;
|
|
||||||
try {
|
|
||||||
item = getItem(itemPath);
|
|
||||||
} catch (HttpNotFoundException hnfe) {
|
|
||||||
// failover for Exchange 2007 plus encoding issue
|
|
||||||
String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
|
|
||||||
LOGGER.debug("Item not found at " + itemPath + ", search by displayname: '" + decodedEventName + '\'');
|
|
||||||
ExchangeSession.MessageList messages = searchMessages(folderPath, equals("displayname", decodedEventName));
|
|
||||||
if (!messages.isEmpty()) {
|
|
||||||
item = getItem(messages.get(0).getPermanentUrl());
|
|
||||||
} else {
|
|
||||||
throw hnfe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get item by url
|
|
||||||
*
|
|
||||||
* @param itemPath Event path
|
|
||||||
* @return event object
|
|
||||||
* @throws IOException on error
|
|
||||||
*/
|
|
||||||
public Item getItem(String itemPath) throws IOException {
|
|
||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES);
|
|
||||||
if (responses.length == 0) {
|
|
||||||
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
|
||||||
}
|
|
||||||
String contentClass = getPropertyIfExists(responses[0].getProperties(HttpStatus.SC_OK),
|
|
||||||
"contentclass", DAV);
|
|
||||||
if ("urn:content-classes:person".equals(contentClass)) {
|
|
||||||
return new Contact(responses[0]);
|
|
||||||
} else if ("urn:content-classes:appointment".equals(contentClass)
|
|
||||||
|| "urn:content-classes:calendarmessage".equals(contentClass)) {
|
|
||||||
return new Event(responses[0]);
|
|
||||||
} else {
|
|
||||||
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete event named eventName in folder
|
* Delete event named eventName in folder
|
||||||
@ -2905,15 +2754,9 @@ public abstract class ExchangeSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ItemResult internalCreateOrUpdateContact(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
protected abstract ItemResult internalCreateOrUpdateContact(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException;
|
||||||
Contact contact = new Contact(messageUrl, contentClass, icsBody, etag, noneMatch);
|
|
||||||
return contact.createOrUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ItemResult internalCreateOrUpdateEvent(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
protected abstract ItemResult internalCreateOrUpdateEvent(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException;
|
||||||
Event event = new Event(messageUrl, contentClass, icsBody, etag, noneMatch);
|
|
||||||
return event.createOrUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get folder ctag (change tag).
|
* Get folder ctag (change tag).
|
||||||
|
@ -26,6 +26,7 @@ import davmail.exception.HttpNotFoundException;
|
|||||||
import davmail.exchange.ExchangeSession;
|
import davmail.exchange.ExchangeSession;
|
||||||
import davmail.http.DavGatewayHttpClientFacade;
|
import davmail.http.DavGatewayHttpClientFacade;
|
||||||
import davmail.ui.tray.DavGatewayTray;
|
import davmail.ui.tray.DavGatewayTray;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.httpclient.*;
|
import org.apache.commons.httpclient.*;
|
||||||
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
|
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
|
||||||
import org.apache.commons.httpclient.methods.GetMethod;
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
@ -41,6 +42,7 @@ import org.apache.jackrabbit.webdav.property.*;
|
|||||||
import org.apache.jackrabbit.webdav.xml.Namespace;
|
import org.apache.jackrabbit.webdav.xml.Namespace;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.NoRouteToHostException;
|
import java.net.NoRouteToHostException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@ -228,18 +230,24 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
@Override
|
@Override
|
||||||
public void appendTo(StringBuilder buffer) {
|
public void appendTo(StringBuilder buffer) {
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
buffer.append('(');
|
|
||||||
for (Condition condition : conditions) {
|
for (Condition condition : conditions) {
|
||||||
|
if (condition != null) {
|
||||||
if (first) {
|
if (first) {
|
||||||
|
buffer.append('(');
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
buffer.append(' ').append(operator).append(' ');
|
buffer.append(' ').append(operator).append(' ');
|
||||||
}
|
}
|
||||||
condition.appendTo(buffer);
|
condition.appendTo(buffer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// at least one non empty condition
|
||||||
|
if (!first) {
|
||||||
buffer.append(')');
|
buffer.append(')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static class NotCondition extends ExchangeSession.NotCondition {
|
protected static class NotCondition extends ExchangeSession.NotCondition {
|
||||||
protected NotCondition(Condition condition) {
|
protected NotCondition(Condition condition) {
|
||||||
@ -264,7 +272,7 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
operatorMap.put(Operator.IsLessThan, " < ");
|
operatorMap.put(Operator.IsLessThan, " < ");
|
||||||
operatorMap.put(Operator.Like, " like ");
|
operatorMap.put(Operator.Like, " like ");
|
||||||
operatorMap.put(Operator.IsNull, " is null");
|
operatorMap.put(Operator.IsNull, " is null");
|
||||||
|
operatorMap.put(Operator.IsFalse, " is false");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class AttributeCondition extends ExchangeSession.AttributeCondition {
|
protected static class AttributeCondition extends ExchangeSession.AttributeCondition {
|
||||||
@ -386,6 +394,109 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
return new MonoCondition(attributeName, Operator.IsFalse);
|
return new MonoCondition(attributeName, Operator.IsFalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Contact extends ExchangeSession.Contact {
|
||||||
|
/**
|
||||||
|
* Build Contact instance from multistatusResponse info
|
||||||
|
*
|
||||||
|
* @param multiStatusResponse response
|
||||||
|
* @throws URIException on error
|
||||||
|
*/
|
||||||
|
public Contact(MultiStatusResponse multiStatusResponse) throws URIException {
|
||||||
|
href = URIUtil.decode(multiStatusResponse.getHref());
|
||||||
|
DavPropertySet properties = multiStatusResponse.getProperties(HttpStatus.SC_OK);
|
||||||
|
permanentUrl = getPropertyIfExists(properties, "permanenturl", SCHEMAS_EXCHANGE);
|
||||||
|
etag = getPropertyIfExists(properties, "getetag", DAV);
|
||||||
|
displayName = getPropertyIfExists(properties, "displayname", DAV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public Contact(String messageUrl, String contentClass, String itemBody, String etag, String noneMatch) {
|
||||||
|
super(messageUrl, contentClass, itemBody, etag, noneMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Event extends ExchangeSession.Event {
|
||||||
|
/**
|
||||||
|
* Build Event instance from response info.
|
||||||
|
*
|
||||||
|
* @param multiStatusResponse response
|
||||||
|
* @throws URIException on error
|
||||||
|
*/
|
||||||
|
public Event(MultiStatusResponse multiStatusResponse) throws URIException {
|
||||||
|
href = URIUtil.decode(multiStatusResponse.getHref());
|
||||||
|
DavPropertySet properties = multiStatusResponse.getProperties(HttpStatus.SC_OK);
|
||||||
|
permanentUrl = getPropertyIfExists(properties, "permanenturl", SCHEMAS_EXCHANGE);
|
||||||
|
etag = getPropertyIfExists(properties, "getetag", DAV);
|
||||||
|
displayName = getPropertyIfExists(properties, "displayname", DAV);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(String messageUrl, String contentClass, String itemBody, String etag, String noneMatch) {
|
||||||
|
super(messageUrl, contentClass, itemBody, etag, noneMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ItemResult createOrUpdate(byte[] messageContent) throws IOException {
|
||||||
|
PutMethod putmethod = new PutMethod(URIUtil.encodePath(href));
|
||||||
|
putmethod.setRequestHeader("Translate", "f");
|
||||||
|
putmethod.setRequestHeader("Overwrite", "f");
|
||||||
|
if (etag != null) {
|
||||||
|
putmethod.setRequestHeader("If-Match", etag);
|
||||||
|
}
|
||||||
|
if (noneMatch != null) {
|
||||||
|
putmethod.setRequestHeader("If-None-Match", noneMatch);
|
||||||
|
}
|
||||||
|
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
||||||
|
putmethod.setRequestEntity(new ByteArrayRequestEntity(messageContent, "message/rfc822"));
|
||||||
|
int status;
|
||||||
|
try {
|
||||||
|
status = httpClient.executeMethod(putmethod);
|
||||||
|
if (status == HttpURLConnection.HTTP_OK) {
|
||||||
|
if (etag != null) {
|
||||||
|
LOGGER.debug("Updated event " + href);
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Overwritten event " + href);
|
||||||
|
}
|
||||||
|
} else if (status != HttpURLConnection.HTTP_CREATED) {
|
||||||
|
LOGGER.warn("Unable to create or update message " + status + ' ' + putmethod.getStatusLine());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
putmethod.releaseConnection();
|
||||||
|
}
|
||||||
|
ItemResult itemResult = new ItemResult();
|
||||||
|
// 440 means forbidden on Exchange
|
||||||
|
if (status == 440) {
|
||||||
|
status = HttpStatus.SC_FORBIDDEN;
|
||||||
|
}
|
||||||
|
itemResult.status = status;
|
||||||
|
if (putmethod.getResponseHeader("GetETag") != null) {
|
||||||
|
itemResult.etag = putmethod.getResponseHeader("GetETag").getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger activeSync push event, only if davmail.forceActiveSyncUpdate setting is true
|
||||||
|
if ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) &&
|
||||||
|
(Settings.getBooleanProperty("davmail.forceActiveSyncUpdate"))) {
|
||||||
|
ArrayList<DavConstants> propertyList = new ArrayList<DavConstants>();
|
||||||
|
// Set contentclass to make ActiveSync happy
|
||||||
|
propertyList.add(Field.createDavProperty("contentclass", contentClass));
|
||||||
|
// ... but also set PR_INTERNET_CONTENT to preserve custom properties
|
||||||
|
propertyList.add(Field.createDavProperty("internetContent", new String(Base64.encodeBase64(messageContent))));
|
||||||
|
PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(href), propertyList);
|
||||||
|
int patchStatus = DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propPatchMethod);
|
||||||
|
if (patchStatus != HttpStatus.SC_MULTI_STATUS) {
|
||||||
|
LOGGER.warn("Unable to patch event to trigger activeSync push");
|
||||||
|
} else {
|
||||||
|
// need to retrieve new etag
|
||||||
|
Item newItem = getItem(href);
|
||||||
|
itemResult.etag = newItem.etag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return itemResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected Folder buildFolder(MultiStatusResponse entity) throws IOException {
|
protected Folder buildFolder(MultiStatusResponse entity) throws IOException {
|
||||||
String href = URIUtil.decode(entity.getHref());
|
String href = URIUtil.decode(entity.getHref());
|
||||||
@ -604,8 +715,58 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException {
|
public MessageList searchMessages(String folderName, List<String> attributes, Condition condition) throws IOException {
|
||||||
String folderUrl = getFolderPath(folderName);
|
|
||||||
MessageList messages = new MessageList();
|
MessageList messages = new MessageList();
|
||||||
|
MultiStatusResponse[] responses = searchItems(folderName, attributes, condition);
|
||||||
|
|
||||||
|
for (MultiStatusResponse response : responses) {
|
||||||
|
Message message = buildMessage(response);
|
||||||
|
message.messageList = messages;
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
Collections.sort(messages);
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected List<ExchangeSession.Contact> searchContacts(String folderName, List<String> attributes, Condition condition) throws IOException {
|
||||||
|
List<ExchangeSession.Contact> contacts = new ArrayList<ExchangeSession.Contact>();
|
||||||
|
MultiStatusResponse[] responses = searchItems(folderName, attributes, condition);
|
||||||
|
for (MultiStatusResponse response : responses) {
|
||||||
|
contacts.add(new Contact(response));
|
||||||
|
}
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<ExchangeSession.Event> searchEvents(String folderName, List<String> attributes, Condition condition) throws IOException {
|
||||||
|
List<ExchangeSession.Event> events = new ArrayList<ExchangeSession.Event>();
|
||||||
|
MultiStatusResponse[] responses = searchItems(folderName, attributes, condition);
|
||||||
|
for (MultiStatusResponse response : responses) {
|
||||||
|
String instancetype = getPropertyIfExists(response.getProperties(HttpStatus.SC_OK), "instancetype");
|
||||||
|
Event event = new Event(response);
|
||||||
|
//noinspection VariableNotUsedInsideIf
|
||||||
|
if (instancetype == null) {
|
||||||
|
// check ics content
|
||||||
|
try {
|
||||||
|
event.getBody();
|
||||||
|
// getBody success => add event or task
|
||||||
|
events.add(event);
|
||||||
|
} catch (HttpException e) {
|
||||||
|
// invalid event: exclude from list
|
||||||
|
LOGGER.warn("Invalid event " + event.displayName + " found at " + response.getHref(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
events.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiStatusResponse[] searchItems(String folderName, List<String> attributes, Condition condition) throws IOException {
|
||||||
|
String folderUrl = getFolderPath(folderName);
|
||||||
StringBuilder searchRequest = new StringBuilder();
|
StringBuilder searchRequest = new StringBuilder();
|
||||||
searchRequest.append("Select \"http://schemas.microsoft.com/exchange/permanenturl\" as permanenturl");
|
searchRequest.append("Select \"http://schemas.microsoft.com/exchange/permanenturl\" as permanenturl");
|
||||||
if (attributes != null) {
|
if (attributes != null) {
|
||||||
@ -623,16 +784,73 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
// TODO order by ImapUid
|
// TODO order by ImapUid
|
||||||
//searchRequest.append(" ORDER BY \"urn:schemas:httpmail:date\" ASC");
|
//searchRequest.append(" ORDER BY \"urn:schemas:httpmail:date\" ASC");
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_SEARCH_QUERY", searchRequest));
|
DavGatewayTray.debug(new BundleMessage("LOG_SEARCH_QUERY", searchRequest));
|
||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
|
return DavGatewayHttpClientFacade.executeSearchMethod(
|
||||||
httpClient, URIUtil.encodePath(folderUrl), searchRequest.toString());
|
httpClient, URIUtil.encodePath(folderUrl), searchRequest.toString());
|
||||||
|
|
||||||
for (MultiStatusResponse response : responses) {
|
|
||||||
Message message = buildMessage(response);
|
|
||||||
message.messageList = messages;
|
|
||||||
messages.add(message);
|
|
||||||
}
|
}
|
||||||
Collections.sort(messages);
|
|
||||||
return messages;
|
|
||||||
|
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES = new DavPropertyNameSet();
|
||||||
|
|
||||||
|
static {
|
||||||
|
EVENT_REQUEST_PROPERTIES.add(Field.get("permanenturl").davPropertyName);
|
||||||
|
EVENT_REQUEST_PROPERTIES.add(Field.get("etag").davPropertyName);
|
||||||
|
EVENT_REQUEST_PROPERTIES.add(Field.get("contentclass").davPropertyName);
|
||||||
|
EVENT_REQUEST_PROPERTIES.add(Field.get("displayname").davPropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getItem(String folderPath, String itemName) throws IOException {
|
||||||
|
String itemPath = folderPath + '/' + convertItemNameToEML(itemName);
|
||||||
|
Item item;
|
||||||
|
try {
|
||||||
|
item = getItem(itemPath);
|
||||||
|
} catch (HttpNotFoundException hnfe) {
|
||||||
|
// failover for Exchange 2007 plus encoding issue
|
||||||
|
String decodedEventName = convertItemNameToEML(itemName).replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
|
||||||
|
LOGGER.debug("Item not found at " + itemPath + ", search by displayname: '" + decodedEventName + '\'');
|
||||||
|
ExchangeSession.MessageList messages = searchMessages(folderPath, equals("displayname", decodedEventName));
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
item = getItem(messages.get(0).getPermanentUrl());
|
||||||
|
} else {
|
||||||
|
throw hnfe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get item by url
|
||||||
|
*
|
||||||
|
* @param itemPath Event path
|
||||||
|
* @return event object
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
public Item getItem(String itemPath) throws IOException {
|
||||||
|
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES);
|
||||||
|
if (responses.length == 0) {
|
||||||
|
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
||||||
|
}
|
||||||
|
String contentClass = getPropertyIfExists(responses[0].getProperties(HttpStatus.SC_OK),
|
||||||
|
"contentclass", DAV);
|
||||||
|
if ("urn:content-classes:person".equals(contentClass)) {
|
||||||
|
return new Contact(responses[0]);
|
||||||
|
} else if ("urn:content-classes:appointment".equals(contentClass)
|
||||||
|
|| "urn:content-classes:calendarmessage".equals(contentClass)) {
|
||||||
|
return new Event(responses[0]);
|
||||||
|
} else {
|
||||||
|
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemResult internalCreateOrUpdateEvent(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
||||||
|
Event event = new Event(messageUrl, contentClass, icsBody, etag, noneMatch);
|
||||||
|
return event.createOrUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ItemResult internalCreateOrUpdateContact(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
||||||
|
Contact contact = new Contact(messageUrl, contentClass, icsBody, etag, noneMatch);
|
||||||
|
return contact.createOrUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<DavConstants> buildProperties(Map<String, String> properties) {
|
protected List<DavConstants> buildProperties(Map<String, String> properties) {
|
||||||
@ -678,6 +896,7 @@ public class DavExchangeSession extends ExchangeSession {
|
|||||||
* @param messageBody mail body
|
* @param messageBody mail body
|
||||||
* @throws IOException when unable to create message
|
* @throws IOException when unable to create message
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, String messageBody) throws IOException {
|
public void createMessage(String folderPath, String messageName, HashMap<String, String> properties, String messageBody) throws IOException {
|
||||||
String messageUrl = URIUtil.encodePathQuery(getFolderPath(folderPath) + '/' + messageName + ".EML");
|
String messageUrl = URIUtil.encodePathQuery(getFolderPath(folderPath) + '/' + messageName + ".EML");
|
||||||
PropPatchMethod patchMethod;
|
PropPatchMethod patchMethod;
|
||||||
|
@ -54,6 +54,7 @@ public class Field {
|
|||||||
protected static final Namespace SCHEMAS_MAPI_STRING = Namespace.getNamespace("http://schemas.microsoft.com/mapi/string/");
|
protected static final Namespace SCHEMAS_MAPI_STRING = Namespace.getNamespace("http://schemas.microsoft.com/mapi/string/");
|
||||||
protected static final Namespace SCHEMAS_REPL = Namespace.getNamespace("http://schemas.microsoft.com/repl/");
|
protected static final Namespace SCHEMAS_REPL = Namespace.getNamespace("http://schemas.microsoft.com/repl/");
|
||||||
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
|
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
|
||||||
|
protected static final Namespace URN_SCHEMAS_CALENDAR = Namespace.getNamespace("urn:schemas:calendar:");
|
||||||
|
|
||||||
protected static final Namespace SCHEMAS_MAPI_STRING_INTERNET_HEADERS =
|
protected static final Namespace SCHEMAS_MAPI_STRING_INTERNET_HEADERS =
|
||||||
Namespace.getNamespace(SCHEMAS_MAPI_STRING.getURI() +
|
Namespace.getNamespace(SCHEMAS_MAPI_STRING.getURI() +
|
||||||
@ -74,6 +75,7 @@ public class Field {
|
|||||||
propertyTypeMap.put(PropertyType.Boolean, "000b");
|
propertyTypeMap.put(PropertyType.Boolean, "000b");
|
||||||
propertyTypeMap.put(PropertyType.SystemTime, "0040");
|
propertyTypeMap.put(PropertyType.SystemTime, "0040");
|
||||||
propertyTypeMap.put(PropertyType.String, "001f");
|
propertyTypeMap.put(PropertyType.String, "001f");
|
||||||
|
propertyTypeMap.put(PropertyType.Binary, "0102");
|
||||||
propertyTypeMap.put(PropertyType.Custom, "0030");
|
propertyTypeMap.put(PropertyType.Custom, "0030");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,9 +135,21 @@ public class Field {
|
|||||||
|
|
||||||
createField("lastmodified", 0x3008, PropertyType.SystemTime);//PR_LAST_MODIFICATION_TIME DAV:getlastmodified
|
createField("lastmodified", 0x3008, PropertyType.SystemTime);//PR_LAST_MODIFICATION_TIME DAV:getlastmodified
|
||||||
|
|
||||||
|
|
||||||
// failover search
|
// failover search
|
||||||
createField(DAV, "displayname");
|
createField(DAV, "displayname");
|
||||||
|
|
||||||
|
// items
|
||||||
|
createField("etag", DAV, "getetag");
|
||||||
|
|
||||||
|
// calendar
|
||||||
|
createField(SCHEMAS_EXCHANGE, "permanenturl");
|
||||||
|
createField(URN_SCHEMAS_CALENDAR, "instancetype");
|
||||||
|
createField(URN_SCHEMAS_CALENDAR, "dtstart");
|
||||||
|
createField(SCHEMAS_EXCHANGE, "sensitivity");
|
||||||
|
createField("processed", 0x65e8, PropertyType.Boolean);//PR_MESSAGE_PROCESSED
|
||||||
|
|
||||||
|
createField(DAV, "contentclass");
|
||||||
|
createField("internetContent", 0x6659, PropertyType.Binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void createField(String alias, int propertyTag, PropertyType propertyType) {
|
protected static void createField(String alias, int propertyTag, PropertyType propertyType) {
|
||||||
|
@ -278,6 +278,7 @@ public class EwsExchangeSession extends ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static final HashSet<FieldURI> FOLDER_PROPERTIES = new HashSet<FieldURI>();
|
protected static final HashSet<FieldURI> FOLDER_PROPERTIES = new HashSet<FieldURI>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
FOLDER_PROPERTIES.add(ExtendedFieldURI.PR_URL_COMP_NAME);
|
FOLDER_PROPERTIES.add(ExtendedFieldURI.PR_URL_COMP_NAME);
|
||||||
FOLDER_PROPERTIES.add(ExtendedFieldURI.PR_LAST_MODIFICATION_TIME);
|
FOLDER_PROPERTIES.add(ExtendedFieldURI.PR_LAST_MODIFICATION_TIME);
|
||||||
@ -395,6 +396,31 @@ public class EwsExchangeSession extends ExchangeSession {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Contact> searchContacts(String folderName, List<String> attributes, Condition condition) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Event> searchEvents(String folderPath, List<String> attributes, Condition condition) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Item getItem(String folderPath, String itemName) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ItemResult internalCreateOrUpdateContact(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ItemResult internalCreateOrUpdateEvent(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private FolderId getFolderId(String folderPath) throws IOException {
|
private FolderId getFolderId(String folderPath) throws IOException {
|
||||||
String[] folderNames;
|
String[] folderNames;
|
||||||
|
Loading…
Reference in New Issue
Block a user