From 29101ee293fa437fed675c2cf38a0d8d992ca9e0 Mon Sep 17 00:00:00 2001 From: mguessan Date: Fri, 10 Apr 2009 17:05:54 +0000 Subject: [PATCH] Caldav: refactor CaldavConnection, prepare /public context git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@512 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- src/java/davmail/caldav/CaldavConnection.java | 303 ++++++++++++------ 1 file changed, 202 insertions(+), 101 deletions(-) diff --git a/src/java/davmail/caldav/CaldavConnection.java b/src/java/davmail/caldav/CaldavConnection.java index 72e2c54f..07ce8e13 100644 --- a/src/java/davmail/caldav/CaldavConnection.java +++ b/src/java/davmail/caldav/CaldavConnection.java @@ -8,7 +8,6 @@ import davmail.exchange.ICSBufferedReader; import davmail.ui.tray.DavGatewayTray; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.auth.AuthenticationException; import org.apache.commons.httpclient.util.URIUtil; import org.apache.log4j.Logger; @@ -165,88 +164,79 @@ public class CaldavConnection extends AbstractConnection { DavGatewayTray.resetIcon(); } - protected int getDepth(Map headers) { - int result = 0; - String depthValue = headers.get("depth"); - if (depthValue != null) { - try { - result = Integer.valueOf(depthValue); - } catch (NumberFormatException e) { - DavGatewayTray.warn("Invalid depth value: " + depthValue); - } - } - return result; - } - - protected boolean isIcal(Map headers) { - String userAgent = headers.get("user-agent"); - return userAgent != null && userAgent.indexOf("DAVKit")>=0; - } - public void handleRequest(String command, String path, Map headers, String body) throws IOException { - int depth = getDepth(headers); - String[] paths = path.replaceAll("//", "/").split("/"); - + CaldavRequest request = new CaldavRequest(command, path, headers, body); // full debug trace if (wireLogger.isDebugEnabled()) { - wireLogger.debug("Caldav command: " + command + " " + path + " depth: " + depth + "\n" + body); + wireLogger.debug("Caldav command: " + request.toString()); } - - CaldavRequest request = null; - if ("PROPFIND".equals(command) || "REPORT".equals(command)) { - request = new CaldavRequest(isIcal(headers), body); - } - if ("OPTIONS".equals(command)) { + if (request.isOptions()) { sendOptions(); - // redirect PROPFIND on / to current user principal - } else if ("PROPFIND".equals(command) && (paths.length == 0 || paths.length == 1)) { + } else if (request.isPropFind() && request.isRoot()) { sendRoot(request); - } else if ("GET".equals(command) && (paths.length == 0 || paths.length == 1)) { + } else if (request.isGet() && request.isRoot()) { sendGetRoot(); - // return current user calendar - } else if ("calendar".equals(paths[1])) { + // deprecated url, will be removed + } else if (request.isPath(1, "calendar")) { StringBuilder message = new StringBuilder(); message.append("/calendar no longer supported, recreate calendar with /users/") .append(session.getEmail()).append("/calendar"); DavGatewayTray.error(message.toString()); sendErr(HttpStatus.SC_BAD_REQUEST, message.toString()); - } else if ("user".equals(paths[1])) { - sendRedirect(headers, "/principals/users/" + session.getEmail()); - // principal namespace - } else if ("PROPFIND".equals(command) && "principals".equals(paths[1]) && paths.length == 4 && - "users".equals(paths[2])) { - sendPrincipal(request, paths[3]); + // /principals/users namespace + } else if (request.isPath(1, "principals") && request.isPath(2, "users")) { + handleUserPrincipals(request); + // users root + } else if (request.isPath(1, "users") && request.getPathLength() >= 3) { + handleUsers(request); + } else { + sendUnsupported(request); + } + } + + protected void handleUserPrincipals(CaldavRequest request) throws IOException { + if (request.isPropFind() && request.isPathLength(4)) { + sendPrincipal(request, request.getPathElement(3)); + } else if (request.isPropFind() && request.isPathLength(4)) { + sendPrincipal(request, request.getPathElement(3)); // send back principal on search - } else if ("REPORT".equals(command) && "principals".equals(paths[1]) && paths.length == 3 && - "users".equals(paths[2])) { + } else if (request.isReport() && request.isPathLength(3)) { sendPrincipal(request, session.getEmail()); - // user root - } else if ("PROPFIND".equals(command) && "users".equals(paths[1]) && paths.length == 3) { - sendUserRoot(request, depth, paths[2]); - } else if ("PROPFIND".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "inbox".equals(paths[3])) { - sendInbox(request, depth, paths[2]); - } else if ("REPORT".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "inbox".equals(paths[3])) { - reportEvents(request, paths[2], "INBOX"); - } else if ("PROPFIND".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "outbox".equals(paths[3])) { - sendOutbox(request, paths[2]); - } else if ("POST".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "outbox".equals(paths[3])) { - if (body.indexOf("VFREEBUSY") >= 0) { - sendFreeBusy(body); + } else { + sendUnsupported(request); + } + } + + protected void handleUsers(CaldavRequest request) throws IOException { + String principal = request.getPathElement(2); + String folderName = request.getPathElement(3); + if (request.isPropFind() && request.isPathLength(3)) { + sendUserRoot(request, principal); + } else if (request.isPropFind() && request.isPathLength(4) && "inbox".equals(folderName)) { + sendInbox(request, principal); + } else if (request.isReport() && request.isPathLength(4) && "inbox".equals(folderName)) { + reportEvents(request, principal, "INBOX"); + } else if (request.isPropFind() && request.isPathLength(4) && "outbox".equals(folderName)) { + sendOutbox(request, principal); + } else if (request.isPost() && request.isPathLength(4) && "outbox".equals(folderName)) { + if (request.isFreeBusy()) { + sendFreeBusy(request.getBody()); } else { - int status = session.sendEvent(body); + int status = session.sendEvent(request.getBody()); sendHttpResponse(status); } - } else if ("PROPFIND".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "calendar".equals(paths[3])) { - sendCalendar(request, depth, paths[2]); - } else if ("PROPPATCH".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "calendar".equals(paths[3])) { + } else if (request.isPropFind() && request.isPathLength(4) && "calendar".equals(folderName)) { + sendCalendar(request, principal); + } else if (request.isPropPatch() && request.isPathLength(4) && "calendar".equals(folderName)) { patchCalendar(); - } else if ("REPORT".equals(command) && "users".equals(paths[1]) && paths.length == 4 && "calendar".equals(paths[3])) { - reportEvents(request, paths[2], "calendar"); + } else if (request.isReport() && request.isPathLength(4) && "calendar".equals(folderName)) { + reportEvents(request, principal, "calendar"); - } else if ("PUT".equals(command) && "users".equals(paths[1]) && paths.length == 5 && "calendar".equals(paths[3])) { - String etag = headers.get("if-match"); - String noneMatch = headers.get("if-none-match"); - ExchangeSession.EventResult eventResult = session.createOrUpdateEvent(paths[2], xmlDecodeName(URIUtil.decode(paths[4])), body, etag, noneMatch); + } else if (request.isPut() && request.isPathLength(5) && "calendar".equals(folderName)) { + String eventName = xmlDecodeName(URIUtil.decode(request.getPathElement(4))); + String etag = request.getHeader("if-match"); + String noneMatch = request.getHeader("if-none-match"); + ExchangeSession.EventResult eventResult = session.createOrUpdateEvent(principal, eventName, request.getBody(), etag, noneMatch); if (eventResult.etag != null) { HashMap responseHeaders = new HashMap(); responseHeaders.put("ETag", eventResult.etag); @@ -255,24 +245,19 @@ public class CaldavConnection extends AbstractConnection { sendHttpResponse(eventResult.status); } - } else if ("DELETE".equals(command) && "users".equals(paths[1]) && paths.length == 5) { - if ("inbox".equals(paths[3])) { - paths[3] = "INBOX"; + } else if (request.isDelete() && request.isPathLength(5)) { + String eventName = xmlDecodeName(URIUtil.decode(request.getPathElement(4))); + if ("inbox".equals(folderName)) { + folderName = "INBOX"; } - int status = session.deleteEvent(paths[2], paths[3], xmlDecodeName(URIUtil.decode(paths[4]))); + int status = session.deleteEvent(principal, folderName, eventName); sendHttpResponse(status); - } else if ("GET".equals(command) && "users".equals(paths[1]) && paths.length == 5 && "calendar".equals(paths[3]) - // only current user for now - && session.getEmail().equalsIgnoreCase(paths[2])) { - ExchangeSession.Event event = session.getEvent(paths[2], paths[3], xmlDecodeName(URIUtil.decode(paths[4]))); + } else if (request.isGet() && request.isPathLength(5) && "calendar".equals(folderName)) { + String eventName = xmlDecodeName(URIUtil.decode(request.getPathElement(4))); + ExchangeSession.Event event = session.getEvent(principal, folderName, eventName); sendHttpResponse(HttpStatus.SC_OK, null, "text/calendar;charset=UTF-8", event.getICS(), true); - } else { - StringBuilder message = new StringBuilder(); - message.append("Unsupported request: ").append(command).append(" ").append(path); - message.append(" Depth: ").append(depth).append("\n").append(body); - DavGatewayTray.error(message.toString()); - sendErr(HttpStatus.SC_BAD_REQUEST, message.toString()); + sendUnsupported(request); } } @@ -386,11 +371,11 @@ public class CaldavConnection extends AbstractConnection { sendHttpResponse(HttpStatus.SC_OK, null, "text/html;charset=UTF-8", buffer.toString(), true); } - public void sendInbox(CaldavRequest request, int depth, String principal) throws IOException { + public void sendInbox(CaldavRequest request, String principal) throws IOException { CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); response.startMultistatus(); appendInbox(response, principal, request); - if (depth == 1) { + if (request.getDepth() == 1) { DavGatewayTray.debug("Searching calendar messages..."); List events = session.getEventMessages(principal); DavGatewayTray.debug("Found " + events.size() + " calendar messages"); @@ -408,11 +393,11 @@ public class CaldavConnection extends AbstractConnection { response.close(); } - public void sendCalendar(CaldavRequest request, int depth, String principal) throws IOException { + public void sendCalendar(CaldavRequest request, String principal) throws IOException { CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); response.startMultistatus(); appendCalendar(response, principal, request); - if (depth == 1) { + if (request.getDepth() == 1) { DavGatewayTray.debug("Searching calendar events..."); List events = session.getAllEvents(principal); DavGatewayTray.debug("Found " + events.size() + " calendar events"); @@ -460,7 +445,7 @@ public class CaldavConnection extends AbstractConnection { appendEventResponse(response, request, path, session.getEvent(principal, path, eventName)); } } catch (HttpException e) { - DavGatewayTray.warn("Event not found:"+href); + DavGatewayTray.warn("Event not found:" + href); notFound.add(href); } } @@ -482,13 +467,12 @@ public class CaldavConnection extends AbstractConnection { response.close(); } - public void sendUserRoot(CaldavRequest request, int depth, String principal) throws IOException { + public void sendUserRoot(CaldavRequest request, String principal) throws IOException { CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS); response.startMultistatus(); response.startResponse("/users/" + principal); response.startPropstat(); - if (request.hasProperty("resourcetype")) { response.appendProperty("D:resourcetype", ""); } @@ -496,7 +480,7 @@ public class CaldavConnection extends AbstractConnection { response.appendProperty("D:displayname", principal); } response.endPropStatOK(); - if (depth == 1) { + if (request.getDepth() == 1) { appendInbox(response, principal, request); appendOutbox(response, principal, request); appendCalendar(response, principal, request); @@ -657,6 +641,13 @@ public class CaldavConnection extends AbstractConnection { sendErr(HttpStatus.SC_SERVICE_UNAVAILABLE, message); } + public void sendUnsupported(CaldavRequest request) throws IOException { + StringBuilder message = new StringBuilder(); + message.append("Unsupported request: ").append(request.toString()); + DavGatewayTray.error(message.toString()); + sendErr(HttpStatus.SC_BAD_REQUEST, message.toString()); + } + public void sendErr(int status, String message) throws IOException { sendHttpResponse(status, null, "text/plain;charset=UTF-8", message, false); } @@ -791,12 +782,125 @@ public class CaldavConnection extends AbstractConnection { } protected static class CaldavRequest { + protected String command; + protected String path; + protected String[] pathElements; + protected Map headers; + protected int depth; + protected String body; protected final HashSet properties = new HashSet(); protected HashSet hrefs; protected boolean isMultiGet; - public CaldavRequest(boolean isIcal, String body) throws IOException { - // parse body + public CaldavRequest(String command, String path, Map headers, String body) throws IOException { + this.command = command; + this.path = path.replaceAll("//", "/"); + pathElements = this.path.split("/"); + this.headers = headers; + buildDepth(); + this.body = body; + + if (isPropFind() || isReport()) { + parseXmlBody(); + } + } + + public boolean isOptions() { + return "OPTIONS".equals(command); + } + + public boolean isPropFind() { + return "PROPFIND".equals(command); + } + + public boolean isPropPatch() { + return "PROPFIND".equals(command); + } + + public boolean isReport() { + return "REPORT".equals(command); + } + + public boolean isGet() { + return "GET".equals(command); + } + + public boolean isPut() { + return "PUT".equals(command); + } + + public boolean isPost() { + return "POST".equals(command); + } + + public boolean isDelete() { + return "DELETE".equals(command); + } + + public boolean isRoot() { + return (pathElements.length == 0 || pathElements.length == 1); + } + + public boolean isPathLength(int length) { + return pathElements.length == length; + } + + public int getPathLength() { + return pathElements.length; + } + + /** + * Check if path element at index is value + * + * @param index path element index + * @param value path value + * @return true if path element at index is value + */ + public boolean isPath(int index, String value) { + return value != null && value.equals(getPathElement(index)); + } + + protected String getPathElement(int index) { + if (index < pathElements.length) { + return pathElements[index]; + } else { + return null; + } + } + + protected boolean isIcal() { + String userAgent = headers.get("user-agent"); + return userAgent != null && userAgent.indexOf("DAVKit") >= 0; + } + + public boolean isFreeBusy() { + return body != null && body.indexOf("VFREEBUSY") >= 0; + } + + protected void buildDepth() { + String depthValue = headers.get("depth"); + if (depthValue != null) { + try { + depth = Integer.valueOf(depthValue); + } catch (NumberFormatException e) { + DavGatewayTray.warn("Invalid depth value: " + depthValue); + } + } + } + + public int getDepth() { + return depth; + } + + public String getBody() { + return body; + } + + public String getHeader(String headerName) { + return headers.get(headerName); + } + + protected void parseXmlBody() throws IOException { XMLStreamReader streamReader = null; try { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); @@ -828,7 +932,7 @@ public class CaldavConnection extends AbstractConnection { if (hrefs == null) { hrefs = new HashSet(); } - if (isIcal) { + if (isIcal()) { hrefs.add(streamReader.getText()); } else { hrefs.add(URIUtil.decode(streamReader.getText())); @@ -861,6 +965,11 @@ public class CaldavConnection extends AbstractConnection { public Set getHrefs() { return hrefs; } + + @Override + public String toString() { + return command + " " + path + " Depth: " + depth + "\n" + body; + } } protected class CaldavResponse { @@ -946,15 +1055,11 @@ public class CaldavConnection extends AbstractConnection { } public void endPropStatOK() throws IOException { - writer.write(""); - writer.write("HTTP/1.1 200 OK"); - writer.write(""); + writer.write("HTTP/1.1 200 OK"); } public void appendPropstatNotFound() throws IOException { - writer.write(""); - writer.write("HTTP/1.1 404 Not Found"); - writer.write(""); + writer.write("HTTP/1.1 404 Not Found"); } public void endResponse() throws IOException { @@ -970,13 +1075,9 @@ public class CaldavConnection extends AbstractConnection { } public void startRecipientResponse(String recipient) throws IOException { - writer.write(""); - writer.write(""); - writer.write(""); + writer.write(""); writer.write(recipient); - writer.write(""); - writer.write(""); - writer.write("2.0;Success"); + writer.write("2.0;Success"); } public void endRecipientResponse() throws IOException {