mirror of
https://github.com/moparisthebest/davmail
synced 2025-02-28 09:21:49 -05:00
Caldav (Calendar) support with free/busy and rename threads
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@179 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
20ef4a9da0
commit
64e0688f0d
@ -2,6 +2,7 @@ package davmail;
|
|||||||
|
|
||||||
import davmail.exchange.ExchangeSession;
|
import davmail.exchange.ExchangeSession;
|
||||||
import davmail.tray.DavGatewayTray;
|
import davmail.tray.DavGatewayTray;
|
||||||
|
import davmail.smtp.SmtpConnection;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -9,6 +10,8 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic connection common to pop3 and smtp implementations
|
* Generic connection common to pop3 and smtp implementations
|
||||||
*/
|
*/
|
||||||
@ -26,11 +29,12 @@ public class AbstractConnection extends Thread {
|
|||||||
protected ExchangeSession session;
|
protected ExchangeSession session;
|
||||||
|
|
||||||
// Initialize the streams and start the thread
|
// Initialize the streams and start the thread
|
||||||
public AbstractConnection(Socket clientSocket) {
|
public AbstractConnection(String name, Socket clientSocket) {
|
||||||
|
super(name+"-"+clientSocket.getPort());
|
||||||
this.client = clientSocket;
|
this.client = clientSocket;
|
||||||
try {
|
try {
|
||||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||||
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
|
in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
|
||||||
os = client.getOutputStream();
|
os = client.getOutputStream();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
close();
|
close();
|
||||||
@ -38,10 +42,21 @@ public class AbstractConnection extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message to client followed by CRLF.
|
||||||
|
* @param message message
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
public void sendClient(String message) throws IOException {
|
public void sendClient(String message) throws IOException {
|
||||||
sendClient(null, message);
|
sendClient(null, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send prefix and message to client followed by CRLF.
|
||||||
|
* @param prefix prefix
|
||||||
|
* @param message message
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
public void sendClient(String prefix, String message) throws IOException {
|
public void sendClient(String prefix, String message) throws IOException {
|
||||||
StringBuffer logBuffer = new StringBuffer("> ");
|
StringBuffer logBuffer = new StringBuffer("> ");
|
||||||
if (prefix != null) {
|
if (prefix != null) {
|
||||||
@ -56,6 +71,19 @@ public class AbstractConnection extends Thread {
|
|||||||
os.flush();
|
os.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send only bytes to client.
|
||||||
|
* @param messageBytes content
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
public void sendClient(byte[] messageBytes) throws IOException {
|
||||||
|
StringBuffer logBuffer = new StringBuffer("> ");
|
||||||
|
logBuffer.append(new String(messageBytes));
|
||||||
|
DavGatewayTray.debug(logBuffer.toString());
|
||||||
|
os.write(messageBytes);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a line from the client connection.
|
* Read a line from the client connection.
|
||||||
* Log message to stdout
|
* Log message to stdout
|
||||||
@ -65,10 +93,17 @@ public class AbstractConnection extends Thread {
|
|||||||
*/
|
*/
|
||||||
public String readClient() throws IOException {
|
public String readClient() throws IOException {
|
||||||
String line = in.readLine();
|
String line = in.readLine();
|
||||||
if (line != null && !line.startsWith("PASS")) {
|
// TODO : add basic authorization check
|
||||||
DavGatewayTray.debug("< " + line);
|
if (line != null) {
|
||||||
} else {
|
if (line.startsWith("PASS")) {
|
||||||
DavGatewayTray.debug("< PASS ********");
|
DavGatewayTray.debug("< PASS ********");
|
||||||
|
} else if (state == SmtpConnection.PASSWORD){
|
||||||
|
DavGatewayTray.debug("< ********");
|
||||||
|
} else if (line.startsWith("Authorization:")){
|
||||||
|
DavGatewayTray.debug("< Authorization: ********");
|
||||||
|
} else {
|
||||||
|
DavGatewayTray.debug("< " + line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DavGatewayTray.switchIcon();
|
DavGatewayTray.switchIcon();
|
||||||
return line;
|
return line;
|
||||||
@ -106,4 +141,11 @@ public class AbstractConnection extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String base64Encode(String value) {
|
||||||
|
return new String(Base64.encode(value.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String base64Decode(String value) throws IOException {
|
||||||
|
return new String(Base64.decode(value.getBytes()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,13 @@ public abstract class AbstractServer extends Thread {
|
|||||||
* Create a ServerSocket to listen for connections.
|
* Create a ServerSocket to listen for connections.
|
||||||
* Start the thread.
|
* Start the thread.
|
||||||
*
|
*
|
||||||
|
* @param name thread name
|
||||||
* @param port tcp socket chosen port
|
* @param port tcp socket chosen port
|
||||||
* @param defaultPort tcp socket default port
|
* @param defaultPort tcp socket default port
|
||||||
* @throws java.io.IOException unable to create server socket
|
* @throws java.io.IOException unable to create server socket
|
||||||
*/
|
*/
|
||||||
public AbstractServer(int port, int defaultPort) throws IOException {
|
public AbstractServer(String name, int port, int defaultPort) throws IOException {
|
||||||
|
super(name);
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
this.port = defaultPort;
|
this.port = defaultPort;
|
||||||
} else {
|
} else {
|
||||||
@ -52,6 +54,8 @@ public abstract class AbstractServer extends Thread {
|
|||||||
//noinspection InfiniteLoopStatement
|
//noinspection InfiniteLoopStatement
|
||||||
while (true) {
|
while (true) {
|
||||||
clientSocket = serverSocket.accept();
|
clientSocket = serverSocket.accept();
|
||||||
|
// set default timeout to 5 minutes
|
||||||
|
clientSocket.setSoTimeout(300000);
|
||||||
DavGatewayTray.debug("Connection from " + clientSocket.getInetAddress() + " on port " + port);
|
DavGatewayTray.debug("Connection from " + clientSocket.getInetAddress() + " on port " + port);
|
||||||
// only accept localhost connections for security reasons
|
// only accept localhost connections for security reasons
|
||||||
if (Settings.getBooleanProperty("davmail.allowRemote") ||
|
if (Settings.getBooleanProperty("davmail.allowRemote") ||
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package davmail;
|
package davmail;
|
||||||
|
|
||||||
import davmail.http.DavGatewaySSLProtocolSocketFactory;
|
import davmail.caldav.CaldavServer;
|
||||||
import davmail.http.DavGatewayHttpClientFacade;
|
import davmail.http.DavGatewayHttpClientFacade;
|
||||||
|
import davmail.http.DavGatewaySSLProtocolSocketFactory;
|
||||||
import davmail.pop.PopServer;
|
import davmail.pop.PopServer;
|
||||||
import davmail.smtp.SmtpServer;
|
import davmail.smtp.SmtpServer;
|
||||||
import davmail.tray.DavGatewayTray;
|
import davmail.tray.DavGatewayTray;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import org.apache.commons.httpclient.HttpClient;
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
import org.apache.commons.httpclient.HttpStatus;
|
import org.apache.commons.httpclient.HttpStatus;
|
||||||
import org.apache.commons.httpclient.methods.GetMethod;
|
import org.apache.commons.httpclient.methods.GetMethod;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DavGateway main class
|
* DavGateway main class
|
||||||
*/
|
*/
|
||||||
@ -23,6 +23,7 @@ public class DavGateway {
|
|||||||
|
|
||||||
private static SmtpServer smtpServer;
|
private static SmtpServer smtpServer;
|
||||||
private static PopServer popServer;
|
private static PopServer popServer;
|
||||||
|
private static CaldavServer caldavServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the gateway, listen on spécified smtp and pop3 ports
|
* Start the gateway, listen on spécified smtp and pop3 ports
|
||||||
@ -52,14 +53,21 @@ public class DavGateway {
|
|||||||
if (popPort == 0) {
|
if (popPort == 0) {
|
||||||
popPort = PopServer.DEFAULT_PORT;
|
popPort = PopServer.DEFAULT_PORT;
|
||||||
}
|
}
|
||||||
|
int caldavPort = Settings.getIntProperty("davmail.caldavPort");
|
||||||
|
if (caldavPort == 0) {
|
||||||
|
caldavPort = CaldavServer.DEFAULT_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
smtpServer = new SmtpServer(smtpPort);
|
smtpServer = new SmtpServer(smtpPort);
|
||||||
popServer = new PopServer(popPort);
|
popServer = new PopServer(popPort);
|
||||||
|
caldavServer = new CaldavServer(caldavPort);
|
||||||
smtpServer.start();
|
smtpServer.start();
|
||||||
popServer.start();
|
popServer.start();
|
||||||
|
caldavServer.start();
|
||||||
|
|
||||||
String message = "DavMail gateway listening on SMTP port " + smtpPort +
|
String message = "DavMail gateway listening on SMTP port " + smtpPort +
|
||||||
|
", Caldav port " + caldavPort +
|
||||||
" and POP port " + popPort;
|
" and POP port " + popPort;
|
||||||
String releasedVersion = getReleasedVersion();
|
String releasedVersion = getReleasedVersion();
|
||||||
String currentVersion = getCurrentVersion();
|
String currentVersion = getCurrentVersion();
|
||||||
@ -93,6 +101,14 @@ public class DavGateway {
|
|||||||
DavGatewayTray.warn("Exception waiting for listener to die", e);
|
DavGatewayTray.warn("Exception waiting for listener to die", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (caldavServer != null) {
|
||||||
|
caldavServer.close();
|
||||||
|
try {
|
||||||
|
caldavServer.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
DavGatewayTray.warn("Exception waiting for listener to die", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCurrentVersion() {
|
public static String getCurrentVersion() {
|
||||||
|
@ -49,6 +49,7 @@ public class Settings {
|
|||||||
SETTINGS.put("davmail.url", "http://exchangeServer/exchange/");
|
SETTINGS.put("davmail.url", "http://exchangeServer/exchange/");
|
||||||
SETTINGS.put("davmail.popPort", "1110");
|
SETTINGS.put("davmail.popPort", "1110");
|
||||||
SETTINGS.put("davmail.smtpPort", "1025");
|
SETTINGS.put("davmail.smtpPort", "1025");
|
||||||
|
SETTINGS.put("davmail.caldavPort", "1080");
|
||||||
SETTINGS.put("davmail.keepDelay", "30");
|
SETTINGS.put("davmail.keepDelay", "30");
|
||||||
SETTINGS.put("davmail.allowRemote", "false");
|
SETTINGS.put("davmail.allowRemote", "false");
|
||||||
SETTINGS.put("davmail.bindAddress", "");
|
SETTINGS.put("davmail.bindAddress", "");
|
||||||
|
462
src/java/davmail/caldav/CaldavConnection.java
Normal file
462
src/java/davmail/caldav/CaldavConnection.java
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
package davmail.caldav;
|
||||||
|
|
||||||
|
import davmail.AbstractConnection;
|
||||||
|
import davmail.Settings;
|
||||||
|
import davmail.exchange.ExchangeSession;
|
||||||
|
import davmail.exchange.ExchangeSessionFactory;
|
||||||
|
import davmail.exchange.NetworkDownException;
|
||||||
|
import davmail.tray.DavGatewayTray;
|
||||||
|
import org.apache.commons.httpclient.HttpException;
|
||||||
|
import org.apache.commons.httpclient.HttpStatus;
|
||||||
|
import org.apache.commons.httpclient.util.URIUtil;
|
||||||
|
|
||||||
|
import javax.xml.stream.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a caldav connection.
|
||||||
|
*/
|
||||||
|
public class CaldavConnection extends AbstractConnection {
|
||||||
|
protected boolean closed = false;
|
||||||
|
|
||||||
|
// Initialize the streams and start the thread
|
||||||
|
public CaldavConnection(Socket clientSocket) {
|
||||||
|
super("CaldavConnection", clientSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
throw new IOException("Invalid header: " + line);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
throw new IOException("Invalid content length: " + contentLength);
|
||||||
|
}
|
||||||
|
char[] buffer = new char[size];
|
||||||
|
int actualSize = in.read(buffer);
|
||||||
|
if (actualSize < 0) {
|
||||||
|
throw new IOException("End of stream reached reading content");
|
||||||
|
}
|
||||||
|
String result = new String(buffer, 0, actualSize);
|
||||||
|
DavGatewayTray.debug("< " + result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setSocketTimeout(String keepAliveValue) throws IOException {
|
||||||
|
if (keepAliveValue != null || keepAliveValue.length() > 0) {
|
||||||
|
int keepAlive;
|
||||||
|
try {
|
||||||
|
keepAlive = Integer.parseInt(keepAliveValue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IOException("Invalid Keep-Alive: " + keepAliveValue);
|
||||||
|
}
|
||||||
|
if (keepAlive > 300) {
|
||||||
|
keepAlive = 300;
|
||||||
|
}
|
||||||
|
client.setSoTimeout(keepAlive * 1000);
|
||||||
|
DavGatewayTray.debug("Set socket timeout to " + keepAlive + " seconds");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
String line;
|
||||||
|
StringTokenizer tokens;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!closed) {
|
||||||
|
line = readClient();
|
||||||
|
// unable to read line, connection closed ?
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tokens = new StringTokenizer(line);
|
||||||
|
if (tokens.hasMoreTokens()) {
|
||||||
|
String command = tokens.nextToken();
|
||||||
|
Map<String, String> headers = parseHeaders();
|
||||||
|
if (tokens.hasMoreTokens()) {
|
||||||
|
String path = tokens.nextToken();
|
||||||
|
String content = getContent(headers.get("content-length"));
|
||||||
|
setSocketTimeout(headers.get("keep-alive"));
|
||||||
|
if ("OPTIONS".equals(command)) {
|
||||||
|
sendOptions();
|
||||||
|
} else if (!headers.containsKey("authorization")) {
|
||||||
|
sendUnauthorized();
|
||||||
|
} else {
|
||||||
|
decodeCredentials(headers.get("authorization"));
|
||||||
|
// authenticate only once
|
||||||
|
if (session == null) {
|
||||||
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||||
|
}
|
||||||
|
handleRequest(command, path, headers, content);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendErr(HttpStatus.SC_NOT_IMPLEMENTED, "Invalid URI");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
DavGatewayTray.debug("Closing connection on timeout");
|
||||||
|
} catch (IOException e) {
|
||||||
|
DavGatewayTray.error(e);
|
||||||
|
try {
|
||||||
|
sendErr(HttpStatus.SC_INTERNAL_SERVER_ERROR, e);
|
||||||
|
} catch (IOException e2) {
|
||||||
|
DavGatewayTray.debug("Exception sending error to client", e2);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
DavGatewayTray.resetIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getDepth(Map<String, String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleRequest(String command, String path, Map<String, String> headers, String body) throws IOException {
|
||||||
|
int depth = getDepth(headers);
|
||||||
|
if ("OPTIONS".equals(command)) {
|
||||||
|
sendOptions();
|
||||||
|
} else if ("PROPFIND".equals(command) && "/user/".equals(path)) {
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
buffer.append("<D:multistatus xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n");
|
||||||
|
buffer.append(" <D:response>\n");
|
||||||
|
buffer.append(" <D:href>/user</D:href>\n");
|
||||||
|
buffer.append(" <D:propstat>\n");
|
||||||
|
buffer.append(" <D:prop>\n");
|
||||||
|
buffer.append(" <C:calendar-home-set>\n");
|
||||||
|
buffer.append(" <D:href>/calendar</D:href>\n");
|
||||||
|
buffer.append(" </C:calendar-home-set>");
|
||||||
|
|
||||||
|
buffer.append(" <C:calendar-user-address-set>\n");
|
||||||
|
// TODO
|
||||||
|
buffer.append(" <D:href>mailto:" + session.getEmail() + "</D:href>\n");
|
||||||
|
buffer.append(" </C:calendar-user-address-set>");
|
||||||
|
|
||||||
|
buffer.append(" <C:schedule-inbox-URL>\n");
|
||||||
|
buffer.append(" <D:href>/inbox</D:href>\n");
|
||||||
|
buffer.append(" </C:schedule-inbox-URL>");
|
||||||
|
|
||||||
|
buffer.append(" <C:schedule-outbox-URL>\n");
|
||||||
|
buffer.append(" <D:href>/outbox</D:href>\n");
|
||||||
|
buffer.append(" </C:schedule-outbox-URL>");
|
||||||
|
|
||||||
|
buffer.append(" </D:prop>\n");
|
||||||
|
buffer.append(" <D:status>HTTP/1.1 200 OK</D:status>\n");
|
||||||
|
buffer.append(" </D:propstat>\n");
|
||||||
|
buffer.append(" </D:response>\n");
|
||||||
|
buffer.append("</D:multistatus>\n");
|
||||||
|
sendHttpResponse(HttpStatus.SC_MULTI_STATUS, null, "text/xml;charset=UTF-8", buffer.toString(), true);
|
||||||
|
} else if ("PROPFIND".equals(command) && "/calendar/".equals(path)) {
|
||||||
|
if (depth != 0 || body == null) {
|
||||||
|
throw new IOException("Unsupported operation: " + command + " " + path);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
buffer.append("<D:multistatus xmlns:D=\"DAV:\" xmlns:CS=\"http://calendarserver.org/ns/\">\n");
|
||||||
|
buffer.append(" <D:response>\n");
|
||||||
|
buffer.append(" <D:href>/calendar</D:href>\n");
|
||||||
|
buffer.append(" <D:propstat>\n");
|
||||||
|
buffer.append(" <D:prop>\n");
|
||||||
|
// TODO : parse request
|
||||||
|
if (body.indexOf("resourcetype") >= 0) {
|
||||||
|
buffer.append(" <D:resourcetype>\n");
|
||||||
|
buffer.append(" <D:collection/>\n");
|
||||||
|
buffer.append(" <C:calendar xmlns:C=\"urn:ietf:params:xml:ns:caldav\"/>\n");
|
||||||
|
buffer.append(" </D:resourcetype>\n");
|
||||||
|
}
|
||||||
|
if (body.indexOf("owner") >= 0) {
|
||||||
|
buffer.append(" <D:owner>\n");
|
||||||
|
buffer.append(" <D:href>/user</D:href>\n");
|
||||||
|
buffer.append(" </D:owner>\n");
|
||||||
|
}
|
||||||
|
if (body.indexOf("getctag") >= 0) {
|
||||||
|
buffer.append(" <CS:getctag>")
|
||||||
|
.append(base64Encode(session.getCalendarEtag()))
|
||||||
|
.append("</CS:getctag>\n");
|
||||||
|
}
|
||||||
|
buffer.append(" </D:prop>\n");
|
||||||
|
buffer.append(" <D:status>HTTP/1.1 200 OK</D:status>\n");
|
||||||
|
buffer.append(" </D:propstat>\n");
|
||||||
|
buffer.append(" </D:response>\n");
|
||||||
|
buffer.append("</D:multistatus>\n");
|
||||||
|
|
||||||
|
HashMap<String, String> responseHeaders = new HashMap<String, String>();
|
||||||
|
sendHttpResponse(HttpStatus.SC_MULTI_STATUS, responseHeaders, "text/xml;charset=UTF-8", buffer.toString(), true);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
sendUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ("REPORT".equals(command)) {
|
||||||
|
if (!"/calendar/".equals(path) || depth != 1 || body == null) {
|
||||||
|
throw new IOException("Unsupported operation: " + command + " " + path);
|
||||||
|
}
|
||||||
|
HashSet<String> properties = new HashSet<String>();
|
||||||
|
// TODO : parse body
|
||||||
|
if (body.indexOf("D:getetag") >= 0) {
|
||||||
|
properties.add("getetag");
|
||||||
|
}
|
||||||
|
if (body.indexOf("calendar-data") >= 0) {
|
||||||
|
properties.add("calendar-data");
|
||||||
|
}
|
||||||
|
List<ExchangeSession.Event> events;
|
||||||
|
List<String> notFound = new ArrayList<String>();
|
||||||
|
if (body.indexOf("calendar-multiget") >= 0) {
|
||||||
|
events = new ArrayList<ExchangeSession.Event>();
|
||||||
|
try {
|
||||||
|
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
||||||
|
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
|
||||||
|
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
|
||||||
|
|
||||||
|
XMLStreamReader reader = inputFactory.createXMLStreamReader(new StringReader(body));
|
||||||
|
boolean inHref = false;
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
int event = reader.next();
|
||||||
|
if (event == XMLStreamConstants.START_ELEMENT && "href".equals(reader.getLocalName())) {
|
||||||
|
inHref = true;
|
||||||
|
} else if (event == XMLStreamConstants.CHARACTERS && inHref) {
|
||||||
|
try {
|
||||||
|
events.add(session.getEvent(URIUtil.decode(reader.getText().substring("/calendar/".length()))));
|
||||||
|
} catch (HttpException e) {
|
||||||
|
notFound.add(reader.getText().substring("/calendar/".length()));
|
||||||
|
}
|
||||||
|
inHref = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XMLStreamException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
events = session.getAllEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<D:multistatus xmlns:D=\"DAV:\">\n");
|
||||||
|
for (ExchangeSession.Event event : events) {
|
||||||
|
|
||||||
|
String eventPath = event.getPath().replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
||||||
|
buffer.append("<D:response>\n");
|
||||||
|
buffer.append(" <D:href>/calendar").append(eventPath).append("</D:href>\n");
|
||||||
|
buffer.append(" <D:propstat>\n");
|
||||||
|
buffer.append(" <D:prop>\n");
|
||||||
|
if (properties.contains("calendar-data")) {
|
||||||
|
String ics = event.getICS();
|
||||||
|
if (ics != null && ics.length() > 0) {
|
||||||
|
ics = ics.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
||||||
|
buffer.append(" <C:calendar-data xmlns:C=\"urn:ietf:params:xml:ns:caldav\"\n");
|
||||||
|
buffer.append(" C:content-type=\"text/calendar\" C:version=\"2.0\">");
|
||||||
|
buffer.append(ics);
|
||||||
|
buffer.append("</C:calendar-data>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (properties.contains("getetag")) {
|
||||||
|
buffer.append(" <D:getetag>").append(event.getEtag()).append("</D:getetag>\n");
|
||||||
|
}
|
||||||
|
buffer.append(" </D:prop>\n");
|
||||||
|
buffer.append(" <D:status>HTTP/1.1 200 OK</D:status>\n");
|
||||||
|
buffer.append(" </D:propstat>\n");
|
||||||
|
if (notFound.size() > 0) {
|
||||||
|
buffer.append(" <D:propstat>\n");
|
||||||
|
for (String href : notFound) {
|
||||||
|
buffer.append(" <D:href>").append(href).append("</D:href>\n");
|
||||||
|
}
|
||||||
|
buffer.append(" <D:status>HTTP/1.1 404 Not Found</D:status>\n");
|
||||||
|
buffer.append(" </D:propstat>\n");
|
||||||
|
}
|
||||||
|
buffer.append(" </D:response>").append((char) 13).append((char) 10);
|
||||||
|
|
||||||
|
}
|
||||||
|
buffer.append("</D:multistatus>");
|
||||||
|
|
||||||
|
// TODO : remove
|
||||||
|
sendHttpResponse(HttpStatus.SC_MULTI_STATUS, null, "text/xml;charset=UTF-8", buffer.toString(), true);
|
||||||
|
} else if ("PUT".equals(command) && path.startsWith("/calendar/")) {
|
||||||
|
String etag = headers.get("if-match");
|
||||||
|
int status = session.createOrUpdateEvent(path.substring("/calendar/".length()), body, etag);
|
||||||
|
sendHttpResponse(status, true);
|
||||||
|
|
||||||
|
} else if ("POST".equals(command) && path.startsWith("/outbox")) {
|
||||||
|
Map<String,String> valueMap = new HashMap<String,String>();
|
||||||
|
Map<String,String> keyMap = new HashMap<String,String>();
|
||||||
|
BufferedReader reader = new BufferedReader(new StringReader(body));
|
||||||
|
String line;
|
||||||
|
String key = null;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.startsWith(" ") & "ATTENDEE".equals(key)) {
|
||||||
|
valueMap.put(key, valueMap.get(key)+line.substring(1));
|
||||||
|
} else {
|
||||||
|
int index = line.indexOf(':');
|
||||||
|
if (index <= 0) {
|
||||||
|
throw new IOException("Invalid request: " + body);
|
||||||
|
}
|
||||||
|
String fullkey = line.substring(0, index);
|
||||||
|
String value = line.substring(index+1);
|
||||||
|
int semicolonIndex = fullkey.indexOf(";");
|
||||||
|
if (semicolonIndex > 0) {
|
||||||
|
key = fullkey.substring(0, semicolonIndex);
|
||||||
|
} else {
|
||||||
|
key = fullkey;
|
||||||
|
}
|
||||||
|
valueMap.put(key, value);
|
||||||
|
keyMap.put(key, fullkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String response = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
|
||||||
|
" <C:schedule-response xmlns:D=\"DAV:\"\n" +
|
||||||
|
" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n" +
|
||||||
|
" <C:response>\n" +
|
||||||
|
" <C:recipient>\n" +
|
||||||
|
" <D:href>"+valueMap.get("ATTENDEE")+"</D:href>\n" +
|
||||||
|
" </C:recipient>\n" +
|
||||||
|
" <C:request-status>2.0;Success</C:request-status>\n" +
|
||||||
|
" <C:calendar-data>BEGIN:VCALENDAR\n" +
|
||||||
|
"VERSION:2.0\n" +
|
||||||
|
"PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\n" +
|
||||||
|
"METHOD:REPLY\n" +
|
||||||
|
"BEGIN:VFREEBUSY\n" +
|
||||||
|
"DTSTAMP:" + valueMap.get("DTSTAMP") + "\n" +
|
||||||
|
"ORGANIZER:" + valueMap.get("ORGANIZER") + "\n" +
|
||||||
|
"DTSTART:" + valueMap.get("DTSTART") + "\n" +
|
||||||
|
"DTEND:" + valueMap.get("DTEND") + "\n" +
|
||||||
|
"UID:" + valueMap.get("UID") + "\n" +
|
||||||
|
keyMap.get("ATTENDEE")+";" + valueMap.get("ATTENDEE") + "\n" +
|
||||||
|
"FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:" + session.getFreebusy(valueMap) + "\n" +
|
||||||
|
"END:VFREEBUSY\n" +
|
||||||
|
"END:VCALENDAR" +
|
||||||
|
"</C:calendar-data>\n" +
|
||||||
|
" </C:response>\n" +
|
||||||
|
" </C:schedule-response>";
|
||||||
|
sendHttpResponse(HttpStatus.SC_OK, null, "text/xml;charset=UTF-8", response, true);
|
||||||
|
|
||||||
|
} else if ("DELETE".equals(command) && path.startsWith("/calendar/")) {
|
||||||
|
int status = session.deleteEvent(path.substring("/calendar/".length()));
|
||||||
|
sendHttpResponse(status, true);
|
||||||
|
} else {
|
||||||
|
sendErr(HttpStatus.SC_BAD_REQUEST, "Unsupported command: " + command);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void sendErr(int status, Exception e) throws IOException {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null) {
|
||||||
|
message = e.toString();
|
||||||
|
}
|
||||||
|
sendErr(status, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErr(int status, String message) throws IOException {
|
||||||
|
sendHttpResponse(status, null, "text/plain;charset=UTF-8", message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendOptions() throws IOException {
|
||||||
|
HashMap<String, String> headers = new HashMap<String, String>();
|
||||||
|
headers.put("Allow", "OPTIONS, GET, PROPFIND, PUT, POST");
|
||||||
|
headers.put("DAV", "1, 2, 3, access-control, calendar-access, ticket, calendar-schedule");
|
||||||
|
sendHttpResponse(HttpStatus.SC_OK, headers, null, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendUnauthorized() throws IOException {
|
||||||
|
HashMap<String, String> headers = new HashMap<String, String>();
|
||||||
|
headers.put("WWW-Authenticate", "Basic realm=\"" + Settings.getProperty("davmail.url") + "\"");
|
||||||
|
sendHttpResponse(HttpStatus.SC_UNAUTHORIZED, headers, null, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendHttpResponse(int status, boolean keepAlive) throws IOException {
|
||||||
|
sendHttpResponse(status, null, null, null, keepAlive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendHttpResponse(int status, Map<String, String> headers, String contentType, String content, boolean keepAlive) throws IOException {
|
||||||
|
sendClient("HTTP/1.1 " + status + " " + HttpStatus.getStatusText(status));
|
||||||
|
sendClient("Server: DavMail Gateway");
|
||||||
|
SimpleDateFormat formatter = new java.text.SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
||||||
|
sendClient("Date: " + formatter.format(new java.util.Date()));
|
||||||
|
if (headers != null) {
|
||||||
|
for (String header : headers.keySet()) {
|
||||||
|
sendClient(header + ": " + headers.get(header));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contentType != null) {
|
||||||
|
sendClient("Content-Type: " + contentType);
|
||||||
|
}
|
||||||
|
sendClient("Connection: " + (keepAlive ? "keep-alive" : "close"));
|
||||||
|
closed = !keepAlive;
|
||||||
|
if (content != null && content.length() > 0) {
|
||||||
|
sendClient("Content-Length: " + content.getBytes("UTF-8").length);
|
||||||
|
} else {
|
||||||
|
sendClient("Content-Length: 0");
|
||||||
|
}
|
||||||
|
sendClient("");
|
||||||
|
if (content != null && content.length() > 0) {
|
||||||
|
sendClient(content.getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode HTTP credentials
|
||||||
|
*
|
||||||
|
* @param authorization http authorization header value
|
||||||
|
* @throws java.io.IOException if invalid credentials
|
||||||
|
*/
|
||||||
|
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)) {
|
||||||
|
throw new IOException("Unsupported authorization mode: " + mode);
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
throw new IOException("Invalid credentials");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IOException("Invalid credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
29
src/java/davmail/caldav/CaldavServer.java
Normal file
29
src/java/davmail/caldav/CaldavServer.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package davmail.caldav;
|
||||||
|
|
||||||
|
import davmail.AbstractServer;
|
||||||
|
import davmail.AbstractConnection;
|
||||||
|
import davmail.pop.PopConnection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calendar server, handle HTTP Caldav requests.
|
||||||
|
*/
|
||||||
|
public class CaldavServer extends AbstractServer {
|
||||||
|
public static final int DEFAULT_PORT = 80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ServerSocket to listen for connections.
|
||||||
|
* Start the thread.
|
||||||
|
* @param port pop listen port, 80 if not defined (0)
|
||||||
|
* @throws java.io.IOException on error
|
||||||
|
*/
|
||||||
|
public CaldavServer(int port) throws IOException {
|
||||||
|
super("CaldavServer", port, CaldavServer.DEFAULT_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
||||||
|
return new CaldavConnection(clientSocket);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package davmail.exchange;
|
package davmail.exchange;
|
||||||
|
|
||||||
import davmail.Settings;
|
import davmail.Settings;
|
||||||
|
import davmail.tray.DavGatewayTray;
|
||||||
import davmail.http.DavGatewayHttpClientFacade;
|
import davmail.http.DavGatewayHttpClientFacade;
|
||||||
import org.apache.commons.httpclient.Header;
|
import org.apache.commons.httpclient.Header;
|
||||||
import org.apache.commons.httpclient.HttpClient;
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
@ -18,24 +19,20 @@ import org.apache.log4j.Logger;
|
|||||||
import org.apache.webdav.lib.Property;
|
import org.apache.webdav.lib.Property;
|
||||||
import org.apache.webdav.lib.ResponseEntity;
|
import org.apache.webdav.lib.ResponseEntity;
|
||||||
import org.apache.webdav.lib.WebdavResource;
|
import org.apache.webdav.lib.WebdavResource;
|
||||||
|
import org.apache.webdav.lib.methods.SearchMethod;
|
||||||
|
import org.apache.webdav.lib.methods.PropFindMethod;
|
||||||
|
|
||||||
import javax.mail.internet.MimeUtility;
|
import javax.mail.internet.MimeUtility;
|
||||||
import java.io.BufferedReader;
|
import javax.xml.stream.XMLStreamReader;
|
||||||
import java.io.IOException;
|
import javax.xml.stream.XMLInputFactory;
|
||||||
import java.io.InputStreamReader;
|
import javax.xml.stream.XMLStreamException;
|
||||||
import java.io.OutputStream;
|
import javax.xml.stream.XMLStreamConstants;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.*;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.SimpleTimeZone;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exchange session through Outlook Web Access (DAV)
|
* Exchange session through Outlook Web Access (DAV)
|
||||||
@ -75,6 +72,7 @@ public class ExchangeSession {
|
|||||||
private String deleteditemsUrl;
|
private String deleteditemsUrl;
|
||||||
private String sendmsgUrl;
|
private String sendmsgUrl;
|
||||||
private String draftsUrl;
|
private String draftsUrl;
|
||||||
|
private String calendarUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base user mailboxes path (used to select folder)
|
* Base user mailboxes path (used to select folder)
|
||||||
@ -92,6 +90,7 @@ public class ExchangeSession {
|
|||||||
// each session
|
// each session
|
||||||
dateParser = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
dateParser = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||||
dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||||
|
LOGGER.debug("Session " + this + " created");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,6 +228,7 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void login(String userName, String password) throws IOException {
|
void login(String userName, String password) throws IOException {
|
||||||
|
LOGGER.debug("Session " + this + " login");
|
||||||
try {
|
try {
|
||||||
baseUrl = Settings.getProperty("davmail.url");
|
baseUrl = Settings.getProperty("davmail.url");
|
||||||
|
|
||||||
@ -280,7 +280,12 @@ public class ExchangeSession {
|
|||||||
String queryString = method.getQueryString();
|
String queryString = method.getQueryString();
|
||||||
if (queryString != null && queryString.endsWith("reason=2")) {
|
if (queryString != null && queryString.endsWith("reason=2")) {
|
||||||
method.releaseConnection();
|
method.releaseConnection();
|
||||||
throw new HttpException("Authentication failed: invalid user or password");
|
if (userName != null && userName.contains("\\")) {
|
||||||
|
throw new HttpException("Authentication failed: invalid user or password");
|
||||||
|
} else {
|
||||||
|
throw new HttpException("Authentication failed: invalid user or password, " +
|
||||||
|
"retry with domain\\user");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mailPath = getMailPath(method);
|
mailPath = getMailPath(method);
|
||||||
@ -298,6 +303,7 @@ public class ExchangeSession {
|
|||||||
reqProps.add("urn:schemas:httpmail:deleteditems");
|
reqProps.add("urn:schemas:httpmail:deleteditems");
|
||||||
reqProps.add("urn:schemas:httpmail:sendmsg");
|
reqProps.add("urn:schemas:httpmail:sendmsg");
|
||||||
reqProps.add("urn:schemas:httpmail:drafts");
|
reqProps.add("urn:schemas:httpmail:drafts");
|
||||||
|
reqProps.add("urn:schemas:httpmail:calendar");
|
||||||
|
|
||||||
Enumeration foldersEnum = wdr.propfindMethod(0, reqProps);
|
Enumeration foldersEnum = wdr.propfindMethod(0, reqProps);
|
||||||
if (!foldersEnum.hasMoreElements()) {
|
if (!foldersEnum.hasMoreElements()) {
|
||||||
@ -326,6 +332,10 @@ public class ExchangeSession {
|
|||||||
draftsUrl = URIUtil.decode(inboxProp.
|
draftsUrl = URIUtil.decode(inboxProp.
|
||||||
getPropertyAsString());
|
getPropertyAsString());
|
||||||
}
|
}
|
||||||
|
if ("calendar".equals(inboxProp.getLocalName())) {
|
||||||
|
calendarUrl = URIUtil.decode(inboxProp.
|
||||||
|
getPropertyAsString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set current folder to Inbox
|
// set current folder to Inbox
|
||||||
@ -378,6 +388,7 @@ public class ExchangeSession {
|
|||||||
* @throws IOException if unable to close Webdav context
|
* @throws IOException if unable to close Webdav context
|
||||||
*/
|
*/
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
LOGGER.debug("Session " + this + " closed");
|
||||||
wdr.close();
|
wdr.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,15 +416,20 @@ public class ExchangeSession {
|
|||||||
String messageUrl = URIUtil.encodePathQuery(folderUrl + "/" + subject + ".EML");
|
String messageUrl = URIUtil.encodePathQuery(folderUrl + "/" + subject + ".EML");
|
||||||
|
|
||||||
PutMethod putmethod = new PutMethod(messageUrl);
|
PutMethod putmethod = new PutMethod(messageUrl);
|
||||||
|
// TODO : test, bcc ?
|
||||||
|
putmethod.setRequestHeader("Translate", "f");
|
||||||
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
||||||
putmethod.setRequestBody(messageBody);
|
putmethod.setRequestBody(messageBody);
|
||||||
|
try {
|
||||||
|
int code = wdr.retrieveSessionInstance().executeMethod(putmethod);
|
||||||
|
|
||||||
int code = wdr.retrieveSessionInstance().executeMethod(putmethod);
|
if (code == HttpURLConnection.HTTP_OK) {
|
||||||
|
LOGGER.warn("Overwritten message " + messageUrl);
|
||||||
if (code == HttpURLConnection.HTTP_OK) {
|
} else if (code != HttpURLConnection.HTTP_CREATED) {
|
||||||
LOGGER.warn("Overwritten message " + messageUrl);
|
throw new IOException("Unable to create message " + code + " " + putmethod.getStatusLine());
|
||||||
} else if (code != HttpURLConnection.HTTP_CREATED) {
|
}
|
||||||
throw new IOException("Unable to create message " + code + " " + putmethod.getStatusLine());
|
} finally {
|
||||||
|
putmethod.releaseConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,4 +724,380 @@ public class ExchangeSession {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WebdavResource getWebDavResource() throws IOException {
|
||||||
|
return wdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
protected String href;
|
||||||
|
protected String etag;
|
||||||
|
|
||||||
|
public String getICS() throws IOException {
|
||||||
|
DavGatewayTray.debug("Get event: " + href);
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
GetMethod method = new GetMethod(URIUtil.encodePath(href));
|
||||||
|
method.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
|
||||||
|
method.setRequestHeader("Translate", "f");
|
||||||
|
BufferedReader eventReader = null;
|
||||||
|
try {
|
||||||
|
int status = wdr.retrieveSessionInstance().executeMethod(method);
|
||||||
|
if (status != HttpStatus.SC_OK) {
|
||||||
|
DavGatewayTray.warn("Unable to get event at " + href + " status: " + status);
|
||||||
|
}
|
||||||
|
eventReader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream(), "UTF-8"));
|
||||||
|
String line;
|
||||||
|
boolean inbody = false;
|
||||||
|
while ((line = eventReader.readLine()) != null) {
|
||||||
|
if ("BEGIN:VCALENDAR".equals(line)) {
|
||||||
|
inbody = true;
|
||||||
|
}
|
||||||
|
if (inbody) {
|
||||||
|
buffer.append(line);
|
||||||
|
buffer.append((char) 13);
|
||||||
|
buffer.append((char) 10);
|
||||||
|
}
|
||||||
|
if ("END:VCALENDAR".equals(line)) {
|
||||||
|
inbody = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (eventReader != null) {
|
||||||
|
try {
|
||||||
|
eventReader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Error parsing event at " + method.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() throws URIException {
|
||||||
|
return href.substring(calendarUrl.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEtag() {
|
||||||
|
return etag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Event> getAllEvents() throws IOException {
|
||||||
|
List<Event> events = new ArrayList<Event>();
|
||||||
|
String searchRequest = "<?xml version=\"1.0\"?>\n" +
|
||||||
|
"<d:searchrequest xmlns:d=\"DAV:\">\n" +
|
||||||
|
" <d:sql> Select \"DAV:getetag\"" +
|
||||||
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + calendarUrl + "\"')\n" +
|
||||||
|
" WHERE NOT \"urn:schemas:calendar:instancetype\" = 1\n" +
|
||||||
|
" AND \"DAV:contentclass\" = 'urn:content-classes:appointment'\n" +
|
||||||
|
" AND \"urn:schemas:calendar:dtstart\" > '2008/11/01 00:00:00'\n" +
|
||||||
|
" ORDER BY \"urn:schemas:calendar:dtstart\" ASC\n" +
|
||||||
|
" </d:sql>\n" +
|
||||||
|
"</d:searchrequest>";
|
||||||
|
SearchMethod searchMethod = new SearchMethod(calendarUrl, searchRequest);
|
||||||
|
try {
|
||||||
|
int status = wdr.retrieveSessionInstance().executeMethod(searchMethod);
|
||||||
|
// Also accept OK sent by buggy servers.
|
||||||
|
if (status != HttpStatus.SC_MULTI_STATUS
|
||||||
|
&& status != HttpStatus.SC_OK) {
|
||||||
|
HttpException ex = new HttpException();
|
||||||
|
ex.setReasonCode(status);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration calendarEnum = searchMethod.getResponses();
|
||||||
|
while (calendarEnum.hasMoreElements()) {
|
||||||
|
ResponseEntity calendarResponse = (ResponseEntity) calendarEnum.
|
||||||
|
nextElement();
|
||||||
|
String href = calendarResponse.getHref();
|
||||||
|
Event event = new Event();
|
||||||
|
event.href = URIUtil.decode(href);
|
||||||
|
String contentclass = null;
|
||||||
|
Enumeration propertiesEnumeration = calendarResponse.getProperties();
|
||||||
|
while (propertiesEnumeration.hasMoreElements()) {
|
||||||
|
Property property = (Property) propertiesEnumeration.nextElement();
|
||||||
|
if ("getetag".equals(property.getLocalName())) {
|
||||||
|
event.etag = property.getPropertyAsString();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if ("contentclass".equals(property.getLocalName())) {
|
||||||
|
contentclass = property.getPropertyAsString();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
// filter folder and non appointment objects
|
||||||
|
//if ("urn:content-classes:appointment".equals(contentclass)) {
|
||||||
|
events.add(event);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
searchMethod.releaseConnection();
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event getEvent(String path) throws IOException {
|
||||||
|
// TODO : refactor with getAllEvents
|
||||||
|
Event event = new Event();
|
||||||
|
final Vector<String> EVENT_REQUEST_PROPERTIES = new Vector<String>();
|
||||||
|
EVENT_REQUEST_PROPERTIES.add("DAV:getetag");
|
||||||
|
|
||||||
|
//wdr.setDebug(4);
|
||||||
|
Enumeration calendarEnum = wdr.propfindMethod(calendarUrl + "/" + path, 0, EVENT_REQUEST_PROPERTIES);
|
||||||
|
//wdr.setDebug(0);
|
||||||
|
if (!calendarEnum.hasMoreElements()) {
|
||||||
|
throw new IOException("Unable to get calendar event");
|
||||||
|
}
|
||||||
|
ResponseEntity calendarResponse = (ResponseEntity) calendarEnum.
|
||||||
|
nextElement();
|
||||||
|
String href = calendarResponse.getHref();
|
||||||
|
event.href = URIUtil.decode(href);
|
||||||
|
Enumeration propertiesEnumeration = calendarResponse.getProperties();
|
||||||
|
while (propertiesEnumeration.hasMoreElements()) {
|
||||||
|
Property property = (Property) propertiesEnumeration.nextElement();
|
||||||
|
if ("getetag".equals(property.getLocalName())) {
|
||||||
|
event.etag = property.getPropertyAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int createOrUpdateEvent(String path, String icsBody, String etag) throws IOException {
|
||||||
|
String messageUrl = URIUtil.encodePathQuery(calendarUrl + "/" + URIUtil.decode(path));
|
||||||
|
String uid = path.substring(0, path.lastIndexOf("."));
|
||||||
|
PutMethod putmethod = new PutMethod(messageUrl);
|
||||||
|
putmethod.setRequestHeader("Translate", "f");
|
||||||
|
putmethod.setRequestHeader("Overwrite", "f");
|
||||||
|
if (etag != null) {
|
||||||
|
// TODO
|
||||||
|
putmethod.setRequestHeader("If-Match", etag);
|
||||||
|
}
|
||||||
|
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
||||||
|
StringBuilder body = new StringBuilder();
|
||||||
|
body.append("Content-Transfer-Encoding: 7bit\n" +
|
||||||
|
"Content-class: urn:content-classes:appointment\n" +
|
||||||
|
"MIME-Version: 1.0\n" +
|
||||||
|
"Content-Type: multipart/alternative;\n" +
|
||||||
|
"\tboundary=\"----=_NextPart_" + uid + "\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"This is a multi-part message in MIME format.\n" +
|
||||||
|
"\n" +
|
||||||
|
"------=_NextPart_" + uid + "\n" +
|
||||||
|
"Content-class: urn:content-classes:appointment\n" +
|
||||||
|
"Content-Type: text/calendar;\n" +
|
||||||
|
"\tmethod=REQUEST;\n" +
|
||||||
|
"\tcharset=\"utf-8\"\n" +
|
||||||
|
"Content-Transfer-Encoding: 8bit\n\n");
|
||||||
|
body.append(new String(icsBody.getBytes("UTF-8"), "ISO-8859-1"));
|
||||||
|
body.append("------=_NextPart_" + uid + "--\n");
|
||||||
|
putmethod.setRequestBody(body.toString());
|
||||||
|
int status;
|
||||||
|
try {
|
||||||
|
status = wdr.retrieveSessionInstance().executeMethod(putmethod);
|
||||||
|
|
||||||
|
if (status == HttpURLConnection.HTTP_OK) {
|
||||||
|
LOGGER.warn("Overwritten event " + messageUrl);
|
||||||
|
} else if (status != HttpURLConnection.HTTP_CREATED) {
|
||||||
|
throw new IOException("Unable to create message " + status + " " + putmethod.getStatusLine());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
putmethod.releaseConnection();
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int deleteEvent(String path) throws IOException {
|
||||||
|
//wdr.setDebug(4);
|
||||||
|
wdr.deleteMethod(calendarUrl + "/" + path);
|
||||||
|
//wdr.setDebug(0);
|
||||||
|
int status = wdr.getStatusCode();
|
||||||
|
if (status == HttpStatus.SC_NOT_FOUND) {
|
||||||
|
status = HttpStatus.SC_OK;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCalendarEtag() throws IOException {
|
||||||
|
String etag = null;
|
||||||
|
//wdr.setDebug(4);
|
||||||
|
Enumeration calendarEnum = wdr.propfindMethod(calendarUrl, 0);
|
||||||
|
//wdr.setDebug(0);
|
||||||
|
if (!calendarEnum.hasMoreElements()) {
|
||||||
|
throw new IOException("Unable to get calendar object");
|
||||||
|
}
|
||||||
|
while (calendarEnum.hasMoreElements()) {
|
||||||
|
ResponseEntity calendarResponse = (ResponseEntity) calendarEnum.
|
||||||
|
nextElement();
|
||||||
|
Enumeration propertiesEnumeration = calendarResponse.getProperties();
|
||||||
|
while (propertiesEnumeration.hasMoreElements()) {
|
||||||
|
Property property = (Property) propertiesEnumeration.nextElement();
|
||||||
|
if ("http://schemas.microsoft.com/repl/".equals(property.getNamespaceURI())
|
||||||
|
&& "contenttag".equals(property.getLocalName())) {
|
||||||
|
etag = property.getPropertyAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (etag == null) {
|
||||||
|
throw new IOException("Unable to get calendar etag");
|
||||||
|
}
|
||||||
|
return etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current Exchange user name
|
||||||
|
*
|
||||||
|
* @return user name
|
||||||
|
* @throws java.io.IOException on error
|
||||||
|
*/
|
||||||
|
public String getUserName() throws IOException {
|
||||||
|
int index = mailPath.lastIndexOf("/", mailPath.length() - 2);
|
||||||
|
if (index >= 0 && mailPath.endsWith("/")) {
|
||||||
|
return mailPath.substring(index + 1, mailPath.length() - 1);
|
||||||
|
} else {
|
||||||
|
throw new IOException("Invalid mail path: " + mailPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current user email
|
||||||
|
*
|
||||||
|
* @return user email
|
||||||
|
* @throws java.io.IOException on error
|
||||||
|
*/
|
||||||
|
public String getEmail() throws IOException {
|
||||||
|
String email = null;
|
||||||
|
GetMethod getMethod = new GetMethod("/public/?Cmd=galfind&AN=" + getUserName());
|
||||||
|
// force XML response with Internet Explorer header
|
||||||
|
getMethod.setRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)");
|
||||||
|
XMLStreamReader reader = null;
|
||||||
|
try {
|
||||||
|
int status = wdr.retrieveSessionInstance().executeMethod(getMethod);
|
||||||
|
if (status != HttpStatus.SC_OK) {
|
||||||
|
throw new IOException("Unable to get user email from: " + getMethod.getPath());
|
||||||
|
}
|
||||||
|
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
||||||
|
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
|
||||||
|
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
|
||||||
|
|
||||||
|
reader = inputFactory.createXMLStreamReader(getMethod.getResponseBodyAsStream());
|
||||||
|
boolean inEM = false;
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
int event = reader.next();
|
||||||
|
if (event == XMLStreamConstants.START_ELEMENT && "EM".equals(reader.getLocalName())) {
|
||||||
|
inEM = true;
|
||||||
|
} else if (event == XMLStreamConstants.CHARACTERS && inEM) {
|
||||||
|
email = reader.getText();
|
||||||
|
inEM = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XMLStreamException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (XMLStreamException e) {
|
||||||
|
LOGGER.error(e);
|
||||||
|
}
|
||||||
|
getMethod.releaseConnection();
|
||||||
|
}
|
||||||
|
if (email == null) {
|
||||||
|
throw new IOException("Unable to get user email from: " + getMethod.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFreebusy(Map<String, String> valueMap) throws IOException {
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
String startDateValue = valueMap.get("DTSTART");
|
||||||
|
String endDateValue = valueMap.get("DTEND");
|
||||||
|
String attendee = valueMap.get("ATTENDEE");
|
||||||
|
if (attendee.startsWith("mailto:")) {
|
||||||
|
attendee = attendee.substring("mailto:".length());
|
||||||
|
}
|
||||||
|
int interval = 30;
|
||||||
|
|
||||||
|
SimpleDateFormat icalParser = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
|
||||||
|
icalParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||||
|
|
||||||
|
SimpleDateFormat shortIcalParser = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
shortIcalParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||||
|
|
||||||
|
SimpleDateFormat owaFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
owaFormatter.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||||
|
|
||||||
|
String url = null;
|
||||||
|
Date startDate = null;
|
||||||
|
Date endDate = null;
|
||||||
|
try {
|
||||||
|
if (startDateValue.length() == 8) {
|
||||||
|
startDate = shortIcalParser.parse(startDateValue);
|
||||||
|
} else {
|
||||||
|
startDate = icalParser.parse(startDateValue);
|
||||||
|
}
|
||||||
|
if (endDateValue.length() == 8) {
|
||||||
|
endDate = shortIcalParser.parse(endDateValue);
|
||||||
|
} else {
|
||||||
|
endDate = icalParser.parse(endDateValue);
|
||||||
|
}
|
||||||
|
url = "/public/?cmd=freebusy" +
|
||||||
|
"&start=" + owaFormatter.format(startDate) +
|
||||||
|
"&end=" + owaFormatter.format(endDate) +
|
||||||
|
"&interval=" + interval +
|
||||||
|
"&u=SMTP:" + attendee;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
GetMethod getMethod = new GetMethod(url);
|
||||||
|
getMethod.setRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)");
|
||||||
|
getMethod.setRequestHeader("Content-Type", "text/xml");
|
||||||
|
|
||||||
|
try {
|
||||||
|
int status = wdr.retrieveSessionInstance().executeMethod(getMethod);
|
||||||
|
if (status != HttpStatus.SC_OK) {
|
||||||
|
throw new IOException("Unable to get free-busy from: " + getMethod.getPath());
|
||||||
|
}
|
||||||
|
// TODO : parse
|
||||||
|
String body = getMethod.getResponseBodyAsString();
|
||||||
|
int startIndex = body.lastIndexOf("<a:fbdata>");
|
||||||
|
int endIndex = body.lastIndexOf("</a:fbdata>");
|
||||||
|
if (startIndex >= 0 && endIndex >= 0) {
|
||||||
|
String fbdata = body.substring(startIndex + "<a:fbdata>".length(), endIndex);
|
||||||
|
Calendar currentCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||||
|
currentCal.setTime(startDate);
|
||||||
|
|
||||||
|
StringBuilder busyBuffer = new StringBuilder();
|
||||||
|
boolean isBusy = fbdata.charAt(0) != '0';
|
||||||
|
if (isBusy) {
|
||||||
|
busyBuffer.append(icalParser.format(currentCal.getTime()));
|
||||||
|
}
|
||||||
|
for (int i = 1; i < fbdata.length(); i++) {
|
||||||
|
currentCal.add(Calendar.MINUTE, interval);
|
||||||
|
if (isBusy && fbdata.charAt(i) == '0') {
|
||||||
|
// busy -> non busy
|
||||||
|
busyBuffer.append('/').append(icalParser.format(currentCal.getTime()));
|
||||||
|
} else if (!isBusy && fbdata.charAt(i) != '0') {
|
||||||
|
// non busy -> busy
|
||||||
|
if (busyBuffer.length() > 0) {
|
||||||
|
busyBuffer.append(',');
|
||||||
|
}
|
||||||
|
busyBuffer.append(icalParser.format(currentCal.getTime()));
|
||||||
|
}
|
||||||
|
isBusy = fbdata.charAt(i) != '0';
|
||||||
|
}
|
||||||
|
result = busyBuffer.toString();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
getMethod.releaseConnection();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
throw new IOException("Unable to get user email from: " + getMethod.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public class ImapConnection extends AbstractConnection {
|
|||||||
|
|
||||||
// Initialize the streams and start the thread
|
// Initialize the streams and start the thread
|
||||||
public ImapConnection(Socket clientSocket) {
|
public ImapConnection(Socket clientSocket) {
|
||||||
super(clientSocket);
|
super("ImapConnection", clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -18,7 +18,7 @@ public class ImapServer extends AbstractServer {
|
|||||||
* Start the thread.
|
* Start the thread.
|
||||||
*/
|
*/
|
||||||
public ImapServer(int port) throws IOException {
|
public ImapServer(int port) throws IOException {
|
||||||
super(port, ImapServer.DEFAULT_PORT);
|
super("ImapServer", port, ImapServer.DEFAULT_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
||||||
|
@ -25,7 +25,7 @@ public class PopConnection extends AbstractConnection {
|
|||||||
|
|
||||||
// Initialize the streams and start the thread
|
// Initialize the streams and start the thread
|
||||||
public PopConnection(Socket clientSocket) {
|
public PopConnection(Socket clientSocket) {
|
||||||
super(clientSocket);
|
super("PopConnection", clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTotalMessagesLength() {
|
public long getTotalMessagesLength() {
|
||||||
|
@ -19,7 +19,7 @@ public class PopServer extends AbstractServer {
|
|||||||
* @param port pop listen port, 110 if not defined (0)
|
* @param port pop listen port, 110 if not defined (0)
|
||||||
*/
|
*/
|
||||||
public PopServer(int port) throws IOException {
|
public PopServer(int port) throws IOException {
|
||||||
super(port, PopServer.DEFAULT_PORT);
|
super("PopServer", port, PopServer.DEFAULT_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
||||||
|
@ -23,7 +23,7 @@ public class SmtpConnection extends AbstractConnection {
|
|||||||
|
|
||||||
// Initialize the streams and start the thread
|
// Initialize the streams and start the thread
|
||||||
public SmtpConnection(Socket clientSocket) {
|
public SmtpConnection(Socket clientSocket) {
|
||||||
super(clientSocket);
|
super("SmtpConnection", clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -12,9 +12,11 @@ public class SmtpServer extends AbstractServer {
|
|||||||
/**
|
/**
|
||||||
* Create a ServerSocket to listen for connections.
|
* Create a ServerSocket to listen for connections.
|
||||||
* Start the thread.
|
* Start the thread.
|
||||||
|
* @param port smtp port
|
||||||
|
* @throws java.io.IOException on error
|
||||||
*/
|
*/
|
||||||
public SmtpServer(int port) throws IOException {
|
public SmtpServer(int port) throws IOException {
|
||||||
super(port, SmtpServer.DEFAULT_PORT);
|
super("SmtpServer", port, SmtpServer.DEFAULT_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
public AbstractConnection createConnectionHandler(Socket clientSocket) {
|
||||||
|
@ -128,7 +128,7 @@ public class SwtGatewayTray implements DavGatewayTrayInterface {
|
|||||||
DavGatewayTray.warn("Unable to set look and feel");
|
DavGatewayTray.warn("Unable to set look and feel");
|
||||||
}
|
}
|
||||||
|
|
||||||
new Thread() {
|
new Thread("SWT") {
|
||||||
public void run() {
|
public void run() {
|
||||||
display = new Display();
|
display = new Display();
|
||||||
shell = new Shell(display);
|
shell = new Shell(display);
|
||||||
|
@ -21,6 +21,7 @@ public class SettingsFrame extends JFrame {
|
|||||||
protected JTextField urlField;
|
protected JTextField urlField;
|
||||||
protected JTextField popPortField;
|
protected JTextField popPortField;
|
||||||
protected JTextField smtpPortField;
|
protected JTextField smtpPortField;
|
||||||
|
protected JTextField caldavPortField;
|
||||||
protected JTextField keepDelayField;
|
protected JTextField keepDelayField;
|
||||||
|
|
||||||
JCheckBox enableProxyField;
|
JCheckBox enableProxyField;
|
||||||
@ -50,13 +51,14 @@ public class SettingsFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected JPanel getSettingsPanel() {
|
protected JPanel getSettingsPanel() {
|
||||||
JPanel settingsPanel = new JPanel(new GridLayout(4, 2));
|
JPanel settingsPanel = new JPanel(new GridLayout(5, 2));
|
||||||
settingsPanel.setBorder(BorderFactory.createTitledBorder("Gateway"));
|
settingsPanel.setBorder(BorderFactory.createTitledBorder("Gateway"));
|
||||||
|
|
||||||
urlField = new JTextField(Settings.getProperty("davmail.url"), 17);
|
urlField = new JTextField(Settings.getProperty("davmail.url"), 17);
|
||||||
urlField.setToolTipText("Base outlook web access URL");
|
urlField.setToolTipText("Base outlook web access URL");
|
||||||
popPortField = new JTextField(Settings.getProperty("davmail.popPort"), 4);
|
popPortField = new JTextField(Settings.getProperty("davmail.popPort"), 4);
|
||||||
smtpPortField = new JTextField(Settings.getProperty("davmail.smtpPort"), 4);
|
smtpPortField = new JTextField(Settings.getProperty("davmail.smtpPort"), 4);
|
||||||
|
caldavPortField = new JTextField(Settings.getProperty("davmail.caldavPort"), 4);
|
||||||
keepDelayField = new JTextField(Settings.getProperty("davmail.keepDelay"), 4);
|
keepDelayField = new JTextField(Settings.getProperty("davmail.keepDelay"), 4);
|
||||||
keepDelayField.setToolTipText("Number of days to keep messages in trash");
|
keepDelayField.setToolTipText("Number of days to keep messages in trash");
|
||||||
|
|
||||||
@ -64,6 +66,7 @@ public class SettingsFrame extends JFrame {
|
|||||||
addSettingComponent(settingsPanel, "OWA url: ", urlField);
|
addSettingComponent(settingsPanel, "OWA url: ", urlField);
|
||||||
addSettingComponent(settingsPanel, "Local POP port: ", popPortField);
|
addSettingComponent(settingsPanel, "Local POP port: ", popPortField);
|
||||||
addSettingComponent(settingsPanel, "Local SMTP port: ", smtpPortField);
|
addSettingComponent(settingsPanel, "Local SMTP port: ", smtpPortField);
|
||||||
|
addSettingComponent(settingsPanel, "Caldav HTTP port: ", caldavPortField);
|
||||||
addSettingComponent(settingsPanel, "Keep Delay: ", keepDelayField);
|
addSettingComponent(settingsPanel, "Keep Delay: ", keepDelayField);
|
||||||
return settingsPanel;
|
return settingsPanel;
|
||||||
}
|
}
|
||||||
@ -150,6 +153,7 @@ public class SettingsFrame extends JFrame {
|
|||||||
urlField.setText(Settings.getProperty("davmail.url"));
|
urlField.setText(Settings.getProperty("davmail.url"));
|
||||||
popPortField.setText(Settings.getProperty("davmail.popPort"));
|
popPortField.setText(Settings.getProperty("davmail.popPort"));
|
||||||
smtpPortField.setText(Settings.getProperty("davmail.smtpPort"));
|
smtpPortField.setText(Settings.getProperty("davmail.smtpPort"));
|
||||||
|
caldavPortField.setText(Settings.getProperty("davmail.caldavPort"));
|
||||||
keepDelayField.setText(Settings.getProperty("davmail.keepDelay"));
|
keepDelayField.setText(Settings.getProperty("davmail.keepDelay"));
|
||||||
boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy");
|
boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy");
|
||||||
enableProxyField.setSelected(enableProxy);
|
enableProxyField.setSelected(enableProxy);
|
||||||
@ -215,6 +219,7 @@ public class SettingsFrame extends JFrame {
|
|||||||
Settings.setProperty("davmail.url", urlField.getText());
|
Settings.setProperty("davmail.url", urlField.getText());
|
||||||
Settings.setProperty("davmail.popPort", popPortField.getText());
|
Settings.setProperty("davmail.popPort", popPortField.getText());
|
||||||
Settings.setProperty("davmail.smtpPort", smtpPortField.getText());
|
Settings.setProperty("davmail.smtpPort", smtpPortField.getText());
|
||||||
|
Settings.setProperty("davmail.caldavPort", caldavPortField.getText());
|
||||||
Settings.setProperty("davmail.keepDelay", keepDelayField.getText());
|
Settings.setProperty("davmail.keepDelay", keepDelayField.getText());
|
||||||
Settings.setProperty("davmail.enableProxy", String.valueOf(enableProxyField.isSelected()));
|
Settings.setProperty("davmail.enableProxy", String.valueOf(enableProxyField.isSelected()));
|
||||||
Settings.setProperty("davmail.proxyHost", httpProxyField.getText());
|
Settings.setProperty("davmail.proxyHost", httpProxyField.getText());
|
||||||
|
@ -40,6 +40,11 @@
|
|||||||
<td>Local SMTP server port to configure in POP client configuration</td>
|
<td>Local SMTP server port to configure in POP client configuration</td>
|
||||||
<td>25</td>
|
<td>25</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Local Caldav HTTP port</td>
|
||||||
|
<td>Local Caldav server port to configure in Caldav (calendar) client configuration</td>
|
||||||
|
<td>80</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Keep Delay</td>
|
<td>Keep Delay</td>
|
||||||
<td>Number of days to keep messages in Exchange trash folder before actual deletion</td>
|
<td>Number of days to keep messages in Exchange trash folder before actual deletion</td>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user