diff --git a/src/java/davmail/caldav/CaldavConnection.java b/src/java/davmail/caldav/CaldavConnection.java
index 259e4e90..e45c7e64 100644
--- a/src/java/davmail/caldav/CaldavConnection.java
+++ b/src/java/davmail/caldav/CaldavConnection.java
@@ -208,10 +208,10 @@ public class CaldavConnection extends AbstractConnection {
if (request.isPropFind() && request.isPathLength(3)) {
sendUserRoot(request);
} else {
- handleCalendar(request);
+ handleFolder(request);
}
} else if (request.isPath(1, "public")) {
- handleCalendar(request);
+ handleFolder(request);
} else {
sendUnsupported(request);
}
@@ -238,7 +238,7 @@ public class CaldavConnection extends AbstractConnection {
}
}
- protected void handleCalendar(CaldavRequest request) throws IOException {
+ protected void handleFolder(CaldavRequest request) throws IOException {
String lastPath = xmlDecodeName(request.getLastPath());
// folder requests
if (request.isPropFind() && "inbox".equals(lastPath)) {
@@ -254,7 +254,7 @@ public class CaldavConnection extends AbstractConnection {
sendHttpResponse(status);
}
} else if (request.isPropFind()) {
- sendCalendar(request);
+ sendFolder(request);
} else if (request.isPropPatch()) {
patchCalendar(request);
} else if (request.isReport()) {
@@ -359,20 +359,29 @@ public class CaldavConnection extends AbstractConnection {
}
/**
- * Append calendar object to Caldav response.
+ * Append folder object to Caldav response.
*
* @param response Caldav response
* @param request Caldav request
* @param subFolder calendar folder path relative to request path
* @throws IOException on error
*/
- public void appendCalendar(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
+ public void appendFolder(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
+ ExchangeSession.Folder folder = session.getFolder(request.getExchangeFolderPath(subFolder));
+
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
response.startPropstat();
if (request.hasProperty("resourcetype")) {
- response.appendProperty("D:resourcetype", "" +
- "");
+ if (folder.isContact()) {
+ response.appendProperty("D:resourcetype", "CD=\"urn:ietf:params:xml:ns:carddav\"", "" +
+ "");
+ } else if (folder.isCalendar()) {
+ response.appendProperty("D:resourcetype", "" + "");
+ } else {
+ response.appendProperty("D:resourcetype", "");
+ }
+
}
if (request.hasProperty("owner")) {
if ("users".equals(request.getPathElement(1))) {
@@ -382,14 +391,18 @@ public class CaldavConnection extends AbstractConnection {
}
}
if (request.hasProperty("getcontenttype")) {
- response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
+ if (folder.isContact()) {
+ response.appendProperty("D:getcontenttype", "text/x-vcard");
+ } else if (folder.isCalendar()) {
+ response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
+ }
}
if (request.hasProperty("getetag")) {
- response.appendProperty("D:getetag", session.getFolderResourceTag(request.getExchangeFolderPath(subFolder)));
+ response.appendProperty("D:getetag", folder.etag);
}
if (request.hasProperty("getctag")) {
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
- base64Encode(session.getFolderCtag(request.getExchangeFolderPath(subFolder))));
+ base64Encode(folder.ctag));
}
if (request.hasProperty("displayname")) {
if (subFolder == null || subFolder.length() == 0) {
@@ -401,7 +414,7 @@ public class CaldavConnection extends AbstractConnection {
if (request.hasProperty("calendar-description")) {
response.appendProperty("C:calendar-description", "");
}
- if (request.hasProperty("supported-calendar-component-set")) {
+ if (request.hasProperty("supported-calendar-component-set") && folder.isCalendar()) {
response.appendProperty("C:supported-calendar-component-set", "");
}
@@ -507,7 +520,7 @@ public class CaldavConnection extends AbstractConnection {
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus();
appendInbox(response, request, null);
- // do not try to access inbox on shared calendar
+ // do not try to access inbox on shared calendar
if (!session.isSharedFolder(request.getExchangeFolderPath(null)) && request.getDepth() == 1 && !Settings.getBooleanProperty("davmail.caldavDisableInbox")) {
try {
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_MESSAGES"));
@@ -543,11 +556,11 @@ public class CaldavConnection extends AbstractConnection {
* @param request Caldav request
* @throws IOException on error
*/
- public void sendCalendar(CaldavRequest request) throws IOException {
+ public void sendFolder(CaldavRequest request) throws IOException {
String folderPath = request.getExchangeFolderPath();
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus();
- appendCalendar(response, request, null);
+ appendFolder(response, request, null);
if (request.getDepth() == 1) {
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_EVENTS", folderPath));
List events = session.getAllEvents(folderPath);
@@ -557,7 +570,7 @@ public class CaldavConnection extends AbstractConnection {
if (!folderPath.startsWith("/public")) {
List folderList = session.getSubCalendarFolders(folderPath, false);
for (ExchangeSession.Folder folder : folderList) {
- appendCalendar(response, request, folder.folderPath.substring(folder.folderPath.indexOf('/') + 1));
+ appendFolder(response, request, folder.folderPath.substring(folder.folderPath.indexOf('/') + 1));
}
}
}
@@ -670,7 +683,7 @@ public class CaldavConnection extends AbstractConnection {
if (request.getDepth() == 1) {
appendInbox(response, request, "inbox");
appendOutbox(response, request, "outbox");
- appendCalendar(response, request, "calendar");
+ appendFolder(response, request, "calendar");
}
response.endResponse();
response.endMultistatus();
@@ -1003,7 +1016,7 @@ public class CaldavConnection extends AbstractConnection {
if (status != HttpStatus.SC_UNAUTHORIZED) {
String version = DavGateway.getCurrentVersion();
sendClient("Server: DavMail Gateway " + (version == null ? "" : version));
- sendClient("DAV: 1, calendar-access, calendar-schedule, calendarserver-private-events");
+ sendClient("DAV: 1, calendar-access, calendar-schedule, calendarserver-private-events, addressbook");
SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
// force GMT timezone
formatter.setTimeZone(ExchangeSession.GMT_TIMEZONE);
diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java
index 1f72f7f8..aca3f475 100644
--- a/src/java/davmail/exchange/ExchangeSession.java
+++ b/src/java/davmail/exchange/ExchangeSession.java
@@ -133,10 +133,12 @@ public class ExchangeSession {
protected static final DavPropertyNameSet FOLDER_PROPERTIES = new DavPropertyNameSet();
static {
+ FOLDER_PROPERTIES.add(DavPropertyName.create("contentclass", Namespace.getNamespace("DAV:")));
FOLDER_PROPERTIES.add(DavPropertyName.create("hassubs"));
FOLDER_PROPERTIES.add(DavPropertyName.create("nosubs"));
FOLDER_PROPERTIES.add(DavPropertyName.create("unreadcount", URN_SCHEMAS_HTTPMAIL));
FOLDER_PROPERTIES.add(DavPropertyName.create("contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/")));
+ FOLDER_PROPERTIES.add(DavPropertyName.create("resourcetag", Namespace.getNamespace("http://schemas.microsoft.com/repl/")));
}
protected static final DavPropertyNameSet CONTENT_TAG = new DavPropertyNameSet();
@@ -969,10 +971,12 @@ public class ExchangeSession {
String href = URIUtil.decode(entity.getHref());
Folder folder = new Folder();
DavPropertySet properties = entity.getProperties(HttpStatus.SC_OK);
+ folder.contentClass = getPropertyIfExists(properties, "contentclass", Namespace.getNamespace("DAV:"));
folder.hasChildren = "1".equals(getPropertyIfExists(properties, "hassubs", Namespace.getNamespace("DAV:")));
folder.noInferiors = "1".equals(getPropertyIfExists(properties, "nosubs", Namespace.getNamespace("DAV:")));
folder.unreadCount = getIntPropertyIfExists(properties, "unreadcount", URN_SCHEMAS_HTTPMAIL);
- folder.contenttag = getPropertyIfExists(properties, "contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/"));
+ folder.ctag = getPropertyIfExists(properties, "contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/"));
+ folder.etag = getPropertyIfExists(properties, "resourcetag", Namespace.getNamespace("http://schemas.microsoft.com/repl/"));
// replace well known folder names
if (href.startsWith(inboxUrl)) {
@@ -1178,15 +1182,15 @@ public class ExchangeSession {
*/
public boolean refreshFolder(Folder currentFolder) throws IOException {
Folder newFolder = getFolder(currentFolder.folderName);
- if (currentFolder.contenttag == null || !currentFolder.contenttag.equals(newFolder.contenttag)) {
+ if (currentFolder.ctag == null || !currentFolder.ctag.equals(newFolder.ctag)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Contenttag changed on " + currentFolder.folderName + ' '
- + currentFolder.contenttag + " => " + newFolder.contenttag + ", reloading messages");
+ + currentFolder.ctag + " => " + newFolder.ctag + ", reloading messages");
}
currentFolder.hasChildren = newFolder.hasChildren;
currentFolder.noInferiors = newFolder.noInferiors;
currentFolder.unreadCount = newFolder.unreadCount;
- currentFolder.contenttag = newFolder.contenttag;
+ currentFolder.ctag = newFolder.ctag;
currentFolder.loadMessages();
return true;
} else {
@@ -1322,6 +1326,11 @@ public class ExchangeSession {
* Logical (IMAP) folder path.
*/
public String folderPath;
+
+ /**
+ * Folder content class.
+ */
+ public String contentClass;
/**
* Folder unread message count.
*/
@@ -1341,7 +1350,11 @@ public class ExchangeSession {
/**
* Folder content tag (to detect folder content changes).
*/
- public String contenttag;
+ public String ctag;
+ /**
+ * Folder etag (to detect folder object changes).
+ */
+ public String etag;
/**
* Folder message list, empty before loadMessages call.
*/
@@ -1441,6 +1454,22 @@ public class ExchangeSession {
public Message get(int index) {
return messages.get(index);
}
+
+ /**
+ * Calendar folder flag.
+ * @return true if this is a calendar folder
+ */
+ public boolean isCalendar() {
+ return "urn:content-classes:calendarfolder".equals(contentClass);
+ }
+
+ /**
+ * Contact folder flag.
+ * @return true if this is a calendar folder
+ */
+ public boolean isContact() {
+ return "urn:content-classes:contactfolder".equals(contentClass);
+ }
}
/**