2009-07-21 04:39:18 -04:00
|
|
|
/*
|
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
|
|
|
* Copyright (C) 2009 Mickael Guessant
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
package davmail.caldav;
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
import davmail.AbstractConnection;
|
|
|
|
import davmail.BundleMessage;
|
2009-08-06 03:39:57 -04:00
|
|
|
import davmail.DavGateway;
|
2009-11-17 18:11:08 -05:00
|
|
|
import davmail.Settings;
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.exception.DavMailAuthenticationException;
|
2009-08-03 17:40:05 -04:00
|
|
|
import davmail.exception.DavMailException;
|
2008-11-26 19:56:28 -05:00
|
|
|
import davmail.exchange.ExchangeSession;
|
|
|
|
import davmail.exchange.ExchangeSessionFactory;
|
2009-02-09 05:12:09 -05:00
|
|
|
import davmail.exchange.ICSBufferedReader;
|
2009-04-03 03:38:31 -04:00
|
|
|
import davmail.ui.tray.DavGatewayTray;
|
2009-11-17 18:11:08 -05:00
|
|
|
import davmail.util.StringUtil;
|
2008-11-26 19:56:28 -05:00
|
|
|
import org.apache.commons.httpclient.HttpException;
|
|
|
|
import org.apache.commons.httpclient.HttpStatus;
|
2009-02-09 05:12:09 -05:00
|
|
|
import org.apache.commons.httpclient.util.URIUtil;
|
2008-12-26 07:35:08 -05:00
|
|
|
import org.apache.log4j.Logger;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2008-11-29 09:27:33 -05:00
|
|
|
import javax.xml.stream.XMLInputFactory;
|
|
|
|
import javax.xml.stream.XMLStreamConstants;
|
|
|
|
import javax.xml.stream.XMLStreamException;
|
|
|
|
import javax.xml.stream.XMLStreamReader;
|
2009-03-03 06:09:07 -05:00
|
|
|
import java.io.*;
|
2008-11-26 19:56:28 -05:00
|
|
|
import java.net.Socket;
|
2008-12-05 03:47:54 -05:00
|
|
|
import java.net.SocketException;
|
2008-12-17 10:22:37 -05:00
|
|
|
import java.net.SocketTimeoutException;
|
2008-11-26 19:56:28 -05:00
|
|
|
import java.text.SimpleDateFormat;
|
2008-11-29 09:27:33 -05:00
|
|
|
import java.util.*;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a caldav connection.
|
|
|
|
*/
|
|
|
|
public class CaldavConnection extends AbstractConnection {
|
2009-04-15 11:20:06 -04:00
|
|
|
/**
|
|
|
|
* Maximum keep alive time in seconds
|
|
|
|
*/
|
|
|
|
protected static final int MAX_KEEP_ALIVE_TIME = 300;
|
2009-02-24 06:53:02 -05:00
|
|
|
protected final Logger wireLogger = Logger.getLogger(this.getClass());
|
2008-12-26 07:35:08 -05:00
|
|
|
|
2009-04-16 18:20:30 -04:00
|
|
|
protected boolean closed;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2009-08-05 18:13:34 -04:00
|
|
|
/**
|
2009-08-03 17:40:05 -04:00
|
|
|
* Initialize the streams and start the thread.
|
|
|
|
*
|
|
|
|
* @param clientSocket Caldav client socket
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public CaldavConnection(Socket clientSocket) {
|
2009-05-04 08:27:38 -04:00
|
|
|
super(CaldavConnection.class.getSimpleName(), clientSocket, "UTF-8");
|
2009-08-05 18:13:34 -04:00
|
|
|
// set caldav logging to davmail logging level
|
|
|
|
wireLogger.setLevel(Settings.getLoggingLevel("davmail"));
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
protected Map<String, String> parseHeaders() throws IOException {
|
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
|
|
|
String line;
|
|
|
|
while ((line = readClient()) != null && line.length() > 0) {
|
|
|
|
int index = line.indexOf(':');
|
|
|
|
if (index <= 0) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_HEADER", line);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
headers.put(line.substring(0, index).toLowerCase(), line.substring(index + 1).trim());
|
|
|
|
}
|
|
|
|
return headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String getContent(String contentLength) throws IOException {
|
|
|
|
if (contentLength == null || contentLength.length() == 0) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
int size;
|
|
|
|
try {
|
|
|
|
size = Integer.parseInt(contentLength);
|
|
|
|
} catch (NumberFormatException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CONTENT_LENGTH", contentLength);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
char[] buffer = new char[size];
|
2009-02-11 20:33:49 -05:00
|
|
|
StringBuilder builder = new StringBuilder();
|
2009-02-12 05:10:23 -05:00
|
|
|
int actualSize = in.read(buffer);
|
|
|
|
builder.append(buffer, 0, actualSize);
|
|
|
|
if (actualSize < 0) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_END_OF_STREAM");
|
2009-02-12 05:10:23 -05:00
|
|
|
}
|
2009-02-11 20:33:49 -05:00
|
|
|
// dirty hack to ensure full content read
|
|
|
|
// TODO : replace with a dedicated reader
|
|
|
|
while (builder.toString().getBytes("UTF-8").length < size) {
|
|
|
|
actualSize = in.read(buffer);
|
|
|
|
builder.append(buffer, 0, actualSize);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
2009-02-12 05:10:23 -05:00
|
|
|
|
2009-06-29 16:14:08 -04:00
|
|
|
String content = builder.toString();
|
|
|
|
if (wireLogger.isDebugEnabled()) {
|
|
|
|
wireLogger.debug("< " + content);
|
|
|
|
}
|
|
|
|
return content;
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setSocketTimeout(String keepAliveValue) throws IOException {
|
2008-12-02 05:20:46 -05:00
|
|
|
if (keepAliveValue != null && keepAliveValue.length() > 0) {
|
2008-11-26 19:56:28 -05:00
|
|
|
int keepAlive;
|
|
|
|
try {
|
|
|
|
keepAlive = Integer.parseInt(keepAliveValue);
|
|
|
|
} catch (NumberFormatException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_KEEPALIVE", keepAliveValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
2009-04-15 11:20:06 -04:00
|
|
|
if (keepAlive > MAX_KEEP_ALIVE_TIME) {
|
|
|
|
keepAlive = MAX_KEEP_ALIVE_TIME;
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
client.setSoTimeout(keepAlive * 1000);
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_SET_SOCKET_TIMEOUT", keepAlive));
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-19 09:14:43 -04:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2008-11-26 19:56:28 -05:00
|
|
|
String line;
|
|
|
|
StringTokenizer tokens;
|
|
|
|
|
|
|
|
try {
|
|
|
|
while (!closed) {
|
|
|
|
line = readClient();
|
|
|
|
// unable to read line, connection closed ?
|
|
|
|
if (line == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tokens = new StringTokenizer(line);
|
2009-04-15 11:20:06 -04:00
|
|
|
String command = tokens.nextToken();
|
|
|
|
Map<String, String> headers = parseHeaders();
|
2009-08-06 08:15:52 -04:00
|
|
|
String encodedPath = encodePlusSign(tokens.nextToken());
|
2009-06-10 12:57:23 -04:00
|
|
|
String path = URIUtil.decode(encodedPath);
|
2009-04-15 11:20:06 -04:00
|
|
|
String content = getContent(headers.get("content-length"));
|
|
|
|
setSocketTimeout(headers.get("keep-alive"));
|
|
|
|
// client requested connection close
|
|
|
|
closed = "close".equals(headers.get("connection"));
|
|
|
|
if ("OPTIONS".equals(command)) {
|
|
|
|
sendOptions();
|
|
|
|
} else if (!headers.containsKey("authorization")) {
|
|
|
|
sendUnauthorized();
|
|
|
|
} else {
|
|
|
|
decodeCredentials(headers.get("authorization"));
|
2009-10-07 15:58:29 -04:00
|
|
|
// need to check session on each request, credentials may have changed or session expired
|
|
|
|
try {
|
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
2009-04-15 11:20:06 -04:00
|
|
|
handleRequest(command, path, headers, content);
|
2009-10-07 15:58:29 -04:00
|
|
|
} catch (DavMailAuthenticationException e) {
|
|
|
|
sendUnauthorized();
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
os.flush();
|
2009-03-16 12:00:08 -04:00
|
|
|
DavGatewayTray.resetIcon();
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} catch (SocketTimeoutException e) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CLOSE_CONNECTION_ON_TIMEOUT"));
|
2008-12-05 03:47:54 -05:00
|
|
|
} catch (SocketException e) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CONNECTION_CLOSED"));
|
2009-04-15 11:20:06 -04:00
|
|
|
} catch (Exception e) {
|
2009-07-15 18:47:31 -04:00
|
|
|
DavGatewayTray.log(e);
|
2008-11-26 19:56:28 -05:00
|
|
|
try {
|
2009-03-25 17:59:27 -04:00
|
|
|
sendErr(e);
|
2008-11-26 19:56:28 -05:00
|
|
|
} catch (IOException e2) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
DavGatewayTray.resetIcon();
|
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Handle caldav request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param command Http command
|
2009-08-05 18:13:34 -04:00
|
|
|
* @param path request path
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param headers Http headers map
|
2009-08-05 18:13:34 -04:00
|
|
|
* @param body request body
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public void handleRequest(String command, String path, Map<String, String> headers, String body) throws IOException {
|
2009-04-10 13:05:54 -04:00
|
|
|
CaldavRequest request = new CaldavRequest(command, path, headers, body);
|
|
|
|
if (request.isOptions()) {
|
2008-11-26 19:56:28 -05:00
|
|
|
sendOptions();
|
2009-04-10 13:05:54 -04:00
|
|
|
} else if (request.isPropFind() && request.isRoot()) {
|
2008-12-30 07:05:41 -05:00
|
|
|
sendRoot(request);
|
2009-04-10 13:05:54 -04:00
|
|
|
} else if (request.isGet() && request.isRoot()) {
|
2008-12-30 07:05:41 -05:00
|
|
|
sendGetRoot();
|
2009-04-15 18:05:11 -04:00
|
|
|
} else if (request.isPath(1, "principals")) {
|
|
|
|
handlePrincipals(request);
|
2009-04-14 19:46:37 -04:00
|
|
|
} else if (request.isPath(1, "users")) {
|
|
|
|
if (request.isPropFind() && request.isPathLength(3)) {
|
|
|
|
sendUserRoot(request);
|
|
|
|
} else {
|
2009-08-05 19:23:11 -04:00
|
|
|
handleCalendar(request);
|
2009-04-14 19:46:37 -04:00
|
|
|
}
|
|
|
|
} else if (request.isPath(1, "public")) {
|
2009-08-05 19:23:11 -04:00
|
|
|
handleCalendar(request);
|
2009-04-10 13:05:54 -04:00
|
|
|
} else {
|
|
|
|
sendUnsupported(request);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-15 18:05:11 -04:00
|
|
|
protected void handlePrincipals(CaldavRequest request) throws IOException {
|
|
|
|
if (request.isPath(2, "users")) {
|
|
|
|
if (request.isPropFind() && request.isPathLength(4)) {
|
|
|
|
sendPrincipal(request, "users", request.getPathElement(3));
|
|
|
|
// send back principal on search
|
|
|
|
} else if (request.isReport() && request.isPathLength(3)) {
|
|
|
|
sendPrincipal(request, "users", session.getEmail());
|
|
|
|
} else {
|
|
|
|
sendUnsupported(request);
|
|
|
|
}
|
2009-08-05 19:23:11 -04:00
|
|
|
} else if (request.isPath(2, "public")) {
|
|
|
|
StringBuilder prefixBuffer = new StringBuilder("public");
|
2009-08-06 08:15:52 -04:00
|
|
|
for (int i = 3; i < request.getPathLength() - 1; i++) {
|
|
|
|
prefixBuffer.append('/').append(request.getPathElement(i));
|
2009-08-05 19:23:11 -04:00
|
|
|
}
|
|
|
|
sendPrincipal(request, prefixBuffer.toString(), request.getLastPath());
|
2009-04-10 13:05:54 -04:00
|
|
|
} else {
|
|
|
|
sendUnsupported(request);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-05 19:23:11 -04:00
|
|
|
protected void handleCalendar(CaldavRequest request) throws IOException {
|
|
|
|
String lastPath = xmlDecodeName(request.getLastPath());
|
|
|
|
// folder requests
|
|
|
|
if (request.isPropFind() && "inbox".equals(lastPath)) {
|
|
|
|
sendInbox(request);
|
|
|
|
} else if (request.isPropFind() && "outbox".equals(lastPath)) {
|
|
|
|
sendOutbox(request);
|
|
|
|
} else if (request.isPost() && "outbox".equals(lastPath)) {
|
|
|
|
if (request.isFreeBusy()) {
|
|
|
|
sendFreeBusy(request.getBody());
|
2009-06-24 19:58:06 -04:00
|
|
|
} else {
|
2009-08-05 19:23:11 -04:00
|
|
|
int status = session.sendEvent(request.getBody());
|
2009-11-29 16:08:23 -05:00
|
|
|
// TODO: implement Itip response body
|
2009-04-14 19:46:37 -04:00
|
|
|
sendHttpResponse(status);
|
2009-02-11 20:33:49 -05:00
|
|
|
}
|
2009-08-05 19:23:11 -04:00
|
|
|
} else if (request.isPropFind()) {
|
|
|
|
sendCalendar(request);
|
|
|
|
} else if (request.isPropPatch()) {
|
|
|
|
patchCalendar(request);
|
|
|
|
} else if (request.isReport()) {
|
|
|
|
reportEvents(request);
|
|
|
|
// event requests
|
|
|
|
} else if (request.isPut()) {
|
|
|
|
String etag = request.getHeader("if-match");
|
|
|
|
String noneMatch = request.getHeader("if-none-match");
|
|
|
|
ExchangeSession.EventResult eventResult = session.createOrUpdateEvent(request.getExchangeFolderPath(), lastPath, request.getBody(), etag, noneMatch);
|
|
|
|
sendHttpResponse(eventResult.status, buildEtagHeader(eventResult.etag), null, "", true);
|
|
|
|
|
|
|
|
} else if (request.isDelete()) {
|
|
|
|
int status = session.deleteEvent(request.getExchangeFolderPath(), lastPath);
|
|
|
|
sendHttpResponse(status);
|
|
|
|
} else if (request.isGet()) {
|
2009-11-04 17:57:08 -05:00
|
|
|
if (request.path.endsWith("/")) {
|
|
|
|
// GET request on a folder => build ics content of all folder events
|
|
|
|
String folderPath = request.getExchangeFolderPath();
|
|
|
|
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
2009-11-05 16:54:06 -05:00
|
|
|
ChunkedResponse response = new ChunkedResponse(HttpStatus.SC_OK, "text/calendar;charset=UTF-8");
|
|
|
|
response.append("BEGIN:VCALENDAR\r\n");
|
|
|
|
response.append("VERSION:2.0\r\n");
|
|
|
|
response.append("PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN\r\n");
|
|
|
|
response.append("METHOD:PUBLISH\r\n");
|
|
|
|
|
2009-11-04 17:57:08 -05:00
|
|
|
for (ExchangeSession.Event event : events) {
|
2009-11-05 16:54:06 -05:00
|
|
|
String icsContent = StringUtil.getToken(event.getICS(), "BEGIN:VTIMEZONE", "END:VCALENDAR");
|
|
|
|
if (icsContent != null) {
|
|
|
|
response.append("BEGIN:VTIMEZONE");
|
|
|
|
response.append(icsContent);
|
|
|
|
} else {
|
|
|
|
icsContent = StringUtil.getToken(event.getICS(), "BEGIN:VEVENT", "END:VCALENDAR");
|
|
|
|
if (icsContent != null) {
|
|
|
|
response.append("BEGIN:VEVENT");
|
|
|
|
response.append(icsContent);
|
|
|
|
}
|
|
|
|
}
|
2009-11-04 17:57:08 -05:00
|
|
|
}
|
2009-11-05 16:54:06 -05:00
|
|
|
response.append("END:VCALENDAR");
|
|
|
|
response.close();
|
2009-11-04 17:57:08 -05:00
|
|
|
} else {
|
|
|
|
ExchangeSession.Event event = session.getEvent(request.getExchangeFolderPath(), lastPath);
|
|
|
|
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(event.getEtag()), "text/calendar;charset=UTF-8", event.getICS(), true);
|
|
|
|
}
|
2009-08-05 19:23:11 -04:00
|
|
|
} else if (request.isHead()) {
|
|
|
|
// test event
|
|
|
|
ExchangeSession.Event event = session.getEvent(request.getExchangeFolderPath(), lastPath);
|
|
|
|
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(event.getEtag()), "text/calendar;charset=UTF-8", (byte[]) null, true);
|
2008-11-26 19:56:28 -05:00
|
|
|
} else {
|
2009-04-10 13:05:54 -04:00
|
|
|
sendUnsupported(request);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
2009-08-05 19:23:11 -04:00
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-06-15 12:04:04 -04:00
|
|
|
protected HashMap<String, String> buildEtagHeader(String etag) {
|
|
|
|
if (etag != null) {
|
|
|
|
HashMap<String, String> etagHeader = new HashMap<String, String>();
|
|
|
|
etagHeader.put("ETag", etag);
|
|
|
|
return etagHeader;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 19:46:37 -04:00
|
|
|
protected void appendEventsResponses(CaldavResponse response, CaldavRequest request, List<ExchangeSession.Event> events) throws IOException {
|
2008-12-24 10:29:03 -05:00
|
|
|
int size = events.size();
|
|
|
|
int count = 0;
|
|
|
|
for (ExchangeSession.Event event : events) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LISTING_EVENT", ++count, size));
|
2009-03-25 17:59:27 -04:00
|
|
|
DavGatewayTray.switchIcon();
|
2009-04-14 19:46:37 -04:00
|
|
|
appendEventResponse(response, request, event);
|
2008-12-24 11:57:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 19:46:37 -04:00
|
|
|
protected void appendEventResponse(CaldavResponse response, CaldavRequest request, ExchangeSession.Event event) throws IOException {
|
2009-04-17 05:20:47 -04:00
|
|
|
StringBuilder eventPath = new StringBuilder();
|
2009-07-28 02:52:18 -04:00
|
|
|
eventPath.append(URIUtil.encodePath(request.getPath()));
|
2009-05-19 09:14:43 -04:00
|
|
|
if (!(eventPath.charAt(eventPath.length() - 1) == '/')) {
|
2009-04-17 05:20:47 -04:00
|
|
|
eventPath.append('/');
|
|
|
|
}
|
2009-08-13 04:34:51 -04:00
|
|
|
String eventName = xmlEncodeName(event.getName());
|
2009-04-17 05:20:47 -04:00
|
|
|
eventPath.append(URIUtil.encodeWithinQuery(eventName));
|
|
|
|
response.startResponse(eventPath.toString());
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startPropstat();
|
2008-12-24 11:57:34 -05:00
|
|
|
if (request.hasProperty("calendar-data")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendCalendarData(event.getICS());
|
2008-12-24 10:29:03 -05:00
|
|
|
}
|
2009-03-04 18:31:20 -05:00
|
|
|
if (request.hasProperty("getcontenttype")) {
|
|
|
|
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
|
|
|
|
}
|
2008-12-24 11:57:34 -05:00
|
|
|
if (request.hasProperty("getetag")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:getetag", event.getEtag());
|
2008-12-24 11:57:34 -05:00
|
|
|
}
|
2008-12-30 09:32:19 -05:00
|
|
|
if (request.hasProperty("resourcetype")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:resourcetype");
|
2008-12-30 09:32:19 -05:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-04-17 05:20:47 -04:00
|
|
|
response.appendProperty("D:displayname", eventName);
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2008-12-24 10:29:03 -05:00
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Append calendar object to Caldav response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param response Caldav response
|
|
|
|
* @param request Caldav request
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param subFolder calendar folder path relative to request path
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-15 18:05:11 -04:00
|
|
|
public void appendCalendar(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startPropstat();
|
2008-12-30 07:05:41 -05:00
|
|
|
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>" +
|
2009-08-06 08:59:08 -04:00
|
|
|
"<C:calendar/>");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
if (request.hasProperty("owner")) {
|
2009-04-14 19:46:37 -04:00
|
|
|
if ("users".equals(request.getPathElement(1))) {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("D:owner", "/principals/users/" + request.getPathElement(2));
|
2009-04-14 19:46:37 -04:00
|
|
|
} else {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("D:owner", "/principals" + request.getPath());
|
2009-04-14 19:46:37 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-03-04 18:31:20 -05:00
|
|
|
if (request.hasProperty("getcontenttype")) {
|
|
|
|
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
|
|
|
|
}
|
2009-01-02 11:07:49 -05:00
|
|
|
if (request.hasProperty("getetag")) {
|
2009-04-15 18:05:11 -04:00
|
|
|
response.appendProperty("D:getetag", session.getFolderResourceTag(request.getExchangeFolderPath(subFolder)));
|
2009-01-02 11:07:49 -05:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
if (request.hasProperty("getctag")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
|
2009-04-15 18:05:11 -04:00
|
|
|
base64Encode(session.getFolderCtag(request.getExchangeFolderPath(subFolder))));
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-04-15 18:05:11 -04:00
|
|
|
if (subFolder == null || subFolder.length() == 0) {
|
|
|
|
response.appendProperty("D:displayname", request.getLastPath());
|
|
|
|
} else {
|
|
|
|
response.appendProperty("D:displayname", subFolder);
|
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-08-06 08:59:08 -04:00
|
|
|
if (request.hasProperty("calendar-description")) {
|
|
|
|
response.appendProperty("C:calendar-description", "");
|
|
|
|
}
|
2009-09-30 04:14:28 -04:00
|
|
|
if (request.hasProperty("supported-calendar-component-set")) {
|
|
|
|
response.appendProperty("C:supported-calendar-component-set", "<C:comp name=\"VEVENT\"/>");
|
|
|
|
}
|
2009-08-06 08:59:08 -04:00
|
|
|
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Append calendar inbox object to Caldav response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param response Caldav response
|
|
|
|
* @param request Caldav request
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param subFolder inbox folder path relative to request path
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-15 18:05:11 -04:00
|
|
|
public void appendInbox(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
|
2009-09-15 04:34:13 -04:00
|
|
|
String ctag = "0";
|
|
|
|
String etag = "0";
|
|
|
|
String exchangeFolderPath = request.getExchangeFolderPath(subFolder);
|
|
|
|
try {
|
|
|
|
ctag = base64Encode(session.getFolderCtag(exchangeFolderPath));
|
|
|
|
etag = session.getFolderResourceTag(exchangeFolderPath);
|
|
|
|
} catch (HttpException e) {
|
|
|
|
// unauthorized access, probably an inbox on shared calendar
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_ACCESS_FORBIDDEN", exchangeFolderPath, e.getMessage()));
|
|
|
|
}
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startPropstat();
|
2008-12-30 07:05:41 -05:00
|
|
|
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>" +
|
|
|
|
"<C:schedule-inbox xmlns:C=\"urn:ietf:params:xml:ns:caldav\"/>");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-03-18 08:10:00 -04:00
|
|
|
if (request.hasProperty("getcontenttype")) {
|
|
|
|
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
|
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
if (request.hasProperty("getctag")) {
|
2009-09-15 04:34:13 -04:00
|
|
|
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"", ctag);
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-07-15 17:52:09 -04:00
|
|
|
if (request.hasProperty("getetag")) {
|
2009-09-15 04:34:13 -04:00
|
|
|
response.appendProperty("D:getetag", etag);
|
2009-07-15 17:52:09 -04:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-03-04 12:21:15 -05:00
|
|
|
response.appendProperty("D:displayname", "inbox");
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Append calendar outbox object to Caldav response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param response Caldav response
|
|
|
|
* @param request Caldav request
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param subFolder outbox folder path relative to request path
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-15 18:05:11 -04:00
|
|
|
public void appendOutbox(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startPropstat();
|
2008-12-30 07:05:41 -05:00
|
|
|
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>" +
|
|
|
|
"<C:schedule-outbox xmlns:C=\"urn:ietf:params:xml:ns:caldav\"/>");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
if (request.hasProperty("getctag")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
|
|
|
|
"0");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-07-15 17:52:09 -04:00
|
|
|
if (request.hasProperty("getetag")) {
|
2009-09-10 19:45:40 -04:00
|
|
|
response.appendProperty("D:getetag", "0");
|
2009-07-15 17:52:09 -04:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-03-04 12:21:15 -05:00
|
|
|
response.appendProperty("D:displayname", "outbox");
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send simple html response to GET /.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-12-30 07:05:41 -05:00
|
|
|
public void sendGetRoot() throws IOException {
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
buffer.append("Connected to DavMail<br/>");
|
|
|
|
buffer.append("UserName :").append(userName).append("<br/>");
|
|
|
|
buffer.append("Email :").append(session.getEmail()).append("<br/>");
|
|
|
|
sendHttpResponse(HttpStatus.SC_OK, null, "text/html;charset=UTF-8", buffer.toString(), true);
|
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send inbox response for request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public void sendInbox(CaldavRequest request) throws IOException {
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startMultistatus();
|
2009-04-15 18:05:11 -04:00
|
|
|
appendInbox(response, request, null);
|
2010-02-10 16:46:08 -05:00
|
|
|
if (request.getDepth() == 1 && !Settings.getBooleanProperty("davmail.caldavDisableInbox")) {
|
2009-09-15 04:34:13 -04:00
|
|
|
try {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_MESSAGES"));
|
|
|
|
List<ExchangeSession.Event> events = session.getEventMessages(request.getExchangeFolderPath());
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOUND_CALENDAR_MESSAGES", events.size()));
|
|
|
|
appendEventsResponses(response, request, events);
|
|
|
|
} catch (HttpException e) {
|
|
|
|
// unauthorized access, probably an inbox on shared calendar
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_ACCESS_FORBIDDEN", request.getExchangeFolderPath(), e.getMessage()));
|
|
|
|
}
|
2009-02-20 12:44:30 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send outbox response for request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public void sendOutbox(CaldavRequest request) throws IOException {
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startMultistatus();
|
2009-04-15 18:05:11 -04:00
|
|
|
appendOutbox(response, request, null);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send calendar response for request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public void sendCalendar(CaldavRequest request) throws IOException {
|
2009-04-15 18:05:11 -04:00
|
|
|
String folderPath = request.getExchangeFolderPath();
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startMultistatus();
|
2009-04-15 18:05:11 -04:00
|
|
|
appendCalendar(response, request, null);
|
2009-04-10 13:05:54 -04:00
|
|
|
if (request.getDepth() == 1) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_EVENTS", folderPath));
|
2009-04-14 19:46:37 -04:00
|
|
|
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_FOUND_CALENDAR_EVENTS", events.size()));
|
2009-04-14 19:46:37 -04:00
|
|
|
appendEventsResponses(response, request, events);
|
2009-11-05 16:55:43 -05:00
|
|
|
// Send sub folders for multi-calendar support under iCal, except for public folders
|
|
|
|
if (!folderPath.startsWith("/public")) {
|
|
|
|
List<ExchangeSession.Folder> folderList = session.getSubCalendarFolders(folderPath, false);
|
|
|
|
for (ExchangeSession.Folder folder : folderList) {
|
|
|
|
appendCalendar(response, request, folder.folderPath.substring(folder.folderPath.indexOf('/') + 1));
|
|
|
|
}
|
2009-10-03 16:50:33 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Fake PROPPATCH response for request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-05-19 09:14:43 -04:00
|
|
|
public void patchCalendar(CaldavRequest request) throws IOException {
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-09 12:02:58 -04:00
|
|
|
response.startMultistatus();
|
|
|
|
// just ignore calendar folder proppatch (color not supported in Exchange)
|
2009-05-19 09:14:43 -04:00
|
|
|
if (request.hasProperty("calendar-color")) {
|
|
|
|
response.startPropstat();
|
|
|
|
response.appendProperty("x1:calendar-color", "x1=\"http://apple.com/ns/ical/\"", null);
|
|
|
|
response.endPropStatOK();
|
|
|
|
}
|
2009-03-09 12:02:58 -04:00
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2009-03-09 12:02:58 -04:00
|
|
|
}
|
|
|
|
|
2008-12-30 07:05:41 -05:00
|
|
|
protected String getEventFileNameFromPath(String path) {
|
2009-02-25 05:23:07 -05:00
|
|
|
int index = path.lastIndexOf('/');
|
2008-12-30 07:05:41 -05:00
|
|
|
if (index < 0) {
|
|
|
|
return null;
|
|
|
|
} else {
|
2009-04-03 18:21:09 -04:00
|
|
|
return xmlDecodeName(path.substring(index + 1));
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Report events listed in request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public void reportEvents(CaldavRequest request) throws IOException {
|
2009-04-15 18:05:11 -04:00
|
|
|
String folderPath = request.getExchangeFolderPath();
|
2008-12-30 07:05:41 -05:00
|
|
|
List<ExchangeSession.Event> events;
|
|
|
|
List<String> notFound = new ArrayList<String>();
|
2009-04-01 19:53:26 -04:00
|
|
|
|
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
|
|
|
response.startMultistatus();
|
2008-12-30 07:05:41 -05:00
|
|
|
if (request.isMultiGet()) {
|
2009-01-08 05:35:43 -05:00
|
|
|
int count = 0;
|
|
|
|
int total = request.getHrefs().size();
|
2008-12-30 07:05:41 -05:00
|
|
|
for (String href : request.getHrefs()) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_REPORT_EVENT", ++count, total));
|
2009-04-10 16:08:34 -04:00
|
|
|
DavGatewayTray.switchIcon();
|
2010-02-03 17:00:14 -05:00
|
|
|
String eventName = getEventFileNameFromPath(href);
|
2008-12-30 07:05:41 -05:00
|
|
|
try {
|
2009-09-21 17:34:13 -04:00
|
|
|
// ignore cases for Sunbird
|
|
|
|
if (eventName != null && eventName.length() > 0
|
|
|
|
&& !"inbox".equals(eventName) && !"calendar".equals(eventName)) {
|
2009-11-17 18:11:08 -05:00
|
|
|
ExchangeSession.Event event;
|
2009-11-29 16:08:23 -05:00
|
|
|
event = session.getEvent(folderPath, eventName);
|
2009-11-17 18:11:08 -05:00
|
|
|
appendEventResponse(response, request, event);
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
} catch (HttpException e) {
|
2010-02-03 17:00:14 -05:00
|
|
|
DavGatewayTray.warn(new BundleMessage("LOG_EVENT_NOT_AVAILABLE", eventName, href));
|
2008-12-30 07:05:41 -05:00
|
|
|
notFound.add(href);
|
|
|
|
}
|
|
|
|
}
|
2009-04-14 19:46:37 -04:00
|
|
|
} else if (request.isPath(1, "users") && request.isPath(3, "inbox")) {
|
2009-04-15 18:05:11 -04:00
|
|
|
events = session.getEventMessages(request.getExchangeFolderPath());
|
2009-04-14 19:46:37 -04:00
|
|
|
appendEventsResponses(response, request, events);
|
2008-12-30 07:05:41 -05:00
|
|
|
} else {
|
2009-04-15 18:05:11 -04:00
|
|
|
events = session.getAllEvents(request.getExchangeFolderPath());
|
2009-04-14 19:46:37 -04:00
|
|
|
appendEventsResponses(response, request, events);
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// send not found events errors
|
|
|
|
for (String href : notFound) {
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath(href));
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendPropstatNotFound();
|
|
|
|
response.endResponse();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endMultistatus();
|
2009-03-19 03:42:37 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send user response for request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public void sendUserRoot(CaldavRequest request) throws IOException {
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startMultistatus();
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath(request.getPath()));
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startPropstat();
|
|
|
|
|
2008-12-30 07:05:41 -05:00
|
|
|
if (request.hasProperty("resourcetype")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-04-15 18:05:11 -04:00
|
|
|
response.appendProperty("D:displayname", request.getLastPath());
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-08-06 08:59:08 -04:00
|
|
|
if (request.hasProperty("getctag")) {
|
|
|
|
response.appendProperty("CS:getctag", "CS=\"http://calendarserver.org/ns/\"",
|
|
|
|
base64Encode(session.getFolderCtag(request.getExchangeFolderPath())));
|
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
2009-04-10 13:05:54 -04:00
|
|
|
if (request.getDepth() == 1) {
|
2009-04-15 18:05:11 -04:00
|
|
|
appendInbox(response, request, "inbox");
|
|
|
|
appendOutbox(response, request, "outbox");
|
|
|
|
appendCalendar(response, request, "calendar");
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endResponse();
|
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send caldav response for / request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-12-30 07:05:41 -05:00
|
|
|
public void sendRoot(CaldavRequest request) throws IOException {
|
2009-03-19 09:36:21 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
2009-03-03 06:09:07 -05:00
|
|
|
response.startMultistatus();
|
|
|
|
response.startResponse("/");
|
|
|
|
response.startPropstat();
|
|
|
|
|
2008-12-30 07:05:41 -05:00
|
|
|
if (request.hasProperty("principal-collection-set")) {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("D:principal-collection-set", "/principals/users/" + session.getEmail());
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2008-12-30 17:35:48 -05:00
|
|
|
if (request.hasProperty("displayname")) {
|
2009-03-03 06:09:07 -05:00
|
|
|
response.appendProperty("D:displayname", "ROOT");
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
2009-06-25 06:16:46 -04:00
|
|
|
if (request.hasProperty("resourcetype")) {
|
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>");
|
2009-06-24 11:18:05 -04:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2009-07-23 05:25:57 -04:00
|
|
|
if (request.depth == 1) {
|
2009-07-31 08:20:58 -04:00
|
|
|
// iPhone workaround: send calendar subfolder
|
2009-08-05 18:13:34 -04:00
|
|
|
response.startResponse("/users/" + session.getEmail() + "/calendar");
|
2009-07-31 08:20:58 -04:00
|
|
|
response.startPropstat();
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>" +
|
|
|
|
"<C:calendar xmlns:C=\"urn:ietf:params:xml:ns:caldav\"/>");
|
|
|
|
}
|
|
|
|
if (request.hasProperty("displayname")) {
|
2009-08-05 18:13:34 -04:00
|
|
|
response.appendProperty("D:displayname", session.getEmail());
|
2009-07-31 08:20:58 -04:00
|
|
|
}
|
|
|
|
if (request.hasProperty("supported-calendar-component-set")) {
|
2009-08-05 18:13:34 -04:00
|
|
|
response.appendProperty("C:supported-calendar-component-set", "<C:comp name=\"VEVENT\"/>");
|
2009-07-31 08:20:58 -04:00
|
|
|
}
|
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
|
|
|
|
2009-07-23 05:25:57 -04:00
|
|
|
response.startResponse("/users");
|
|
|
|
response.startPropstat();
|
|
|
|
if (request.hasProperty("displayname")) {
|
|
|
|
response.appendProperty("D:displayname", "users");
|
|
|
|
}
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>");
|
|
|
|
}
|
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
2009-08-05 18:13:34 -04:00
|
|
|
|
2009-07-23 05:25:57 -04:00
|
|
|
response.startResponse("/principals");
|
|
|
|
response.startPropstat();
|
|
|
|
if (request.hasProperty("displayname")) {
|
|
|
|
response.appendProperty("D:displayname", "principals");
|
|
|
|
}
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/>");
|
|
|
|
}
|
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
response.endMultistatus();
|
2009-03-19 04:19:47 -04:00
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Caldav principal response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param request Caldav request
|
|
|
|
* @param prefix principal prefix (users or public)
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param principal principal name (email address for users)
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-15 18:05:11 -04:00
|
|
|
public void sendPrincipal(CaldavRequest request, String prefix, String principal) throws IOException {
|
2009-02-23 04:55:53 -05:00
|
|
|
// actual principal is email address
|
|
|
|
String actualPrincipal = principal;
|
2009-08-03 19:04:11 -04:00
|
|
|
if ("users".equals(prefix) && session.getAlias().equalsIgnoreCase(principal)) {
|
2009-02-23 04:55:53 -05:00
|
|
|
actualPrincipal = session.getEmail();
|
|
|
|
}
|
|
|
|
|
2009-04-01 19:29:05 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
|
|
|
response.startMultistatus();
|
2009-07-28 02:52:18 -04:00
|
|
|
response.startResponse(URIUtil.encodePath("/principals/" + prefix + '/' + principal));
|
2009-04-01 19:29:05 -04:00
|
|
|
response.startPropstat();
|
2009-03-03 06:09:07 -05:00
|
|
|
|
2009-04-01 19:29:05 -04:00
|
|
|
if (request.hasProperty("calendar-home-set")) {
|
2009-04-15 18:05:11 -04:00
|
|
|
if ("users".equals(prefix)) {
|
2009-08-06 08:59:08 -04:00
|
|
|
response.appendHrefProperty("C:calendar-home-set", "/users/" + actualPrincipal + "/calendar/");
|
2009-04-15 18:05:11 -04:00
|
|
|
} else {
|
2009-08-05 19:23:11 -04:00
|
|
|
response.appendHrefProperty("C:calendar-home-set", '/' + prefix + '/' + actualPrincipal);
|
2009-04-15 18:05:11 -04:00
|
|
|
}
|
2009-04-01 19:29:05 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
|
2009-04-15 18:05:11 -04:00
|
|
|
if (request.hasProperty("calendar-user-address-set") && "users".equals(prefix)) {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("C:calendar-user-address-set", "mailto:" + actualPrincipal);
|
2009-04-01 19:29:05 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
|
2009-08-11 12:36:17 -04:00
|
|
|
if ("users".equals(prefix)) {
|
2009-03-19 10:07:49 -04:00
|
|
|
if (request.hasProperty("schedule-inbox-URL")) {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("C:schedule-inbox-URL", "/users/" + actualPrincipal + "/inbox");
|
2009-03-19 10:07:49 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
|
2009-03-19 10:07:49 -04:00
|
|
|
if (request.hasProperty("schedule-outbox-URL")) {
|
2009-07-27 05:51:52 -04:00
|
|
|
response.appendHrefProperty("C:schedule-outbox-URL", "/users/" + actualPrincipal + "/outbox");
|
2009-03-19 10:07:49 -04:00
|
|
|
}
|
2009-12-20 15:56:13 -05:00
|
|
|
} else {
|
2010-01-05 05:04:48 -05:00
|
|
|
// public calendar, send root href as inbox url (always empty) for Lightning
|
|
|
|
if (request.isLightning() && request.hasProperty("schedule-inbox-URL")) {
|
2009-12-22 08:50:33 -05:00
|
|
|
response.appendHrefProperty("C:schedule-inbox-URL", "/");
|
2009-12-20 15:56:13 -05:00
|
|
|
}
|
2009-12-22 08:50:33 -05:00
|
|
|
// send user outbox
|
2009-12-20 15:56:13 -05:00
|
|
|
if (request.hasProperty("schedule-outbox-URL")) {
|
2009-12-22 16:47:43 -05:00
|
|
|
response.appendHrefProperty("C:schedule-outbox-URL", "/users/" + session.getEmail() + "/outbox");
|
2009-12-20 15:56:13 -05:00
|
|
|
}
|
2009-04-01 19:29:05 -04:00
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
|
2009-04-01 19:29:05 -04:00
|
|
|
if (request.hasProperty("displayname")) {
|
|
|
|
response.appendProperty("D:displayname", actualPrincipal);
|
|
|
|
}
|
|
|
|
if (request.hasProperty("resourcetype")) {
|
|
|
|
response.appendProperty("D:resourcetype", "<D:collection/><D:principal/>");
|
|
|
|
}
|
|
|
|
response.endPropStatOK();
|
|
|
|
response.endResponse();
|
|
|
|
response.endMultistatus();
|
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send free busy response for body request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param body request body
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-12-30 07:05:41 -05:00
|
|
|
public void sendFreeBusy(String body) throws IOException {
|
2009-02-12 05:10:23 -05:00
|
|
|
HashMap<String, String> valueMap = new HashMap<String, String>();
|
|
|
|
ArrayList<String> attendees = new ArrayList<String>();
|
|
|
|
HashMap<String, String> attendeeKeyMap = new HashMap<String, String>();
|
2009-02-09 05:12:09 -05:00
|
|
|
ICSBufferedReader reader = new ICSBufferedReader(new StringReader(body));
|
2008-12-30 07:05:41 -05:00
|
|
|
String line;
|
2009-02-09 05:12:09 -05:00
|
|
|
String key;
|
2008-12-30 07:05:41 -05:00
|
|
|
while ((line = reader.readLine()) != null) {
|
2009-02-11 20:33:49 -05:00
|
|
|
int index = line.indexOf(':');
|
|
|
|
if (index <= 0) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_REQUEST", body);
|
2009-02-11 20:33:49 -05:00
|
|
|
}
|
|
|
|
String fullkey = line.substring(0, index);
|
|
|
|
String value = line.substring(index + 1);
|
2009-04-16 17:16:40 -04:00
|
|
|
int semicolonIndex = fullkey.indexOf(';');
|
2009-02-11 20:33:49 -05:00
|
|
|
if (semicolonIndex > 0) {
|
|
|
|
key = fullkey.substring(0, semicolonIndex);
|
|
|
|
} else {
|
|
|
|
key = fullkey;
|
|
|
|
}
|
2009-02-12 05:10:23 -05:00
|
|
|
if ("ATTENDEE".equals(key)) {
|
|
|
|
attendees.add(value);
|
|
|
|
attendeeKeyMap.put(value, fullkey);
|
|
|
|
} else {
|
|
|
|
valueMap.put(key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// get freebusy for each attendee
|
2009-03-20 12:05:45 -04:00
|
|
|
HashMap<String, ExchangeSession.FreeBusy> freeBusyMap = new HashMap<String, ExchangeSession.FreeBusy>();
|
2009-02-12 05:10:23 -05:00
|
|
|
for (String attendee : attendees) {
|
2009-08-13 04:34:51 -04:00
|
|
|
ExchangeSession.FreeBusy freeBusy = session.getFreebusy(attendee, valueMap.get("DTSTART"), valueMap.get("DTEND"));
|
2009-02-12 05:10:23 -05:00
|
|
|
if (freeBusy != null) {
|
|
|
|
freeBusyMap.put(attendee, freeBusy);
|
|
|
|
}
|
2008-12-30 07:05:41 -05:00
|
|
|
}
|
2009-06-16 04:55:00 -04:00
|
|
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_OK);
|
|
|
|
response.startScheduleResponse();
|
|
|
|
|
|
|
|
for (Map.Entry<String, ExchangeSession.FreeBusy> entry : freeBusyMap.entrySet()) {
|
|
|
|
String attendee = entry.getKey();
|
|
|
|
response.startRecipientResponse(attendee);
|
|
|
|
|
|
|
|
StringBuilder ics = new StringBuilder();
|
|
|
|
ics.append("BEGIN:VCALENDAR").append((char) 13).append((char) 10)
|
|
|
|
.append("VERSION:2.0").append((char) 13).append((char) 10)
|
|
|
|
.append("PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN").append((char) 13).append((char) 10)
|
|
|
|
.append("METHOD:REPLY").append((char) 13).append((char) 10)
|
|
|
|
.append("BEGIN:VFREEBUSY").append((char) 13).append((char) 10)
|
|
|
|
.append("DTSTAMP:").append(valueMap.get("DTSTAMP")).append("").append((char) 13).append((char) 10)
|
|
|
|
.append("ORGANIZER:").append(valueMap.get("ORGANIZER")).append("").append((char) 13).append((char) 10)
|
|
|
|
.append("DTSTART:").append(valueMap.get("DTSTART")).append("").append((char) 13).append((char) 10)
|
|
|
|
.append("DTEND:").append(valueMap.get("DTEND")).append("").append((char) 13).append((char) 10)
|
|
|
|
.append("UID:").append(valueMap.get("UID")).append("").append((char) 13).append((char) 10)
|
|
|
|
.append(attendeeKeyMap.get(attendee)).append(':').append(attendee).append("").append((char) 13).append((char) 10);
|
|
|
|
entry.getValue().appendTo(ics);
|
|
|
|
ics.append("END:VFREEBUSY").append((char) 13).append((char) 10)
|
|
|
|
.append("END:VCALENDAR");
|
|
|
|
response.appendCalendarData(ics.toString());
|
|
|
|
response.endRecipientResponse();
|
|
|
|
|
|
|
|
}
|
|
|
|
response.endScheduleResponse();
|
|
|
|
response.close();
|
2008-12-30 07:05:41 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http error response for exception
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param e exception
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-25 17:59:27 -04:00
|
|
|
public void sendErr(Exception e) throws IOException {
|
2008-12-30 17:35:48 -05:00
|
|
|
String message = e.getMessage();
|
|
|
|
if (message == null) {
|
|
|
|
message = e.toString();
|
|
|
|
}
|
2009-03-25 17:59:27 -04:00
|
|
|
sendErr(HttpStatus.SC_SERVICE_UNAVAILABLE, message);
|
2008-12-30 17:35:48 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send 400 bad response for unsupported request.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param request Caldav request
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-10 13:05:54 -04:00
|
|
|
public void sendUnsupported(CaldavRequest request) throws IOException {
|
2009-12-20 05:51:02 -05:00
|
|
|
BundleMessage message = new BundleMessage("LOG_UNSUPPORTED_REQUEST", request);
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.error(message);
|
|
|
|
sendErr(HttpStatus.SC_BAD_REQUEST, message.format());
|
2009-04-10 13:05:54 -04:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http error status and message.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param status Http status
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param message error messagee
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public void sendErr(int status, String message) throws IOException {
|
|
|
|
sendHttpResponse(status, null, "text/plain;charset=UTF-8", message, false);
|
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send OPTIONS response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public void sendOptions() throws IOException {
|
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2009-08-06 03:29:55 -04:00
|
|
|
headers.put("Allow", "OPTIONS, PROPFIND, HEAD, GET, REPORT, PROPPATCH, PUT, DELETE, POST");
|
2009-03-25 17:59:27 -04:00
|
|
|
sendHttpResponse(HttpStatus.SC_OK, headers);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send 401 Unauthorized response.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public void sendUnauthorized() throws IOException {
|
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
2009-07-23 05:21:34 -04:00
|
|
|
headers.put("WWW-Authenticate", "Basic realm=\"" + BundleMessage.format("UI_DAVMAIL_GATEWAY") + '\"');
|
2009-11-13 16:39:33 -05:00
|
|
|
sendHttpResponse(HttpStatus.SC_UNAUTHORIZED, headers, null, (byte[]) null, true);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http response with given status.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param status Http status
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-25 17:59:27 -04:00
|
|
|
public void sendHttpResponse(int status) throws IOException {
|
|
|
|
sendHttpResponse(status, null, null, (byte[]) null, true);
|
2009-03-03 06:09:07 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http response with given status and headers.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param status Http status
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param headers Http headers
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-25 17:59:27 -04:00
|
|
|
public void sendHttpResponse(int status, Map<String, String> headers) throws IOException {
|
|
|
|
sendHttpResponse(status, headers, null, (byte[]) null, true);
|
2009-03-03 06:09:07 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http response with given status in chunked mode.
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
2009-11-05 16:54:06 -05:00
|
|
|
* @param status Http status
|
|
|
|
* @param contentType MIME content type
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-11-05 16:54:06 -05:00
|
|
|
public void sendChunkedHttpResponse(int status, String contentType) throws IOException {
|
2009-03-19 04:19:47 -04:00
|
|
|
HashMap<String, String> headers = new HashMap<String, String>();
|
|
|
|
headers.put("Transfer-Encoding", "chunked");
|
2009-11-05 16:54:06 -05:00
|
|
|
sendHttpResponse(status, headers, contentType, (byte[]) null, true);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http response with given status, headers, content type and content.
|
|
|
|
* Close connection if keepAlive is false
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param status Http status
|
|
|
|
* @param headers Http headers
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param contentType MIME content type
|
2009-08-05 18:13:34 -04:00
|
|
|
* @param content response body as string
|
|
|
|
* @param keepAlive keep connection open
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2008-11-26 19:56:28 -05:00
|
|
|
public void sendHttpResponse(int status, Map<String, String> headers, String contentType, String content, boolean keepAlive) throws IOException {
|
2009-03-03 06:09:07 -05:00
|
|
|
sendHttpResponse(status, headers, contentType, content.getBytes("UTF-8"), keepAlive);
|
|
|
|
}
|
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
|
|
|
* Send Http response with given status, headers, content type and content.
|
|
|
|
* Close connection if keepAlive is false
|
2009-08-05 18:13:34 -04:00
|
|
|
*
|
|
|
|
* @param status Http status
|
|
|
|
* @param headers Http headers
|
2009-08-03 17:40:05 -04:00
|
|
|
* @param contentType MIME content type
|
2009-08-05 18:13:34 -04:00
|
|
|
* @param content response body as byte array
|
|
|
|
* @param keepAlive keep connection open
|
2009-08-03 17:40:05 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-03 06:09:07 -05:00
|
|
|
public void sendHttpResponse(int status, Map<String, String> headers, String contentType, byte[] content, boolean keepAlive) throws IOException {
|
2009-04-23 16:53:22 -04:00
|
|
|
sendClient("HTTP/1.1 " + status + ' ' + HttpStatus.getStatusText(status));
|
2009-12-22 16:47:43 -05:00
|
|
|
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");
|
|
|
|
SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
|
|
|
// force GMT timezone
|
|
|
|
formatter.setTimeZone(ExchangeSession.GMT_TIMEZONE);
|
|
|
|
String now = formatter.format(new Date());
|
|
|
|
sendClient("Date: " + now);
|
|
|
|
sendClient("Expires: " + now);
|
|
|
|
sendClient("Cache-Control: private, max-age=0");
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
if (headers != null) {
|
2008-12-17 10:22:37 -05:00
|
|
|
for (Map.Entry<String, String> header : headers.entrySet()) {
|
2008-12-02 05:20:46 -05:00
|
|
|
sendClient(header.getKey() + ": " + header.getValue());
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (contentType != null) {
|
|
|
|
sendClient("Content-Type: " + contentType);
|
|
|
|
}
|
2009-02-11 20:33:49 -05:00
|
|
|
closed = closed || !keepAlive;
|
|
|
|
sendClient("Connection: " + (closed ? "close" : "keep-alive"));
|
2009-03-03 06:09:07 -05:00
|
|
|
if (content != null && content.length > 0) {
|
|
|
|
sendClient("Content-Length: " + content.length);
|
2009-03-19 03:42:37 -04:00
|
|
|
} else if (headers == null || !"chunked".equals(headers.get("Transfer-Encoding"))) {
|
2008-11-26 19:56:28 -05:00
|
|
|
sendClient("Content-Length: 0");
|
|
|
|
}
|
|
|
|
sendClient("");
|
2009-03-03 06:09:07 -05:00
|
|
|
if (content != null && content.length > 0) {
|
2008-12-24 10:52:13 -05:00
|
|
|
// full debug trace
|
2008-12-26 07:35:08 -05:00
|
|
|
if (wireLogger.isDebugEnabled()) {
|
2009-03-03 06:09:07 -05:00
|
|
|
wireLogger.debug("> " + new String(content, "UTF-8"));
|
2008-12-26 07:35:08 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
sendClient(content);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode HTTP credentials
|
|
|
|
*
|
|
|
|
* @param authorization http authorization header value
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException if invalid credentials
|
2008-11-26 19:56:28 -05:00
|
|
|
*/
|
|
|
|
protected void decodeCredentials(String authorization) throws IOException {
|
|
|
|
int index = authorization.indexOf(' ');
|
|
|
|
if (index > 0) {
|
|
|
|
String mode = authorization.substring(0, index).toLowerCase();
|
|
|
|
if (!"basic".equals(mode)) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_UNSUPPORTED_AUTHORIZATION_MODE", mode);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
String encodedCredentials = authorization.substring(index + 1);
|
|
|
|
String decodedCredentials = base64Decode(encodedCredentials);
|
|
|
|
index = decodedCredentials.indexOf(':');
|
|
|
|
if (index > 0) {
|
|
|
|
userName = decodedCredentials.substring(0, index);
|
|
|
|
password = decodedCredentials.substring(index + 1);
|
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CREDENTIALS");
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} else {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CREDENTIALS");
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-04-03 18:21:09 -04:00
|
|
|
/**
|
|
|
|
* Need to encode xml for iCal
|
|
|
|
*
|
|
|
|
* @param name decoded name
|
|
|
|
* @return name encoded name
|
|
|
|
*/
|
|
|
|
protected String xmlEncodeName(String name) {
|
|
|
|
String result = name;
|
|
|
|
if (name.indexOf('&') >= 0) {
|
|
|
|
result = result.replaceAll("&", "&");
|
|
|
|
}
|
|
|
|
if (name.indexOf('<') >= 0) {
|
2009-04-03 19:01:18 -04:00
|
|
|
result = result.replaceAll("<", "<");
|
2009-04-03 18:21:09 -04:00
|
|
|
}
|
|
|
|
if (name.indexOf('>') >= 0) {
|
2009-04-03 19:01:18 -04:00
|
|
|
result = result.replaceAll(">", ">");
|
2009-04-03 18:21:09 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Need to decode xml for iCal
|
|
|
|
*
|
|
|
|
* @param name encoded name
|
|
|
|
* @return name decoded name
|
|
|
|
*/
|
|
|
|
protected String xmlDecodeName(String name) {
|
|
|
|
String result = name;
|
|
|
|
if (name.indexOf("&") >= 0) {
|
|
|
|
result = result.replaceAll("&", "&");
|
|
|
|
}
|
2009-04-03 19:01:18 -04:00
|
|
|
if (name.indexOf(">") >= 0) {
|
|
|
|
result = result.replaceAll(">", ">");
|
2009-04-03 18:21:09 -04:00
|
|
|
}
|
2009-04-03 19:01:18 -04:00
|
|
|
if (name.indexOf("<") >= 0) {
|
|
|
|
result = result.replaceAll("<", "<");
|
2009-04-03 18:21:09 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-08-06 08:15:52 -04:00
|
|
|
/**
|
|
|
|
* Make sure + sign in URL is encoded.
|
2009-09-09 17:50:11 -04:00
|
|
|
*
|
2009-08-06 08:15:52 -04:00
|
|
|
* @param path URL path
|
|
|
|
* @return reencoded path
|
|
|
|
*/
|
|
|
|
protected String encodePlusSign(String path) {
|
|
|
|
// make sure + sign in URL is encoded
|
|
|
|
if (path.indexOf('+') >= 0) {
|
|
|
|
return path.replaceAll("\\+", "%2B");
|
|
|
|
} else {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 19:46:37 -04:00
|
|
|
protected class CaldavRequest {
|
2009-04-16 17:16:40 -04:00
|
|
|
protected final String command;
|
|
|
|
protected final String path;
|
|
|
|
protected final String[] pathElements;
|
|
|
|
protected final Map<String, String> headers;
|
2009-04-10 13:05:54 -04:00
|
|
|
protected int depth;
|
2009-04-16 17:16:40 -04:00
|
|
|
protected final String body;
|
2009-02-24 06:53:02 -05:00
|
|
|
protected final HashSet<String> properties = new HashSet<String>();
|
2008-12-17 10:22:37 -05:00
|
|
|
protected HashSet<String> hrefs;
|
|
|
|
protected boolean isMultiGet;
|
|
|
|
|
2009-04-16 17:52:17 -04:00
|
|
|
protected CaldavRequest(String command, String path, Map<String, String> headers, String body) throws IOException {
|
2009-04-10 13:05:54 -04:00
|
|
|
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() {
|
2009-06-24 19:58:06 -04:00
|
|
|
return "PROPPATCH".equals(command);
|
2009-04-10 13:05:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isReport() {
|
|
|
|
return "REPORT".equals(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isGet() {
|
|
|
|
return "GET".equals(command);
|
|
|
|
}
|
|
|
|
|
2009-05-17 19:11:06 -04:00
|
|
|
public boolean isHead() {
|
|
|
|
return "HEAD".equals(command);
|
|
|
|
}
|
|
|
|
|
2009-04-10 13:05:54 -04:00
|
|
|
public boolean isPut() {
|
|
|
|
return "PUT".equals(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isPost() {
|
|
|
|
return "POST".equals(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isDelete() {
|
|
|
|
return "DELETE".equals(command);
|
|
|
|
}
|
|
|
|
|
2009-09-24 04:58:22 -04:00
|
|
|
/**
|
|
|
|
* Check if this request is a folder request.
|
|
|
|
*
|
|
|
|
* @return true if this is a folder (not event) request
|
|
|
|
*/
|
|
|
|
public boolean isFolder() {
|
|
|
|
return isPropFind() || isReport() || isPropPatch() || isOptions() || isPost();
|
|
|
|
}
|
|
|
|
|
2009-04-10 13:05:54 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-14 19:46:37 -04:00
|
|
|
public String getPath() {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2009-04-15 18:05:11 -04:00
|
|
|
public String getPath(String subFolder) {
|
2009-08-06 10:12:25 -04:00
|
|
|
String folderPath;
|
2009-04-15 18:05:11 -04:00
|
|
|
if (subFolder == null || subFolder.length() == 0) {
|
2009-08-06 08:59:08 -04:00
|
|
|
folderPath = path;
|
2009-06-25 06:16:46 -04:00
|
|
|
} else if (path.endsWith("/")) {
|
2009-09-09 17:50:11 -04:00
|
|
|
folderPath = path + subFolder;
|
2009-08-06 08:59:08 -04:00
|
|
|
} else {
|
|
|
|
folderPath = path + '/' + subFolder;
|
|
|
|
}
|
|
|
|
if (folderPath.endsWith("/")) {
|
|
|
|
return folderPath;
|
2009-04-15 18:05:11 -04:00
|
|
|
} else {
|
2009-08-06 08:59:08 -04:00
|
|
|
return folderPath + '/';
|
2009-04-15 18:05:11 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-10 13:05:54 -04:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-15 18:05:11 -04:00
|
|
|
public String getLastPath() {
|
|
|
|
return getPathElement(getPathLength() - 1);
|
|
|
|
}
|
|
|
|
|
2010-01-05 05:04:48 -05:00
|
|
|
protected boolean isIcal3() {
|
|
|
|
return isUserAgent("DAVKit/3");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isLightning() {
|
|
|
|
return isUserAgent("Lightning/");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isUserAgent(String key) {
|
2009-04-10 13:05:54 -04:00
|
|
|
String userAgent = headers.get("user-agent");
|
2010-01-05 05:04:48 -05:00
|
|
|
return userAgent != null && userAgent.indexOf(key) >= 0;
|
2009-04-10 13:05:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2009-04-23 10:54:06 -04:00
|
|
|
DavGatewayTray.warn(new BundleMessage("LOG_INVALID_DEPTH", depthValue));
|
2009-04-10 13:05:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getDepth() {
|
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getBody() {
|
|
|
|
return body;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getHeader(String headerName) {
|
|
|
|
return headers.get(headerName);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void parseXmlBody() throws IOException {
|
2008-12-17 10:22:37 -05:00
|
|
|
XMLStreamReader streamReader = null;
|
|
|
|
try {
|
|
|
|
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
|
|
|
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
|
|
|
|
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
|
|
|
|
|
|
|
|
streamReader = inputFactory.createXMLStreamReader(new StringReader(body));
|
|
|
|
boolean inElement = false;
|
|
|
|
boolean inProperties = false;
|
|
|
|
String currentElement = null;
|
|
|
|
while (streamReader.hasNext()) {
|
|
|
|
int event = streamReader.next();
|
|
|
|
if (event == XMLStreamConstants.START_ELEMENT) {
|
|
|
|
inElement = true;
|
|
|
|
currentElement = streamReader.getLocalName();
|
|
|
|
if ("prop".equals(currentElement)) {
|
|
|
|
inProperties = true;
|
|
|
|
} else if ("calendar-multiget".equals(currentElement)) {
|
|
|
|
isMultiGet = true;
|
|
|
|
} else if (inProperties) {
|
|
|
|
properties.add(currentElement);
|
|
|
|
}
|
|
|
|
} else if (event == XMLStreamConstants.END_ELEMENT) {
|
|
|
|
if ("prop".equals(currentElement)) {
|
|
|
|
inProperties = false;
|
|
|
|
}
|
|
|
|
} else if (event == XMLStreamConstants.CHARACTERS && inElement) {
|
|
|
|
if ("href".equals(currentElement)) {
|
|
|
|
if (hrefs == null) {
|
|
|
|
hrefs = new HashSet<String>();
|
|
|
|
}
|
2010-01-05 05:04:48 -05:00
|
|
|
if (isIcal3()) {
|
2009-04-03 18:21:09 -04:00
|
|
|
hrefs.add(streamReader.getText());
|
|
|
|
} else {
|
2009-08-06 08:15:52 -04:00
|
|
|
hrefs.add(URIUtil.decode(encodePlusSign(streamReader.getText())));
|
2009-04-03 18:21:09 -04:00
|
|
|
}
|
2008-12-17 10:22:37 -05:00
|
|
|
}
|
|
|
|
inElement = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (XMLStreamException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_CALDAV_REQUEST", e.getMessage());
|
2008-12-17 10:22:37 -05:00
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
if (streamReader != null) {
|
|
|
|
streamReader.close();
|
|
|
|
}
|
|
|
|
} catch (XMLStreamException e) {
|
|
|
|
DavGatewayTray.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasProperty(String propertyName) {
|
|
|
|
return properties.contains(propertyName);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isMultiGet() {
|
|
|
|
return isMultiGet && hrefs != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Set<String> getHrefs() {
|
|
|
|
return hrefs;
|
|
|
|
}
|
2009-04-10 13:05:54 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2009-04-23 16:53:22 -04:00
|
|
|
return command + ' ' + path + " Depth: " + depth + '\n' + body;
|
2009-04-10 13:05:54 -04:00
|
|
|
}
|
2009-04-14 19:46:37 -04:00
|
|
|
|
2009-04-15 18:05:11 -04:00
|
|
|
/**
|
|
|
|
* Translate request path to Exchange folder path.
|
|
|
|
*
|
|
|
|
* @return exchange folder path
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public String getExchangeFolderPath() throws IOException {
|
2009-09-24 04:58:22 -04:00
|
|
|
int endIndex;
|
|
|
|
if (isFolder()) {
|
|
|
|
endIndex = getPathLength();
|
|
|
|
} else {
|
|
|
|
endIndex = getPathLength() - 1;
|
|
|
|
}
|
2009-04-14 19:46:37 -04:00
|
|
|
if ("users".equals(getPathElement(1))) {
|
2009-09-10 19:45:40 -04:00
|
|
|
StringBuilder calendarPath = new StringBuilder();
|
|
|
|
calendarPath.append(getPathElement(3));
|
2009-09-24 04:58:22 -04:00
|
|
|
for (int i = 4; i < endIndex; i++) {
|
2009-09-10 19:45:40 -04:00
|
|
|
calendarPath.append('/').append(getPathElement(i));
|
|
|
|
}
|
|
|
|
return session.buildCalendarPath(getPathElement(2), calendarPath.toString());
|
2009-04-14 19:46:37 -04:00
|
|
|
} else {
|
2009-09-24 04:58:22 -04:00
|
|
|
StringBuilder calendarPath = new StringBuilder();
|
2009-10-07 15:58:29 -04:00
|
|
|
for (int i = 0; i < endIndex; i++) {
|
2009-11-04 17:57:08 -05:00
|
|
|
if (getPathElement(i).length() > 0) {
|
|
|
|
calendarPath.append('/').append(getPathElement(i));
|
|
|
|
}
|
2009-09-24 04:58:22 -04:00
|
|
|
}
|
|
|
|
return calendarPath.toString();
|
2009-04-14 19:46:37 -04:00
|
|
|
}
|
|
|
|
}
|
2009-04-15 18:05:11 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 {
|
2009-12-22 16:47:43 -05:00
|
|
|
return getExchangeFolderPath() + '/' + subFolder;
|
2009-04-15 18:05:11 -04:00
|
|
|
}
|
|
|
|
}
|
2008-12-17 10:22:37 -05:00
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
|
2009-08-03 17:40:05 -04:00
|
|
|
/**
|
2009-11-05 16:54:06 -05:00
|
|
|
* Http chunked response.
|
2009-08-03 17:40:05 -04:00
|
|
|
*/
|
2009-11-05 16:54:06 -05:00
|
|
|
protected class ChunkedResponse {
|
2009-03-19 03:42:37 -04:00
|
|
|
Writer writer;
|
2009-03-03 06:09:07 -05:00
|
|
|
|
2009-11-05 16:54:06 -05:00
|
|
|
protected ChunkedResponse(int status, String contentType) throws IOException {
|
2009-03-19 04:19:47 -04:00
|
|
|
writer = new OutputStreamWriter(new BufferedOutputStream(new OutputStream() {
|
|
|
|
@Override
|
|
|
|
public void write(byte[] data, int offset, int length) throws IOException {
|
|
|
|
sendClient(Integer.toHexString(length));
|
|
|
|
sendClient(data, offset, length);
|
2009-06-25 06:16:46 -04:00
|
|
|
if (wireLogger.isDebugEnabled()) {
|
|
|
|
StringBuilder logBuffer = new StringBuilder("> ");
|
|
|
|
logBuffer.append(new String(data, offset, length));
|
|
|
|
wireLogger.debug(logBuffer.toString());
|
|
|
|
}
|
2009-03-19 04:19:47 -04:00
|
|
|
sendClient("");
|
|
|
|
}
|
2009-03-19 03:42:37 -04:00
|
|
|
|
2009-03-19 04:19:47 -04:00
|
|
|
@Override
|
|
|
|
public void write(int b) throws IOException {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
2009-03-19 10:07:49 -04:00
|
|
|
|
2009-03-19 04:19:47 -04:00
|
|
|
@Override
|
2009-03-19 10:07:49 -04:00
|
|
|
public void close() throws IOException {
|
2009-03-19 04:19:47 -04:00
|
|
|
sendClient("0");
|
|
|
|
sendClient("");
|
|
|
|
}
|
|
|
|
}), "UTF-8");
|
2009-11-05 16:54:06 -05:00
|
|
|
sendChunkedHttpResponse(status, contentType);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void append(String data) throws IOException {
|
|
|
|
writer.write(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void close() throws IOException {
|
|
|
|
writer.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Caldav response wrapper, content sent in chunked mode to avoid timeout
|
|
|
|
*/
|
|
|
|
protected class CaldavResponse extends ChunkedResponse {
|
|
|
|
|
|
|
|
protected CaldavResponse(int status) throws IOException {
|
|
|
|
super(status, "text/xml;charset=UTF-8");
|
2009-03-03 06:09:07 -05:00
|
|
|
writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
|
|
|
}
|
|
|
|
|
2009-03-19 03:42:37 -04:00
|
|
|
|
2009-03-03 06:09:07 -05:00
|
|
|
public void startMultistatus() throws IOException {
|
|
|
|
writer.write("<D:multistatus xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startResponse(String href) throws IOException {
|
|
|
|
writer.write("<D:response>");
|
|
|
|
writer.write("<D:href>");
|
2009-08-05 19:23:11 -04:00
|
|
|
writer.write(xmlEncodeName(href));
|
2009-03-03 06:09:07 -05:00
|
|
|
writer.write("</D:href>");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startPropstat() throws IOException {
|
|
|
|
writer.write("<D:propstat>");
|
|
|
|
writer.write("<D:prop>");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void appendCalendarData(String ics) throws IOException {
|
|
|
|
if (ics != null && ics.length() > 0) {
|
|
|
|
writer.write("<C:calendar-data xmlns:C=\"urn:ietf:params:xml:ns:caldav\"");
|
|
|
|
writer.write(" C:content-type=\"text/calendar\" C:version=\"2.0\">");
|
2009-04-16 17:52:17 -04:00
|
|
|
writer.write(ics.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"));
|
2009-03-03 06:09:07 -05:00
|
|
|
writer.write("</C:calendar-data>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-27 05:51:52 -04:00
|
|
|
public void appendHrefProperty(String propertyName, String propertyValue) throws IOException {
|
2009-08-05 19:23:11 -04:00
|
|
|
appendProperty(propertyName, null, "<D:href>" + URIUtil.encodePath(xmlEncodeName(propertyValue)) + "</D:href>");
|
2009-07-27 05:51:52 -04:00
|
|
|
}
|
|
|
|
|
2009-03-03 06:09:07 -05:00
|
|
|
public void appendProperty(String propertyName) throws IOException {
|
|
|
|
appendProperty(propertyName, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void appendProperty(String propertyName, String propertyValue) throws IOException {
|
|
|
|
appendProperty(propertyName, null, propertyValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void appendProperty(String propertyName, String namespace, String propertyValue) throws IOException {
|
|
|
|
if (propertyValue != null) {
|
|
|
|
writer.write('<');
|
|
|
|
writer.write(propertyName);
|
|
|
|
if (namespace != null) {
|
|
|
|
writer.write(" xmlns:");
|
|
|
|
writer.write(namespace);
|
|
|
|
}
|
|
|
|
writer.write('>');
|
|
|
|
writer.write(propertyValue);
|
|
|
|
writer.write("</");
|
|
|
|
writer.write(propertyName);
|
|
|
|
writer.write('>');
|
|
|
|
} else {
|
|
|
|
writer.write('<');
|
|
|
|
writer.write(propertyName);
|
2009-05-19 09:14:43 -04:00
|
|
|
if (namespace != null) {
|
|
|
|
writer.write(" xmlns:");
|
|
|
|
writer.write(namespace);
|
|
|
|
}
|
2009-03-03 06:09:07 -05:00
|
|
|
writer.write("/>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endPropStatOK() throws IOException {
|
2009-04-10 13:05:54 -04:00
|
|
|
writer.write("</D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat>");
|
2009-03-03 06:09:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void appendPropstatNotFound() throws IOException {
|
2009-04-10 13:05:54 -04:00
|
|
|
writer.write("<D:propstat><D:status>HTTP/1.1 404 Not Found</D:status></D:propstat>");
|
2009-03-03 06:09:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void endResponse() throws IOException {
|
|
|
|
writer.write("</D:response>");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endMultistatus() throws IOException {
|
|
|
|
writer.write("</D:multistatus>");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startScheduleResponse() throws IOException {
|
|
|
|
writer.write("<C:schedule-response xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startRecipientResponse(String recipient) throws IOException {
|
2009-04-10 13:05:54 -04:00
|
|
|
writer.write("<C:response><C:recipient><D:href>");
|
2009-03-03 06:09:07 -05:00
|
|
|
writer.write(recipient);
|
2009-04-10 13:05:54 -04:00
|
|
|
writer.write("</D:href></C:recipient><C:request-status>2.0;Success</C:request-status>");
|
2009-03-03 06:09:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void endRecipientResponse() throws IOException {
|
|
|
|
writer.write("</C:response>");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endScheduleResponse() throws IOException {
|
|
|
|
writer.write("</C:schedule-response>");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|