2006-12-12 18:57:24 -05:00
|
|
|
package davmail.exchange;
|
|
|
|
|
2007-04-26 06:29:11 -04:00
|
|
|
import davmail.Settings;
|
2008-11-26 19:56:28 -05:00
|
|
|
import davmail.tray.DavGatewayTray;
|
2008-11-03 20:47:10 -05:00
|
|
|
import davmail.http.DavGatewayHttpClientFacade;
|
2008-10-31 13:12:30 -04:00
|
|
|
import org.apache.commons.httpclient.Header;
|
|
|
|
import org.apache.commons.httpclient.HttpClient;
|
|
|
|
import org.apache.commons.httpclient.HttpException;
|
|
|
|
import org.apache.commons.httpclient.HttpMethod;
|
|
|
|
import org.apache.commons.httpclient.HttpStatus;
|
|
|
|
import org.apache.commons.httpclient.HttpURL;
|
|
|
|
import org.apache.commons.httpclient.HttpsURL;
|
|
|
|
import org.apache.commons.httpclient.URIException;
|
2006-12-12 18:57:24 -05:00
|
|
|
import org.apache.commons.httpclient.methods.GetMethod;
|
|
|
|
import org.apache.commons.httpclient.methods.PostMethod;
|
|
|
|
import org.apache.commons.httpclient.methods.PutMethod;
|
|
|
|
import org.apache.commons.httpclient.util.URIUtil;
|
2007-04-26 06:29:11 -04:00
|
|
|
import org.apache.log4j.Logger;
|
2006-12-12 18:57:24 -05:00
|
|
|
import org.apache.webdav.lib.Property;
|
|
|
|
import org.apache.webdav.lib.ResponseEntity;
|
|
|
|
import org.apache.webdav.lib.WebdavResource;
|
2008-11-26 19:56:28 -05:00
|
|
|
import org.apache.webdav.lib.methods.SearchMethod;
|
|
|
|
import org.apache.webdav.lib.methods.PropFindMethod;
|
2008-02-05 18:17:31 -05:00
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
import javax.mail.internet.MimeUtility;
|
2008-11-26 19:56:28 -05:00
|
|
|
import javax.xml.stream.XMLStreamReader;
|
|
|
|
import javax.xml.stream.XMLInputFactory;
|
|
|
|
import javax.xml.stream.XMLStreamException;
|
|
|
|
import javax.xml.stream.XMLStreamConstants;
|
|
|
|
import java.io.*;
|
2007-05-09 18:32:01 -04:00
|
|
|
import java.net.HttpURLConnection;
|
2006-12-12 18:57:24 -05:00
|
|
|
import java.net.URL;
|
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.SimpleDateFormat;
|
2008-11-26 19:56:28 -05:00
|
|
|
import java.util.*;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchange session through Outlook Web Access (DAV)
|
|
|
|
*/
|
|
|
|
public class ExchangeSession {
|
2007-05-09 18:32:01 -04:00
|
|
|
protected static final Logger LOGGER = Logger.getLogger("davmail.exchange.ExchangeSession");
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* exchange message properties needed to rebuild mime message
|
|
|
|
*/
|
2007-05-09 18:32:01 -04:00
|
|
|
protected static final Vector<String> MESSAGE_REQUEST_PROPERTIES = new Vector<String>();
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
static {
|
2007-05-09 18:32:01 -04:00
|
|
|
MESSAGE_REQUEST_PROPERTIES.add("DAV:uid");
|
|
|
|
MESSAGE_REQUEST_PROPERTIES.add("urn:schemas:mailheader:content-class");
|
2008-02-05 18:17:31 -05:00
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
// size
|
2007-05-09 18:32:01 -04:00
|
|
|
MESSAGE_REQUEST_PROPERTIES.add("http://schemas.microsoft.com/mapi/proptag/x0e080003");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2007-02-07 06:51:08 -05:00
|
|
|
private static final int DEFAULT_KEEP_DELAY = 30;
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* Date parser from Exchange format
|
|
|
|
*/
|
2007-05-09 18:32:01 -04:00
|
|
|
private final SimpleDateFormat dateParser;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-11-03 20:47:10 -05:00
|
|
|
/**
|
|
|
|
* Base Exchange URL
|
|
|
|
*/
|
|
|
|
private String baseUrl;
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* Various standard mail boxes Urls
|
|
|
|
*/
|
2007-05-09 18:32:01 -04:00
|
|
|
private String inboxUrl;
|
|
|
|
private String deleteditemsUrl;
|
|
|
|
private String sendmsgUrl;
|
|
|
|
private String draftsUrl;
|
2008-11-26 19:56:28 -05:00
|
|
|
private String calendarUrl;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Base user mailboxes path (used to select folder)
|
|
|
|
*/
|
2007-05-09 18:32:01 -04:00
|
|
|
private String mailPath;
|
|
|
|
private String currentFolderUrl;
|
|
|
|
private WebdavResource wdr = null;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an exchange session for the given URL.
|
|
|
|
* The session is not actually established until a call to login()
|
|
|
|
*/
|
2008-10-31 13:12:30 -04:00
|
|
|
ExchangeSession() {
|
2006-12-12 18:57:24 -05:00
|
|
|
// SimpleDateFormat are not thread safe, need to create one instance for
|
|
|
|
// each session
|
|
|
|
dateParser = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
|
|
dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
2008-11-26 19:56:28 -05:00
|
|
|
LOGGER.debug("Session " + this + " created");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2008-01-09 11:33:57 -05:00
|
|
|
/**
|
|
|
|
* Test authentication mode : form based or basic.
|
|
|
|
*
|
|
|
|
* @param url exchange base URL
|
|
|
|
* @return true if basic authentication detected
|
|
|
|
* @throws java.io.IOException unable to connect to exchange
|
|
|
|
*/
|
|
|
|
protected boolean isBasicAuthentication(String url) throws IOException {
|
2008-11-03 20:47:10 -05:00
|
|
|
return DavGatewayHttpClientFacade.getHttpStatus(url) == HttpStatus.SC_UNAUTHORIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Try to find logon method path from logon form body.
|
|
|
|
*
|
|
|
|
* @param initmethod form body http method
|
|
|
|
* @return logon method path
|
|
|
|
*/
|
|
|
|
protected String getLogonMethodPathAndBaseUrl(HttpMethod initmethod) {
|
|
|
|
// default logon method path
|
|
|
|
String logonMethodPath = "/exchweb/bin/auth/owaauth.dll";
|
|
|
|
|
|
|
|
// try to parse login form to determine logon url and destination
|
|
|
|
BufferedReader loginFormReader = null;
|
|
|
|
try {
|
|
|
|
loginFormReader = new BufferedReader(new InputStreamReader(initmethod.getResponseBodyAsStream()));
|
|
|
|
String line;
|
|
|
|
// skip to form action
|
|
|
|
final String FORM_ACTION = "<form action=\"";
|
|
|
|
final String DESTINATION_INPUT = "name=\"destination\" value=\"";
|
|
|
|
//noinspection StatementWithEmptyBody
|
|
|
|
while ((line = loginFormReader.readLine()) != null && line.toLowerCase().indexOf(FORM_ACTION) == -1) {
|
|
|
|
}
|
|
|
|
if (line != null) {
|
|
|
|
int start = line.toLowerCase().indexOf(FORM_ACTION) + FORM_ACTION.length();
|
|
|
|
int end = line.indexOf("\"", start);
|
|
|
|
logonMethodPath = line.substring(start, end);
|
|
|
|
//noinspection StatementWithEmptyBody
|
|
|
|
while ((line = loginFormReader.readLine()) != null && line.indexOf(DESTINATION_INPUT) == -1) {
|
|
|
|
}
|
|
|
|
if (line != null) {
|
|
|
|
start = line.indexOf(DESTINATION_INPUT) + DESTINATION_INPUT.length();
|
|
|
|
end = line.indexOf("\"", start);
|
|
|
|
baseUrl = line.substring(start, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// allow relative URLs
|
|
|
|
if (!logonMethodPath.startsWith("/")) {
|
|
|
|
String path = initmethod.getPath();
|
|
|
|
int end = path.lastIndexOf('/');
|
|
|
|
if (end >= 0) {
|
|
|
|
logonMethodPath = path.substring(0, end + 1) + logonMethodPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing login form at " + initmethod.getPath());
|
|
|
|
} finally {
|
|
|
|
if (loginFormReader != null) {
|
|
|
|
try {
|
|
|
|
loginFormReader.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing login form at " + initmethod.getPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
initmethod.releaseConnection();
|
|
|
|
}
|
|
|
|
return logonMethodPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void formLogin(HttpClient httpClient, HttpMethod initmethod, String userName, String password) throws IOException {
|
|
|
|
LOGGER.debug("Form based authentication detected");
|
|
|
|
// get logon method path and actual destination (baseUrl)
|
|
|
|
String logonMethodPath = getLogonMethodPathAndBaseUrl(initmethod);
|
|
|
|
|
|
|
|
PostMethod logonMethod = new PostMethod(
|
|
|
|
logonMethodPath + "?ForcedBasic=false&Basic=false&Private=true&Language=No_Value"
|
|
|
|
);
|
|
|
|
logonMethod.addParameter("destination", baseUrl);
|
|
|
|
logonMethod.addParameter("flags", "4");
|
|
|
|
// logonMethod.addParameter("visusername", userName.substring(userName.lastIndexOf('\\')));
|
|
|
|
logonMethod.addParameter("username", userName);
|
|
|
|
logonMethod.addParameter("password", password);
|
|
|
|
// logonMethod.addParameter("SubmitCreds", "Log On");
|
|
|
|
// logonMethod.addParameter("forcedownlevel", "0");
|
|
|
|
logonMethod.addParameter("trusted", "4");
|
|
|
|
|
|
|
|
try {
|
|
|
|
httpClient.executeMethod(logonMethod);
|
|
|
|
} finally {
|
|
|
|
logonMethod.releaseConnection();
|
|
|
|
}
|
|
|
|
Header locationHeader = logonMethod.getResponseHeader("Location");
|
|
|
|
|
|
|
|
if (logonMethod.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP ||
|
|
|
|
locationHeader == null ||
|
|
|
|
!baseUrl.equals(locationHeader.getValue())) {
|
|
|
|
throw new HttpException("Authentication failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String getMailPath(HttpMethod method) {
|
|
|
|
String result = null;
|
|
|
|
// get user mail URL from html body (multi frame)
|
|
|
|
BufferedReader mainPageReader = null;
|
|
|
|
try {
|
|
|
|
mainPageReader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
|
|
|
|
String line;
|
|
|
|
// find base url
|
|
|
|
final String BASE_HREF = "<base href=\"";
|
|
|
|
//noinspection StatementWithEmptyBody
|
|
|
|
while ((line = mainPageReader.readLine()) != null && line.toLowerCase().indexOf(BASE_HREF) == -1) {
|
|
|
|
}
|
|
|
|
if (line != null) {
|
|
|
|
int start = line.toLowerCase().indexOf(BASE_HREF) + BASE_HREF.length();
|
|
|
|
int end = line.indexOf("\"", start);
|
|
|
|
String mailBoxBaseHref = line.substring(start, end);
|
|
|
|
URL baseURL = new URL(mailBoxBaseHref);
|
|
|
|
result = baseURL.getPath();
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing main page at " + method.getPath());
|
|
|
|
} finally {
|
|
|
|
if (mainPageReader != null) {
|
|
|
|
try {
|
|
|
|
mainPageReader.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing main page at " + method.getPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
method.releaseConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2008-01-09 11:33:57 -05:00
|
|
|
}
|
|
|
|
|
2008-10-31 13:12:30 -04:00
|
|
|
void login(String userName, String password) throws IOException {
|
2008-11-26 19:56:28 -05:00
|
|
|
LOGGER.debug("Session " + this + " login");
|
2007-04-25 19:29:46 -04:00
|
|
|
try {
|
2008-11-03 20:47:10 -05:00
|
|
|
baseUrl = Settings.getProperty("davmail.url");
|
2007-04-25 19:29:46 -04:00
|
|
|
|
2008-11-03 20:47:10 -05:00
|
|
|
boolean isBasicAuthentication = isBasicAuthentication(baseUrl);
|
2008-01-09 11:33:57 -05:00
|
|
|
|
2007-04-25 19:29:46 -04:00
|
|
|
// get proxy configuration from setttings properties
|
2008-11-03 20:47:10 -05:00
|
|
|
URL urlObject = new URL(baseUrl);
|
2006-12-12 18:57:24 -05:00
|
|
|
// webdavresource is unable to create the correct url type
|
|
|
|
HttpURL httpURL;
|
2008-11-03 20:47:10 -05:00
|
|
|
if (baseUrl.startsWith("http://")) {
|
2006-12-12 18:57:24 -05:00
|
|
|
httpURL = new HttpURL(userName, password,
|
|
|
|
urlObject.getHost(), urlObject.getPort());
|
2008-11-03 20:47:10 -05:00
|
|
|
} else if (baseUrl.startsWith("https://")) {
|
2006-12-12 18:57:24 -05:00
|
|
|
httpURL = new HttpsURL(userName, password,
|
|
|
|
urlObject.getHost(), urlObject.getPort());
|
|
|
|
} else {
|
2008-11-03 20:47:10 -05:00
|
|
|
throw new IllegalArgumentException("Invalid URL: " + baseUrl);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
wdr = new WebdavResource(httpURL, WebdavResource.NOACTION, 0);
|
|
|
|
|
|
|
|
// set httpclient timeout to 30 seconds
|
|
|
|
//wdr.retrieveSessionInstance().setTimeout(30000);
|
|
|
|
|
|
|
|
// get the internal HttpClient instance
|
|
|
|
HttpClient httpClient = wdr.retrieveSessionInstance();
|
|
|
|
|
2008-11-03 20:47:10 -05:00
|
|
|
DavGatewayHttpClientFacade.configureClient(httpClient);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-10-31 13:12:30 -04:00
|
|
|
// get webmail root url
|
2006-12-12 18:57:24 -05:00
|
|
|
// providing credentials
|
2008-10-31 13:12:30 -04:00
|
|
|
// manually follow redirect
|
2008-11-04 05:37:36 -05:00
|
|
|
HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl);
|
2008-08-24 12:01:03 -04:00
|
|
|
|
2008-01-09 11:33:57 -05:00
|
|
|
if (!isBasicAuthentication) {
|
2008-11-04 05:37:36 -05:00
|
|
|
formLogin(httpClient, method, userName, password);
|
|
|
|
// reexecute method with new base URL
|
|
|
|
method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2008-01-09 11:33:57 -05:00
|
|
|
// User may be authenticated, get various session information
|
2008-11-03 20:47:10 -05:00
|
|
|
int status = method.getStatusCode();
|
2008-01-09 11:33:57 -05:00
|
|
|
if (status != HttpStatus.SC_OK) {
|
2006-12-12 18:57:24 -05:00
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(status);
|
2007-04-26 06:29:11 -04:00
|
|
|
ex.setReason(method.getStatusText());
|
2006-12-12 18:57:24 -05:00
|
|
|
throw ex;
|
|
|
|
}
|
2008-08-24 12:01:03 -04:00
|
|
|
// test form based authentication
|
|
|
|
String queryString = method.getQueryString();
|
|
|
|
if (queryString != null && queryString.endsWith("reason=2")) {
|
2008-11-03 20:47:10 -05:00
|
|
|
method.releaseConnection();
|
2008-11-26 19:56:28 -05:00
|
|
|
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");
|
|
|
|
}
|
2008-08-24 12:01:03 -04:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-11-03 20:47:10 -05:00
|
|
|
mailPath = getMailPath(method);
|
2008-01-09 11:33:57 -05:00
|
|
|
|
2008-08-24 12:01:03 -04:00
|
|
|
if (mailPath == null) {
|
2008-11-03 20:47:10 -05:00
|
|
|
throw new HttpException(baseUrl + " not found in body, authentication failed: password expired ?");
|
2008-01-09 11:33:57 -05:00
|
|
|
}
|
2008-08-24 12:01:03 -04:00
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
// got base http mailbox http url
|
|
|
|
wdr.setPath(mailPath);
|
|
|
|
|
|
|
|
// Retrieve inbox and trash URLs
|
|
|
|
Vector<String> reqProps = new Vector<String>();
|
|
|
|
reqProps.add("urn:schemas:httpmail:inbox");
|
|
|
|
reqProps.add("urn:schemas:httpmail:deleteditems");
|
|
|
|
reqProps.add("urn:schemas:httpmail:sendmsg");
|
|
|
|
reqProps.add("urn:schemas:httpmail:drafts");
|
2008-11-26 19:56:28 -05:00
|
|
|
reqProps.add("urn:schemas:httpmail:calendar");
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2007-11-08 12:12:36 -05:00
|
|
|
Enumeration foldersEnum = wdr.propfindMethod(0, reqProps);
|
|
|
|
if (!foldersEnum.hasMoreElements()) {
|
|
|
|
throw new IOException("Unable to get mail folders");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2007-11-08 12:12:36 -05:00
|
|
|
ResponseEntity inboxResponse = (ResponseEntity) foldersEnum.
|
2006-12-12 18:57:24 -05:00
|
|
|
nextElement();
|
|
|
|
Enumeration inboxPropsEnum = inboxResponse.getProperties();
|
|
|
|
if (!inboxPropsEnum.hasMoreElements()) {
|
2007-11-08 12:12:36 -05:00
|
|
|
throw new IOException("Unable to get mail folders");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
while (inboxPropsEnum.hasMoreElements()) {
|
|
|
|
Property inboxProp = (Property) inboxPropsEnum.nextElement();
|
|
|
|
if ("inbox".equals(inboxProp.getLocalName())) {
|
|
|
|
inboxUrl = URIUtil.decode(inboxProp.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("deleteditems".equals(inboxProp.getLocalName())) {
|
|
|
|
deleteditemsUrl = URIUtil.decode(inboxProp.
|
|
|
|
getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("sendmsg".equals(inboxProp.getLocalName())) {
|
|
|
|
sendmsgUrl = URIUtil.decode(inboxProp.
|
|
|
|
getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("drafts".equals(inboxProp.getLocalName())) {
|
|
|
|
draftsUrl = URIUtil.decode(inboxProp.
|
|
|
|
getPropertyAsString());
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
if ("calendar".equals(inboxProp.getLocalName())) {
|
|
|
|
calendarUrl = URIUtil.decode(inboxProp.
|
|
|
|
getPropertyAsString());
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// set current folder to Inbox
|
|
|
|
currentFolderUrl = inboxUrl;
|
|
|
|
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.debug("Inbox URL : " + inboxUrl);
|
|
|
|
LOGGER.debug("Trash URL : " + deleteditemsUrl);
|
|
|
|
LOGGER.debug("Send URL : " + sendmsgUrl);
|
2007-11-08 12:12:36 -05:00
|
|
|
LOGGER.debug("Drafts URL : " + draftsUrl);
|
|
|
|
// TODO : sometimes path, sometimes Url ?
|
2006-12-12 18:57:24 -05:00
|
|
|
deleteditemsUrl = URIUtil.getPath(deleteditemsUrl);
|
|
|
|
wdr.setPath(URIUtil.getPath(inboxUrl));
|
|
|
|
|
|
|
|
} catch (Exception exc) {
|
2007-04-26 06:29:11 -04:00
|
|
|
StringBuffer message = new StringBuffer();
|
|
|
|
message.append("DavMail login exception: ");
|
|
|
|
if (exc.getMessage() != null) {
|
|
|
|
message.append(exc.getMessage());
|
|
|
|
} else if (exc instanceof HttpException) {
|
|
|
|
message.append(((HttpException) exc).getReasonCode());
|
|
|
|
String httpReason = ((HttpException) exc).getReason();
|
|
|
|
if (httpReason != null) {
|
|
|
|
message.append(" ");
|
|
|
|
message.append(httpReason);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
message.append(exc);
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
try {
|
2007-04-26 06:29:11 -04:00
|
|
|
message.append("\nWebdav status:");
|
|
|
|
message.append(wdr.getStatusCode());
|
|
|
|
|
|
|
|
String webdavStatusMessage = wdr.getStatusMessage();
|
|
|
|
if (webdavStatusMessage != null) {
|
|
|
|
message.append(webdavStatusMessage);
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
} catch (Exception e) {
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.error("Exception getting status from " + wdr);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2007-04-26 06:29:11 -04:00
|
|
|
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.error(message.toString());
|
2007-04-26 06:29:11 -04:00
|
|
|
throw new IOException(message.toString());
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close session.
|
|
|
|
* This will only close http client, not the actual Exchange session
|
|
|
|
*
|
2007-03-14 07:55:37 -04:00
|
|
|
* @throws IOException if unable to close Webdav context
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
|
|
|
public void close() throws IOException {
|
2008-11-26 19:56:28 -05:00
|
|
|
LOGGER.debug("Session " + this + " closed");
|
2006-12-12 18:57:24 -05:00
|
|
|
wdr.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create message in current folder
|
2007-03-14 07:55:37 -04:00
|
|
|
*
|
|
|
|
* @param subject message subject line
|
|
|
|
* @param messageBody mail body
|
|
|
|
* @throws java.io.IOException when unable to create message
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
|
|
|
public void createMessage(String subject, String messageBody) throws IOException {
|
|
|
|
createMessage(currentFolderUrl, subject, messageBody);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create message in specified folder.
|
|
|
|
* Will overwrite an existing message with same subject in the same folder
|
2007-03-14 07:55:37 -04:00
|
|
|
*
|
|
|
|
* @param folderUrl Exchange folder URL
|
|
|
|
* @param subject message subject line
|
|
|
|
* @param messageBody mail body
|
|
|
|
* @throws java.io.IOException when unable to create message
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
|
|
|
public void createMessage(String folderUrl, String subject, String messageBody) throws IOException {
|
|
|
|
String messageUrl = URIUtil.encodePathQuery(folderUrl + "/" + subject + ".EML");
|
|
|
|
|
|
|
|
PutMethod putmethod = new PutMethod(messageUrl);
|
2008-11-26 19:56:28 -05:00
|
|
|
// TODO : test, bcc ?
|
|
|
|
putmethod.setRequestHeader("Translate", "f");
|
2006-12-12 18:57:24 -05:00
|
|
|
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
2008-11-29 09:24:12 -05:00
|
|
|
InputStream bodyStream = null;
|
2008-11-26 19:56:28 -05:00
|
|
|
try {
|
2008-11-29 09:24:12 -05:00
|
|
|
// use same encoding as client socket reader
|
|
|
|
bodyStream = new ByteArrayInputStream(messageBody.getBytes());
|
|
|
|
putmethod.setRequestBody(bodyStream);
|
2008-11-26 19:56:28 -05:00
|
|
|
int code = wdr.retrieveSessionInstance().executeMethod(putmethod);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
if (code == HttpURLConnection.HTTP_OK) {
|
|
|
|
LOGGER.warn("Overwritten message " + messageUrl);
|
|
|
|
} else if (code != HttpURLConnection.HTTP_CREATED) {
|
|
|
|
throw new IOException("Unable to create message " + code + " " + putmethod.getStatusLine());
|
|
|
|
}
|
|
|
|
} finally {
|
2008-11-29 09:24:12 -05:00
|
|
|
if (bodyStream != null) {
|
|
|
|
try {
|
|
|
|
bodyStream.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error(e);
|
|
|
|
}
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
putmethod.releaseConnection();
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-15 05:37:55 -04:00
|
|
|
protected Message buildMessage(ResponseEntity responseEntity) throws URIException {
|
2006-12-12 18:57:24 -05:00
|
|
|
Message message = new Message();
|
|
|
|
message.messageUrl = URIUtil.decode(responseEntity.getHref());
|
|
|
|
Enumeration propertiesEnum = responseEntity.getProperties();
|
|
|
|
while (propertiesEnum.hasMoreElements()) {
|
|
|
|
Property prop = (Property) propertiesEnum.nextElement();
|
|
|
|
String localName = prop.getLocalName();
|
2008-02-05 18:17:31 -05:00
|
|
|
|
|
|
|
if ("x0e080003".equals(localName)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
message.size = Integer.parseInt(prop.getPropertyAsString());
|
|
|
|
} else if ("uid".equals(localName)) {
|
|
|
|
message.uid = prop.getPropertyAsString();
|
|
|
|
} else if ("content-class".equals(prop.getLocalName())) {
|
|
|
|
message.contentClass = prop.getPropertyAsString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Message getMessage(String messageUrl) throws IOException {
|
|
|
|
|
2006-12-19 04:41:31 -05:00
|
|
|
// TODO switch according to Log4J log level
|
2007-11-08 12:12:36 -05:00
|
|
|
//wdr.setDebug(4);
|
|
|
|
//wdr.propfindMethod(messageUrl, 0);
|
2007-05-09 18:32:01 -04:00
|
|
|
Enumeration messageEnum = wdr.propfindMethod(messageUrl, 0, MESSAGE_REQUEST_PROPERTIES);
|
2007-11-08 12:12:36 -05:00
|
|
|
//wdr.setDebug(0);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
// 201 created in some cases ?!?
|
2007-05-09 18:32:01 -04:00
|
|
|
if ((wdr.getStatusCode() != HttpURLConnection.HTTP_OK && wdr.getStatusCode() != HttpURLConnection.HTTP_CREATED)
|
|
|
|
|| !messageEnum.hasMoreElements()) {
|
2006-12-12 18:57:24 -05:00
|
|
|
throw new IOException("Unable to get message: " + wdr.getStatusCode()
|
|
|
|
+ " " + wdr.getStatusMessage());
|
|
|
|
}
|
|
|
|
ResponseEntity entity = (ResponseEntity) messageEnum.nextElement();
|
|
|
|
|
2007-01-02 05:02:32 -05:00
|
|
|
return buildMessage(entity);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Message> getAllMessages() throws IOException {
|
|
|
|
List<Message> messages = new ArrayList<Message>();
|
2008-02-05 18:55:29 -05:00
|
|
|
//wdr.setDebug(4);
|
|
|
|
//wdr.propfindMethod(currentFolderUrl, 1);
|
2007-11-08 12:12:36 -05:00
|
|
|
// one level search
|
2007-05-09 18:32:01 -04:00
|
|
|
Enumeration folderEnum = wdr.propfindMethod(currentFolderUrl, 1, MESSAGE_REQUEST_PROPERTIES);
|
2008-02-05 18:55:29 -05:00
|
|
|
//wdr.setDebug(0);
|
2006-12-12 18:57:24 -05:00
|
|
|
while (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
|
|
|
|
|
|
|
Message message = buildMessage(entity);
|
|
|
|
if ("urn:content-classes:message".equals(message.contentClass) ||
|
2007-03-21 11:35:51 -04:00
|
|
|
"urn:content-classes:calendarmessage".equals(message.contentClass) ||
|
2007-04-23 19:35:59 -04:00
|
|
|
"urn:content-classes:recallmessage".equals(message.contentClass) ||
|
2008-02-05 18:17:31 -05:00
|
|
|
"urn:content-classes:appointment".equals(message.contentClass) ||
|
2007-04-23 19:35:59 -04:00
|
|
|
"urn:content-classes:dsn".equals(message.contentClass)) {
|
2006-12-12 18:57:24 -05:00
|
|
|
messages.add(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
2007-02-07 06:32:44 -05:00
|
|
|
/**
|
|
|
|
* Delete oldest messages in trash.
|
2007-02-07 06:51:08 -05:00
|
|
|
* keepDelay is the number of days to keep messages in trash before delete
|
2007-03-14 07:55:37 -04:00
|
|
|
*
|
|
|
|
* @throws IOException when unable to purge messages
|
2007-02-07 06:32:44 -05:00
|
|
|
*/
|
2007-02-07 06:51:08 -05:00
|
|
|
public void purgeOldestTrashMessages() throws IOException {
|
|
|
|
int keepDelay = Settings.getIntProperty("davmail.keepDelay");
|
|
|
|
if (keepDelay == 0) {
|
|
|
|
keepDelay = DEFAULT_KEEP_DELAY;
|
|
|
|
}
|
|
|
|
|
2007-02-07 06:32:44 -05:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -keepDelay);
|
2007-11-08 12:12:36 -05:00
|
|
|
LOGGER.debug("Delete messages in trash since " + cal.getTime());
|
2007-02-07 06:32:44 -05:00
|
|
|
long keepTimestamp = cal.getTimeInMillis();
|
|
|
|
|
|
|
|
Vector<String> deleteRequestProperties = new Vector<String>();
|
|
|
|
deleteRequestProperties.add("DAV:getlastmodified");
|
|
|
|
deleteRequestProperties.add("urn:schemas:mailheader:content-class");
|
|
|
|
|
|
|
|
Enumeration folderEnum = wdr.propfindMethod(deleteditemsUrl, 1, deleteRequestProperties);
|
|
|
|
while (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
|
|
|
String messageUrl = URIUtil.decode(entity.getHref());
|
|
|
|
String lastModifiedString = null;
|
|
|
|
String contentClass = null;
|
|
|
|
Enumeration propertiesEnum = entity.getProperties();
|
|
|
|
while (propertiesEnum.hasMoreElements()) {
|
|
|
|
Property prop = (Property) propertiesEnum.nextElement();
|
|
|
|
String localName = prop.getLocalName();
|
|
|
|
if ("getlastmodified".equals(localName)) {
|
|
|
|
lastModifiedString = prop.getPropertyAsString();
|
|
|
|
} else if ("content-class".equals(prop.getLocalName())) {
|
|
|
|
contentClass = prop.getPropertyAsString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ("urn:content-classes:message".equals(contentClass) &&
|
|
|
|
lastModifiedString != null && lastModifiedString.length() > 0) {
|
|
|
|
Date parsedDate;
|
|
|
|
try {
|
|
|
|
parsedDate = dateParser.parse(lastModifiedString);
|
|
|
|
if (parsedDate.getTime() < keepTimestamp) {
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.debug("Delete " + messageUrl + " last modified " + parsedDate);
|
2007-02-07 06:32:44 -05:00
|
|
|
wdr.deleteMethod(messageUrl);
|
|
|
|
}
|
|
|
|
} catch (ParseException e) {
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.warn("Invalid message modified date " + lastModifiedString + " on " + messageUrl);
|
2007-02-07 06:32:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
public void sendMessage(BufferedReader reader) throws IOException {
|
|
|
|
String subject = "davmailtemp";
|
|
|
|
String line = reader.readLine();
|
|
|
|
StringBuffer mailBuffer = new StringBuffer();
|
|
|
|
while (!".".equals(line)) {
|
|
|
|
mailBuffer.append(line);
|
|
|
|
mailBuffer.append("\n");
|
|
|
|
line = reader.readLine();
|
|
|
|
|
|
|
|
// patch thunderbird html in reply for correct outlook display
|
2007-02-20 05:42:03 -05:00
|
|
|
if (line.startsWith("<head>")) {
|
2006-12-12 18:57:24 -05:00
|
|
|
line += "\n <style> blockquote { display: block; margin: 1em 0px; padding-left: 1em; border-left: solid; border-color: blue; border-width: thin;}</style>";
|
|
|
|
}
|
|
|
|
if (line.startsWith("Subject")) {
|
|
|
|
subject = MimeUtility.decodeText(line.substring(8).trim());
|
|
|
|
// '/' is invalid as message URL
|
|
|
|
subject = subject.replaceAll("/", "_xF8FF_");
|
|
|
|
// '?' is also invalid
|
|
|
|
subject = subject.replaceAll("\\?", "");
|
2007-11-08 12:12:36 -05:00
|
|
|
// TODO : test & in subject
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
createMessage(draftsUrl, subject, mailBuffer.toString());
|
|
|
|
|
|
|
|
// warning : slide library expects *unencoded* urls
|
|
|
|
String tempUrl = draftsUrl + "/" + subject + ".eml";
|
|
|
|
boolean sent = wdr.moveMethod(tempUrl, sendmsgUrl);
|
|
|
|
if (!sent) {
|
|
|
|
throw new IOException("Unable to send message: " + wdr.getStatusCode()
|
|
|
|
+ " " + wdr.getStatusMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-11-08 12:12:36 -05:00
|
|
|
/**
|
|
|
|
* Select current folder.
|
|
|
|
* Folder name can be logical names INBOX or TRASH (translated to local names),
|
|
|
|
* relative path to user base folder or absolute path.
|
2007-11-08 17:09:38 -05:00
|
|
|
*
|
2007-11-08 12:12:36 -05:00
|
|
|
* @param folderName folder name
|
|
|
|
* @return Folder object
|
|
|
|
* @throws IOException when unable to change folder
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
public Folder selectFolder(String folderName) throws IOException {
|
|
|
|
Folder folder = new Folder();
|
|
|
|
folder.folderUrl = null;
|
|
|
|
if ("INBOX".equals(folderName)) {
|
|
|
|
folder.folderUrl = inboxUrl;
|
2007-01-02 04:51:12 -05:00
|
|
|
} else if ("TRASH".equals(folderName)) {
|
|
|
|
folder.folderUrl = deleteditemsUrl;
|
2007-05-10 17:18:42 -04:00
|
|
|
// absolute folder path
|
|
|
|
} else if (folderName != null && folderName.startsWith("/")) {
|
|
|
|
folder.folderUrl = folderName;
|
2006-12-12 18:57:24 -05:00
|
|
|
} else {
|
|
|
|
folder.folderUrl = mailPath + folderName;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector<String> reqProps = new Vector<String>();
|
|
|
|
reqProps.add("urn:schemas:httpmail:unreadcount");
|
|
|
|
reqProps.add("DAV:childcount");
|
|
|
|
Enumeration folderEnum = wdr.propfindMethod(folder.folderUrl, 0, reqProps);
|
2007-11-08 12:12:36 -05:00
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
if (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
|
|
|
Enumeration propertiesEnum = entity.getProperties();
|
|
|
|
while (propertiesEnum.hasMoreElements()) {
|
|
|
|
Property prop = (Property) propertiesEnum.nextElement();
|
|
|
|
if ("unreadcount".equals(prop.getLocalName())) {
|
|
|
|
folder.unreadCount = Integer.parseInt(prop.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("childcount".equals(prop.getLocalName())) {
|
|
|
|
folder.childCount = Integer.parseInt(prop.getPropertyAsString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2007-11-08 12:12:36 -05:00
|
|
|
throw new IOException("Folder not found: " + folder.folderUrl);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
currentFolderUrl = folder.folderUrl;
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
public class Folder {
|
|
|
|
public String folderUrl;
|
|
|
|
public int childCount;
|
|
|
|
public int unreadCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
public class Message {
|
|
|
|
public String messageUrl;
|
|
|
|
public String uid;
|
|
|
|
public int size;
|
|
|
|
public String contentClass;
|
|
|
|
|
2008-02-05 18:17:31 -05:00
|
|
|
public void write(OutputStream os) throws IOException {
|
|
|
|
HttpMethod method = null;
|
2007-04-25 18:04:37 -04:00
|
|
|
BufferedReader reader = null;
|
2006-12-12 18:57:24 -05:00
|
|
|
try {
|
2008-02-05 18:17:31 -05:00
|
|
|
method = new GetMethod(URIUtil.encodePath(messageUrl));
|
|
|
|
method.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
|
|
|
|
method.setRequestHeader("Translate", "f");
|
|
|
|
wdr.retrieveSessionInstance().executeMethod(method);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-02-05 18:17:31 -05:00
|
|
|
boolean inHTML = false;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-02-05 18:17:31 -05:00
|
|
|
reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
|
|
|
|
OutputStreamWriter isoWriter = new OutputStreamWriter(os);
|
|
|
|
String line;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
if (".".equals(line)) {
|
|
|
|
line = "..";
|
2008-02-14 09:20:37 -05:00
|
|
|
// patch text/calendar to include utf-8 encoding
|
|
|
|
} else if ("Content-Type: text/calendar;".equals(line)) {
|
|
|
|
StringBuffer headerBuffer = new StringBuffer();
|
|
|
|
headerBuffer.append(line);
|
|
|
|
while ((line = reader.readLine()) != null && line.startsWith("\t")) {
|
|
|
|
headerBuffer.append((char) 13);
|
|
|
|
headerBuffer.append((char) 10);
|
|
|
|
headerBuffer.append(line);
|
|
|
|
}
|
|
|
|
if (headerBuffer.indexOf("charset") < 0) {
|
|
|
|
headerBuffer.append(";charset=utf-8");
|
|
|
|
}
|
|
|
|
headerBuffer.append((char) 13);
|
|
|
|
headerBuffer.append((char) 10);
|
|
|
|
headerBuffer.append(line);
|
|
|
|
line = headerBuffer.toString();
|
2008-02-05 18:17:31 -05:00
|
|
|
// detect html body to patch Exchange html body
|
|
|
|
} else if (line.startsWith("<html")) {
|
|
|
|
inHTML = true;
|
|
|
|
} else if (inHTML && "</html>".equals(line)) {
|
|
|
|
inHTML = false;
|
2007-04-25 18:04:37 -04:00
|
|
|
}
|
2008-02-05 18:17:31 -05:00
|
|
|
if (inHTML) {
|
2008-11-29 09:24:12 -05:00
|
|
|
// line = line.replaceAll("’", "'");
|
|
|
|
// line = line.replaceAll("…", "...");
|
2008-01-31 17:17:44 -05:00
|
|
|
}
|
2008-02-05 18:17:31 -05:00
|
|
|
isoWriter.write(line);
|
|
|
|
isoWriter.write((char) 13);
|
|
|
|
isoWriter.write((char) 10);
|
2007-04-25 18:04:37 -04:00
|
|
|
}
|
2008-02-05 18:17:31 -05:00
|
|
|
isoWriter.flush();
|
2007-04-25 18:04:37 -04:00
|
|
|
} finally {
|
|
|
|
if (reader != null) {
|
|
|
|
try {
|
|
|
|
reader.close();
|
|
|
|
} catch (IOException e) {
|
2008-02-05 18:17:31 -05:00
|
|
|
LOGGER.warn("Error closing message input stream", e);
|
2007-04-25 18:04:37 -04:00
|
|
|
}
|
|
|
|
}
|
2008-02-05 18:17:31 -05:00
|
|
|
if (method != null) {
|
|
|
|
method.releaseConnection();
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void delete() throws IOException {
|
|
|
|
String destination = deleteditemsUrl + messageUrl.substring(messageUrl.lastIndexOf("/"));
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.debug("Deleting : " + messageUrl + " to " + destination);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
wdr.moveMethod(messageUrl, destination);
|
2007-05-09 19:37:24 -04:00
|
|
|
if (wdr.getStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
|
2006-12-12 18:57:24 -05:00
|
|
|
int count = 2;
|
|
|
|
// name conflict, try another name
|
2007-05-09 19:37:24 -04:00
|
|
|
while (wdr.getStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
|
2006-12-12 18:57:24 -05:00
|
|
|
wdr.moveMethod(messageUrl, destination.substring(0, destination.lastIndexOf('.')) + "-" + count++ + ".eml");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.debug("Deleted to :" + destination + " " + wdr.getStatusCode() + " " + wdr.getStatusMessage());
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
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 {
|
2008-11-29 09:24:12 -05:00
|
|
|
wdr.deleteMethod(calendarUrl + "/" + URIUtil.decode(path));
|
2008-11-26 19:56:28 -05:00
|
|
|
int status = wdr.getStatusCode();
|
|
|
|
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) {
|
2008-11-29 09:24:12 -05:00
|
|
|
endDate = shortIcalParser.parse(endDateValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|