Caldav: implement public shared calendar

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@522 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2009-04-15 22:05:11 +00:00
parent a16df127b8
commit 59b19fee2b
2 changed files with 110 additions and 80 deletions

View File

@ -170,8 +170,8 @@ public class CaldavConnection extends AbstractConnection {
sendRoot(request); sendRoot(request);
} else if (request.isGet() && request.isRoot()) { } else if (request.isGet() && request.isRoot()) {
sendGetRoot(); sendGetRoot();
} else if (request.isPath(1, "principals") && request.isPath(2, "users")) { } else if (request.isPath(1, "principals")) {
handleUserPrincipals(request); handlePrincipals(request);
} else if (request.isPath(1, "users")) { } else if (request.isPath(1, "users")) {
if (request.isPropFind() && request.isPathLength(3)) { if (request.isPropFind() && request.isPathLength(3)) {
sendUserRoot(request); sendUserRoot(request);
@ -185,12 +185,18 @@ public class CaldavConnection extends AbstractConnection {
} }
} }
protected void handleUserPrincipals(CaldavRequest request) throws IOException { protected void handlePrincipals(CaldavRequest request) throws IOException {
if (request.isPropFind() && request.isPathLength(4)) { if (request.isPath(2, "users")) {
sendPrincipal(request, request.getPathElement(3)); if (request.isPropFind() && request.isPathLength(4)) {
// send back principal on search sendPrincipal(request, "users", request.getPathElement(3));
} else if (request.isReport() && request.isPathLength(3)) { // send back principal on search
sendPrincipal(request, session.getEmail()); } else if (request.isReport() && request.isPathLength(3)) {
sendPrincipal(request, "users", session.getEmail());
} else {
sendUnsupported(request);
}
} else if (request.isPath(2, "public") && request.isPathLength(4)) {
sendPrincipal(request, "public", request.getPathElement(3));
} else { } else {
sendUnsupported(request); sendUnsupported(request);
} }
@ -224,7 +230,7 @@ public class CaldavConnection extends AbstractConnection {
if (request.isPut()) { if (request.isPut()) {
String etag = request.getHeader("if-match"); String etag = request.getHeader("if-match");
String noneMatch = request.getHeader("if-none-match"); String noneMatch = request.getHeader("if-none-match");
ExchangeSession.EventResult eventResult = session.createOrUpdateEvent(request.getFolderPath(), eventName, request.getBody(), etag, noneMatch); ExchangeSession.EventResult eventResult = session.createOrUpdateEvent(request.getExchangeFolderPath(), eventName, request.getBody(), etag, noneMatch);
if (eventResult.etag != null) { if (eventResult.etag != null) {
HashMap<String, String> responseHeaders = new HashMap<String, String>(); HashMap<String, String> responseHeaders = new HashMap<String, String>();
responseHeaders.put("ETag", eventResult.etag); responseHeaders.put("ETag", eventResult.etag);
@ -234,10 +240,10 @@ public class CaldavConnection extends AbstractConnection {
} }
} else if (request.isDelete()) { } else if (request.isDelete()) {
int status = session.deleteEvent(request.getFolderPath(), eventName); int status = session.deleteEvent(request.getExchangeFolderPath(), eventName);
sendHttpResponse(status); sendHttpResponse(status);
} else if (request.isGet()) { } else if (request.isGet()) {
ExchangeSession.Event event = session.getEvent(request.getFolderPath(), eventName); ExchangeSession.Event event = session.getEvent(request.getExchangeFolderPath(), eventName);
sendHttpResponse(HttpStatus.SC_OK, null, "text/calendar;charset=UTF-8", event.getICS(), true); sendHttpResponse(HttpStatus.SC_OK, null, "text/calendar;charset=UTF-8", event.getICS(), true);
} else { } else {
sendUnsupported(request); sendUnsupported(request);
@ -281,12 +287,8 @@ public class CaldavConnection extends AbstractConnection {
response.endResponse(); response.endResponse();
} }
public void appendCalendar(CaldavResponse response, CaldavRequest request) throws IOException { public void appendCalendar(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
if (request.isLastPath("calendar")) { response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
response.startResponse(URIUtil.encodePath(request.getPath()));
} else {
response.startResponse(URIUtil.encodePath(request.getPath()) + "/calendar");
}
response.startPropstat(); response.startPropstat();
if (request.hasProperty("resourcetype")) { if (request.hasProperty("resourcetype")) {
@ -304,25 +306,25 @@ public class CaldavConnection extends AbstractConnection {
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent"); response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
} }
if (request.hasProperty("getetag")) { if (request.hasProperty("getetag")) {
response.appendProperty("D:getetag", session.getCalendarEtag()); response.appendProperty("D:getetag", session.getFolderResourceTag(request.getExchangeFolderPath(subFolder)));
} }
if (request.hasProperty("getctag")) { if (request.hasProperty("getctag")) {
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"", response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
base64Encode(session.getCalendarCtag())); base64Encode(session.getFolderCtag(request.getExchangeFolderPath(subFolder))));
} }
if (request.hasProperty("displayname")) { if (request.hasProperty("displayname")) {
response.appendProperty("D:displayname", request.getPathElement(request.getPathLength() - 1)); if (subFolder == null || subFolder.length() == 0) {
response.appendProperty("D:displayname", request.getLastPath());
} else {
response.appendProperty("D:displayname", subFolder);
}
} }
response.endPropStatOK(); response.endPropStatOK();
response.endResponse(); response.endResponse();
} }
public void appendInbox(CaldavResponse response, CaldavRequest request) throws IOException { public void appendInbox(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
if (request.isLastPath("inbox")) { response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
response.startResponse(URIUtil.encodePath(request.getPath()));
} else {
response.startResponse(URIUtil.encodePath(request.getPath()) + "/inbox");
}
response.startPropstat(); response.startPropstat();
if (request.hasProperty("resourcetype")) { if (request.hasProperty("resourcetype")) {
@ -334,7 +336,7 @@ public class CaldavConnection extends AbstractConnection {
} }
if (request.hasProperty("getctag")) { if (request.hasProperty("getctag")) {
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"", response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
base64Encode(session.getInboxCtag())); base64Encode(session.getFolderCtag(request.getExchangeFolderPath(subFolder))));
} }
if (request.hasProperty("displayname")) { if (request.hasProperty("displayname")) {
response.appendProperty("D:displayname", "inbox"); response.appendProperty("D:displayname", "inbox");
@ -343,12 +345,8 @@ public class CaldavConnection extends AbstractConnection {
response.endResponse(); response.endResponse();
} }
public void appendOutbox(CaldavResponse response, CaldavRequest request) throws IOException { public void appendOutbox(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
if (request.isLastPath("outbox")) { response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
response.startResponse(URIUtil.encodePath(request.getPath()));
} else {
response.startResponse(URIUtil.encodePath(request.getPath()) + "/outbox");
}
response.startPropstat(); response.startPropstat();
if (request.hasProperty("resourcetype")) { if (request.hasProperty("resourcetype")) {
@ -377,10 +375,10 @@ public class CaldavConnection extends AbstractConnection {
public void sendInbox(CaldavRequest request) throws IOException { public void sendInbox(CaldavRequest request) throws IOException {
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus(); response.startMultistatus();
appendInbox(response, request); appendInbox(response, request, null);
if (request.getDepth() == 1) { if (request.getDepth() == 1) {
DavGatewayTray.debug("Searching calendar messages..."); DavGatewayTray.debug("Searching calendar messages...");
List<ExchangeSession.Event> events = session.getEventMessages(request.getFolderPath()); List<ExchangeSession.Event> events = session.getEventMessages(request.getExchangeFolderPath());
DavGatewayTray.debug("Found " + events.size() + " calendar messages"); DavGatewayTray.debug("Found " + events.size() + " calendar messages");
appendEventsResponses(response, request, events); appendEventsResponses(response, request, events);
} }
@ -391,16 +389,16 @@ public class CaldavConnection extends AbstractConnection {
public void sendOutbox(CaldavRequest request) throws IOException { public void sendOutbox(CaldavRequest request) throws IOException {
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus(); response.startMultistatus();
appendOutbox(response, request); appendOutbox(response, request, null);
response.endMultistatus(); response.endMultistatus();
response.close(); response.close();
} }
public void sendCalendar(CaldavRequest request) throws IOException { public void sendCalendar(CaldavRequest request) throws IOException {
String folderPath = request.getFolderPath(); String folderPath = request.getExchangeFolderPath();
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus(); response.startMultistatus();
appendCalendar(response, request); appendCalendar(response, request, null);
if (request.getDepth() == 1) { if (request.getDepth() == 1) {
DavGatewayTray.debug("Searching calendar events at " + folderPath + " ..."); DavGatewayTray.debug("Searching calendar events at " + folderPath + " ...");
List<ExchangeSession.Event> events = session.getAllEvents(folderPath); List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
@ -429,7 +427,7 @@ public class CaldavConnection extends AbstractConnection {
} }
public void reportEvents(CaldavRequest request) throws IOException { public void reportEvents(CaldavRequest request) throws IOException {
String folderPath = request.getFolderPath(); String folderPath = request.getExchangeFolderPath();
List<ExchangeSession.Event> events; List<ExchangeSession.Event> events;
List<String> notFound = new ArrayList<String>(); List<String> notFound = new ArrayList<String>();
@ -456,10 +454,10 @@ public class CaldavConnection extends AbstractConnection {
} }
} }
} else if (request.isPath(1, "users") && request.isPath(3, "inbox")) { } else if (request.isPath(1, "users") && request.isPath(3, "inbox")) {
events = session.getEventMessages(request.getFolderPath()); events = session.getEventMessages(request.getExchangeFolderPath());
appendEventsResponses(response, request, events); appendEventsResponses(response, request, events);
} else { } else {
events = session.getAllEvents(request.getFolderPath()); events = session.getAllEvents(request.getExchangeFolderPath());
appendEventsResponses(response, request, events); appendEventsResponses(response, request, events);
} }
@ -483,13 +481,13 @@ public class CaldavConnection extends AbstractConnection {
response.appendProperty("D:resourcetype", "<D:collection/>"); response.appendProperty("D:resourcetype", "<D:collection/>");
} }
if (request.hasProperty("displayname")) { if (request.hasProperty("displayname")) {
response.appendProperty("D:displayname", request.getPathElement(request.getPathLength() - 1)); response.appendProperty("D:displayname", request.getLastPath());
} }
response.endPropStatOK(); response.endPropStatOK();
if (request.getDepth() == 1) { if (request.getDepth() == 1) {
appendInbox(response, request); appendInbox(response, request, "inbox");
appendOutbox(response, request); appendOutbox(response, request, "outbox");
appendCalendar(response, request); appendCalendar(response, request, "calendar");
} }
response.endResponse(); response.endResponse();
response.endMultistatus(); response.endMultistatus();
@ -514,28 +512,32 @@ public class CaldavConnection extends AbstractConnection {
response.close(); response.close();
} }
public void sendPrincipal(CaldavRequest request, String principal) throws IOException { public void sendPrincipal(CaldavRequest request, String prefix, String principal) throws IOException {
// actual principal is email address // actual principal is email address
String actualPrincipal = principal; String actualPrincipal = principal;
if (userName.equals(principal)) { if ("users".equals(prefix) && userName.equals(principal)) {
actualPrincipal = session.getEmail(); actualPrincipal = session.getEmail();
} }
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
response.startMultistatus(); response.startMultistatus();
response.startResponse("/principals/users/" + principal); response.startResponse(URIUtil.encodePath("/principals/" + prefix + "/" + principal));
response.startPropstat(); response.startPropstat();
if (request.hasProperty("calendar-home-set")) { if (request.hasProperty("calendar-home-set")) {
response.appendProperty("C:calendar-home-set", "<D:href>/users/" + actualPrincipal + "/calendar</D:href>"); if ("users".equals(prefix)) {
response.appendProperty("C:calendar-home-set", "<D:href>/users/" + actualPrincipal + "/calendar</D:href>");
} else {
response.appendProperty("C:calendar-home-set", "<D:href>" + prefix + "/" + actualPrincipal + "</D:href>");
}
} }
if (request.hasProperty("calendar-user-address-set")) { if (request.hasProperty("calendar-user-address-set") && "users".equals(prefix)) {
response.appendProperty("C:calendar-user-address-set", "<D:href>mailto:" + actualPrincipal + "</D:href>"); response.appendProperty("C:calendar-user-address-set", "<D:href>mailto:" + actualPrincipal + "</D:href>");
} }
// no inbox/outbox for delegated calendars // no inbox/outbox for delegated/shared calendars
if (session.getEmail().equals(principal)) { if ("users".equals(prefix) && session.getEmail().equals(principal)) {
if (request.hasProperty("schedule-inbox-URL")) { if (request.hasProperty("schedule-inbox-URL")) {
response.appendProperty("C:schedule-inbox-URL", "<D:href>/users/" + actualPrincipal + "/inbox</D:href>"); response.appendProperty("C:schedule-inbox-URL", "<D:href>/users/" + actualPrincipal + "/inbox</D:href>");
} }
@ -859,6 +861,14 @@ public class CaldavConnection extends AbstractConnection {
return path; return path;
} }
public String getPath(String subFolder) {
if (subFolder == null || subFolder.length() == 0) {
return path;
} else {
return path + "/" + subFolder;
}
}
/** /**
* Check if path element at index is value * Check if path element at index is value
* *
@ -882,6 +892,10 @@ public class CaldavConnection extends AbstractConnection {
} }
} }
public String getLastPath() {
return getPathElement(getPathLength() - 1);
}
protected boolean isIcal() { protected boolean isIcal() {
String userAgent = headers.get("user-agent"); String userAgent = headers.get("user-agent");
return userAgent != null && userAgent.indexOf("DAVKit") >= 0; return userAgent != null && userAgent.indexOf("DAVKit") >= 0;
@ -985,13 +999,34 @@ public class CaldavConnection extends AbstractConnection {
return command + " " + path + " Depth: " + depth + "\n" + body; return command + " " + path + " Depth: " + depth + "\n" + body;
} }
public String getFolderPath() throws IOException { /**
* Translate request path to Exchange folder path.
*
* @return exchange folder path
* @throws IOException on error
*/
public String getExchangeFolderPath() throws IOException {
if ("users".equals(getPathElement(1))) { if ("users".equals(getPathElement(1))) {
return session.buildCalendarPath(getPathElement(2), getPathElement(3)); return session.buildCalendarPath(getPathElement(2), getPathElement(3));
} else { } else {
return getPath(); return getPath();
} }
} }
/**
* Translate request path with subFolder to Exchange folder path.
*
* @param subFolder sub folder name
* @return exchange folder path
* @throws IOException on error
*/
public String getExchangeFolderPath(String subFolder) throws IOException {
if (subFolder == null || subFolder.length() == 0) {
return getExchangeFolderPath();
} else {
return session.buildCalendarPath(getPathElement(2), subFolder);
}
}
} }
protected class CaldavResponse { protected class CaldavResponse {

View File

@ -434,6 +434,15 @@ public class ExchangeSession {
} }
} }
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) { protected int getIntPropertyIfExists(DavPropertySet properties, String name, Namespace namespace) {
DavProperty property = properties.get(name, namespace); DavProperty property = properties.get(name, namespace);
if (property == null) { if (property == null) {
@ -1640,42 +1649,28 @@ public class ExchangeSession {
return status; return status;
} }
public String getInboxCtag() throws IOException { public String getFolderCtag(String folderPath) throws IOException {
return getFolderCtag(inboxUrl); return getFolderProperty(folderPath, CONTENT_TAG);
} }
public String getCalendarCtag() throws IOException { public String getFolderResourceTag(String folderPath) throws IOException {
return getFolderCtag(calendarUrl); return getFolderProperty(folderPath, RESOURCE_TAG);
} }
public String getFolderCtag(String folderUrl) throws IOException { public String getFolderProperty(String folderPath, DavPropertyNameSet davPropertyNameSet) throws IOException {
String ctag; String result;
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod( MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(
httpClient, URIUtil.encodePath(folderUrl), 0, CONTENT_TAG); httpClient, URIUtil.encodePath(folderPath), 0, davPropertyNameSet);
if (responses.length == 0) { if (responses.length == 0) {
throw new IOException("Unable to get folder object"); throw new IOException("Unable to get folder at "+folderPath);
} }
DavPropertySet properties = responses[0].getProperties(HttpStatus.SC_OK); DavPropertySet properties = responses[0].getProperties(HttpStatus.SC_OK);
ctag = getPropertyIfExists(properties, "contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/")); DavPropertyName davPropertyName = davPropertyNameSet.iterator().nextPropertyName();
if (ctag == null) { result = getPropertyIfExists(properties, davPropertyName);
throw new IOException("Unable to get calendar ctag"); if (result == null) {
throw new IOException("Unable to get property "+davPropertyName);
} }
return ctag; return result;
}
public String getCalendarEtag() throws IOException {
String etag;
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(
httpClient, URIUtil.encodePath(calendarUrl), 0, RESOURCE_TAG);
if (responses.length == 0) {
throw new IOException("Unable to get calendar object");
}
MultiStatusResponse calendarResponse = responses[0];
etag = getPropertyIfExists(calendarResponse.getProperties(HttpStatus.SC_OK), "resourcetag", Namespace.getNamespace("http://schemas.microsoft.com/repl/"));
if (etag == null) {
throw new IOException("Unable to get calendar etag");
}
return etag;
} }
/** /**