2006-12-12 18:57:24 -05:00
|
|
|
package davmail.exchange;
|
|
|
|
|
2007-04-26 06:29:11 -04:00
|
|
|
import davmail.Settings;
|
2008-11-03 20:47:10 -05:00
|
|
|
import davmail.http.DavGatewayHttpClientFacade;
|
2008-11-30 13:05:36 -05:00
|
|
|
import org.apache.commons.httpclient.*;
|
2008-12-19 08:01:14 -05:00
|
|
|
import org.apache.commons.httpclient.auth.AuthenticationException;
|
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;
|
2009-02-02 02:19:57 -05:00
|
|
|
import org.apache.commons.httpclient.util.Base64;
|
2006-12-12 18:57:24 -05:00
|
|
|
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;
|
2009-02-02 02:19:57 -05:00
|
|
|
import org.apache.webdav.lib.methods.MoveMethod;
|
2008-12-09 05:10:38 -05:00
|
|
|
import org.apache.webdav.lib.methods.PropPatchMethod;
|
2008-12-23 09:09:46 -05:00
|
|
|
import org.apache.webdav.lib.methods.SearchMethod;
|
2009-02-02 18:29:44 -05:00
|
|
|
import org.apache.webdav.lib.methods.CopyMethod;
|
2008-12-23 09:09:46 -05:00
|
|
|
import org.htmlcleaner.CommentToken;
|
2008-12-01 07:38:49 -05:00
|
|
|
import org.htmlcleaner.HtmlCleaner;
|
|
|
|
import org.htmlcleaner.TagNode;
|
2008-02-05 18:17:31 -05:00
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
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
|
|
|
|
2009-02-05 12:15:30 -05:00
|
|
|
public static final SimpleTimeZone GMT_TIMEZONE = new SimpleTimeZone(0, "GMT");
|
2009-01-15 08:19:24 -05:00
|
|
|
|
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");
|
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
|
|
|
}
|
|
|
|
|
2008-12-09 08:24:17 -05:00
|
|
|
protected static final Vector<String> EVENT_REQUEST_PROPERTIES = new Vector<String>();
|
|
|
|
|
|
|
|
static {
|
|
|
|
EVENT_REQUEST_PROPERTIES.add("DAV:getetag");
|
|
|
|
}
|
|
|
|
|
2008-12-09 03:54:08 -05:00
|
|
|
protected static final Vector<String> WELL_KNOWN_FOLDERS = new Vector<String>();
|
|
|
|
|
|
|
|
static {
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:inbox");
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:deleteditems");
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:sentitems");
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:sendmsg");
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:drafts");
|
|
|
|
WELL_KNOWN_FOLDERS.add("urn:schemas:httpmail:calendar");
|
|
|
|
}
|
|
|
|
|
2009-01-23 05:30:48 -05:00
|
|
|
public static final HashMap<String, String> PRIORITIES = new HashMap<String, String>();
|
|
|
|
|
|
|
|
static {
|
|
|
|
PRIORITIES.put("-2", "5 (Lowest)");
|
|
|
|
PRIORITIES.put("-1", "4 (Low)");
|
|
|
|
PRIORITIES.put("1", "2 (High)");
|
|
|
|
PRIORITIES.put("2", "1 (Highest)");
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
2009-01-23 05:30:48 -05:00
|
|
|
* Date parser/formatter from Exchange format
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
2008-12-05 05:45:23 -05:00
|
|
|
private final SimpleDateFormat dateFormatter;
|
2009-01-23 05:30:48 -05:00
|
|
|
private final SimpleDateFormat dateParser;
|
|
|
|
|
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;
|
2008-12-05 05:01:24 -05:00
|
|
|
private String sentitemsUrl;
|
2007-05-09 18:32:01 -04:00
|
|
|
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;
|
2008-12-25 17:16:24 -05:00
|
|
|
private String email;
|
2007-05-09 18:32:01 -04:00
|
|
|
private WebdavResource wdr = null;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
private final ExchangeSessionFactory.PoolKey poolKey;
|
2008-12-04 06:50:31 -05:00
|
|
|
|
2008-12-23 10:20:50 -05:00
|
|
|
private boolean disableGalLookup = false;
|
|
|
|
|
2008-12-04 06:50:31 -05:00
|
|
|
ExchangeSessionFactory.PoolKey getPoolKey() {
|
|
|
|
return poolKey;
|
|
|
|
}
|
|
|
|
|
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-12-04 06:50:31 -05:00
|
|
|
*
|
|
|
|
* @param poolKey session pool key
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
2008-12-04 06:50:31 -05:00
|
|
|
ExchangeSession(ExchangeSessionFactory.PoolKey poolKey) {
|
|
|
|
this.poolKey = poolKey;
|
2006-12-12 18:57:24 -05:00
|
|
|
// SimpleDateFormat are not thread safe, need to create one instance for
|
|
|
|
// each session
|
2008-12-05 05:45:23 -05:00
|
|
|
dateFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
2009-02-05 12:15:30 -05:00
|
|
|
dateFormatter.setTimeZone(GMT_TIMEZONE);
|
2009-01-23 05:30:48 -05:00
|
|
|
|
|
|
|
dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
|
|
dateParser.setTimeZone(GMT_TIMEZONE);
|
2009-01-23 08:01:46 -05:00
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
LOGGER.debug("Session " + this + " created");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2008-12-04 06:50:31 -05:00
|
|
|
public boolean isExpired() {
|
|
|
|
boolean isExpired = false;
|
|
|
|
try {
|
2008-12-23 17:01:29 -05:00
|
|
|
wdr.propfindMethod(0);
|
|
|
|
int status = wdr.getStatusCode();
|
2008-12-04 06:50:31 -05:00
|
|
|
|
2008-12-23 17:01:29 -05:00
|
|
|
if (status != HttpStatus.SC_MULTI_STATUS) {
|
2008-12-04 06:50:31 -05:00
|
|
|
isExpired = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
isExpired = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isExpired;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-12-19 07:27:33 -05:00
|
|
|
protected String getAbsolutePath(HttpMethod method, String path) {
|
|
|
|
String absolutePath = path;
|
|
|
|
// allow relative path
|
|
|
|
if (!absolutePath.startsWith("/")) {
|
|
|
|
String currentPath = method.getPath();
|
|
|
|
int end = currentPath.lastIndexOf('/');
|
|
|
|
if (end >= 0) {
|
|
|
|
absolutePath = currentPath.substring(0, end + 1) + absolutePath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return absolutePath;
|
|
|
|
}
|
|
|
|
|
2008-11-03 20:47:10 -05:00
|
|
|
/**
|
|
|
|
* Try to find logon method path from logon form body.
|
|
|
|
*
|
2008-12-01 12:56:18 -05:00
|
|
|
* @param httpClient httpClient instance
|
2008-11-03 20:47:10 -05:00
|
|
|
* @param initmethod form body http method
|
2008-12-01 07:38:49 -05:00
|
|
|
* @return logon method
|
2008-12-01 12:56:18 -05:00
|
|
|
* @throws java.io.IOException on error
|
2008-11-03 20:47:10 -05:00
|
|
|
*/
|
2008-12-01 07:38:49 -05:00
|
|
|
protected PostMethod buildLogonMethod(HttpClient httpClient, HttpMethod initmethod) throws IOException {
|
|
|
|
|
|
|
|
PostMethod logonMethod = null;
|
|
|
|
|
|
|
|
// create an instance of HtmlCleaner
|
|
|
|
HtmlCleaner cleaner = new HtmlCleaner();
|
2008-11-03 20:47:10 -05:00
|
|
|
|
|
|
|
try {
|
2008-12-01 07:38:49 -05:00
|
|
|
TagNode node = cleaner.clean(initmethod.getResponseBodyAsStream());
|
2008-12-17 10:27:56 -05:00
|
|
|
List forms = node.getElementListByName("form", true);
|
2008-12-01 07:38:49 -05:00
|
|
|
if (forms.size() == 1) {
|
2008-12-17 10:27:56 -05:00
|
|
|
TagNode form = (TagNode) forms.get(0);
|
2008-12-01 07:38:49 -05:00
|
|
|
String logonMethodPath = form.getAttributeByName("action");
|
|
|
|
|
2008-12-19 07:27:33 -05:00
|
|
|
logonMethod = new PostMethod(getAbsolutePath(initmethod, logonMethodPath));
|
2008-12-01 07:38:49 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
List inputList = form.getElementListByName("input", true);
|
|
|
|
for (Object input : inputList) {
|
|
|
|
String type = ((TagNode) input).getAttributeByName("type");
|
|
|
|
String name = ((TagNode) input).getAttributeByName("name");
|
|
|
|
String value = ((TagNode) input).getAttributeByName("value");
|
2008-12-01 07:38:49 -05:00
|
|
|
if ("hidden".equalsIgnoreCase(type) && name != null && value != null) {
|
|
|
|
logonMethod.addParameter(name, value);
|
|
|
|
}
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
2008-12-01 07:38:49 -05:00
|
|
|
} else {
|
2008-12-17 10:27:56 -05:00
|
|
|
List frameList = node.getElementListByName("frame", true);
|
2008-12-01 07:38:49 -05:00
|
|
|
if (frameList.size() == 1) {
|
2008-12-17 10:27:56 -05:00
|
|
|
String src = ((TagNode) frameList.get(0)).getAttributeByName("src");
|
2008-12-01 07:38:49 -05:00
|
|
|
if (src != null) {
|
|
|
|
LOGGER.debug("Frames detected in form page, try frame content");
|
|
|
|
initmethod.releaseConnection();
|
|
|
|
HttpMethod newInitMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, src);
|
|
|
|
logonMethod = buildLogonMethod(httpClient, newInitMethod);
|
2008-12-19 07:27:33 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// another failover for script based logon forms (Exchange 2007)
|
|
|
|
List scriptList = node.getElementListByName("script", true);
|
|
|
|
for (Object script : scriptList) {
|
|
|
|
List contents = ((TagNode) script).getChildren();
|
|
|
|
for (Object content : contents) {
|
|
|
|
if (content instanceof CommentToken) {
|
|
|
|
String scriptValue = ((CommentToken) content).getCommentedContent();
|
|
|
|
int a_sUrlIndex = scriptValue.indexOf("var a_sUrl = \"");
|
|
|
|
int a_sLgnIndex = scriptValue.indexOf("var a_sLgn = \"");
|
|
|
|
if (a_sUrlIndex >= 0 && a_sLgnIndex >= 0) {
|
|
|
|
a_sUrlIndex += "var a_sUrl = \"".length();
|
|
|
|
a_sLgnIndex += "var a_sLgn = \"".length();
|
|
|
|
int a_sUrlEndIndex = scriptValue.indexOf("\"", a_sUrlIndex);
|
|
|
|
int a_sLgnEndIndex = scriptValue.indexOf("\"", a_sLgnIndex);
|
|
|
|
if (a_sUrlEndIndex >= 0 && a_sLgnEndIndex >= 0) {
|
|
|
|
String src = getAbsolutePath(initmethod,
|
|
|
|
scriptValue.substring(a_sLgnIndex, a_sLgnEndIndex) +
|
|
|
|
scriptValue.substring(a_sUrlIndex, a_sUrlEndIndex));
|
2008-12-22 19:14:41 -05:00
|
|
|
LOGGER.debug("Detected script based logon, redirect to form at " + src);
|
2008-12-19 07:27:33 -05:00
|
|
|
HttpMethod newInitMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, src);
|
|
|
|
logonMethod = buildLogonMethod(httpClient, newInitMethod);
|
|
|
|
}
|
2008-12-22 19:14:41 -05:00
|
|
|
} else {
|
|
|
|
a_sLgnIndex = scriptValue.indexOf("var a_sLgnQS = \"");
|
|
|
|
if (a_sUrlIndex >= 0 && a_sLgnIndex >= 0) {
|
|
|
|
a_sUrlIndex += "var a_sUrl = \"".length();
|
|
|
|
a_sLgnIndex += "var a_sLgnQS = \"".length();
|
|
|
|
int a_sUrlEndIndex = scriptValue.indexOf("\"", a_sUrlIndex);
|
|
|
|
int a_sLgnEndIndex = scriptValue.indexOf("\"", a_sLgnIndex);
|
|
|
|
if (a_sUrlEndIndex >= 0 && a_sLgnEndIndex >= 0) {
|
|
|
|
String src = initmethod.getPath() +
|
|
|
|
scriptValue.substring(a_sLgnIndex, a_sLgnEndIndex) +
|
|
|
|
scriptValue.substring(a_sUrlIndex, a_sUrlEndIndex);
|
|
|
|
LOGGER.debug("Detected script based logon, redirect to form at " + src);
|
|
|
|
HttpMethod newInitMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, src);
|
|
|
|
logonMethod = buildLogonMethod(httpClient, newInitMethod);
|
|
|
|
}
|
|
|
|
}
|
2008-12-19 07:27:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-01 07:38:49 -05:00
|
|
|
}
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2008-12-01 07:38:49 -05:00
|
|
|
LOGGER.error("Error parsing login form at " + initmethod.getURI());
|
2008-11-03 20:47:10 -05:00
|
|
|
} finally {
|
|
|
|
initmethod.releaseConnection();
|
|
|
|
}
|
|
|
|
|
2008-12-01 07:38:49 -05:00
|
|
|
if (logonMethod == null) {
|
2008-12-19 07:27:33 -05:00
|
|
|
throw new IOException("Authentication form not found at " + initmethod.getURI());
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
2008-12-01 07:38:49 -05:00
|
|
|
return logonMethod;
|
|
|
|
}
|
2008-11-03 20:47:10 -05:00
|
|
|
|
2008-12-01 07:38:49 -05:00
|
|
|
protected HttpMethod formLogin(HttpClient httpClient, HttpMethod initmethod, String userName, String password) throws IOException {
|
|
|
|
LOGGER.debug("Form based authentication detected");
|
2008-12-02 05:20:46 -05:00
|
|
|
|
2008-12-01 07:38:49 -05:00
|
|
|
HttpMethod logonMethod = buildLogonMethod(httpClient, initmethod);
|
|
|
|
((PostMethod) logonMethod).addParameter("username", userName);
|
|
|
|
((PostMethod) logonMethod).addParameter("password", password);
|
|
|
|
logonMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
|
|
|
|
return logonMethod;
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
|
|
|
|
2008-12-25 17:16:24 -05:00
|
|
|
protected void buildMailPath(HttpMethod method) throws HttpException {
|
2008-11-03 20:47:10 -05:00
|
|
|
// 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);
|
2008-12-25 17:16:24 -05:00
|
|
|
mailPath = baseURL.getPath();
|
2008-12-26 16:00:33 -05:00
|
|
|
LOGGER.debug("Base href found in body, mailPath is " + mailPath);
|
2009-01-08 04:58:31 -05:00
|
|
|
buildEmail();
|
2008-12-26 16:00:33 -05:00
|
|
|
LOGGER.debug("Current user email is " + email);
|
2008-12-22 19:14:41 -05:00
|
|
|
} else {
|
2008-12-25 17:16:24 -05:00
|
|
|
// failover for Exchange 2007 : build standard mailbox link with email
|
2009-01-08 04:58:31 -05:00
|
|
|
buildEmail();
|
2008-12-25 17:16:24 -05:00
|
|
|
mailPath = "/exchange/" + email + "/";
|
2008-12-26 16:00:33 -05:00
|
|
|
LOGGER.debug("Current user email is " + email + ", mailPath is " + mailPath);
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
2009-01-08 04:58:31 -05:00
|
|
|
LOGGER.error("Error parsing main page at " + method.getPath(), e);
|
2008-11-03 20:47:10 -05:00
|
|
|
} finally {
|
|
|
|
if (mainPageReader != null) {
|
|
|
|
try {
|
|
|
|
mainPageReader.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing main page at " + method.getPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
method.releaseConnection();
|
|
|
|
}
|
|
|
|
|
2008-12-25 17:16:24 -05:00
|
|
|
if (mailPath == null) {
|
2008-12-26 16:00:33 -05:00
|
|
|
throw new AuthenticationException("Unable to build mail path, authentication failed: password expired ?");
|
2008-12-22 19:14:41 -05:00
|
|
|
}
|
2009-01-08 04:58:31 -05:00
|
|
|
if (email == null) {
|
|
|
|
throw new AuthenticationException("Unable to get email, authentication failed: password expired ?");
|
|
|
|
}
|
2008-12-22 19:14:41 -05:00
|
|
|
}
|
|
|
|
|
2008-12-04 06:50:31 -05:00
|
|
|
void login() 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-12-04 06:50:31 -05:00
|
|
|
boolean isBasicAuthentication = isBasicAuthentication(poolKey.url);
|
2008-01-09 11:33:57 -05:00
|
|
|
|
2007-04-25 19:29:46 -04:00
|
|
|
// get proxy configuration from setttings properties
|
2008-12-04 06:50:31 -05:00
|
|
|
URL urlObject = new URL(poolKey.url);
|
2006-12-12 18:57:24 -05:00
|
|
|
// webdavresource is unable to create the correct url type
|
|
|
|
HttpURL httpURL;
|
2008-12-04 06:50:31 -05:00
|
|
|
if (poolKey.url.startsWith("http://")) {
|
|
|
|
httpURL = new HttpURL(poolKey.userName, poolKey.password,
|
2006-12-12 18:57:24 -05:00
|
|
|
urlObject.getHost(), urlObject.getPort());
|
2008-12-04 06:50:31 -05:00
|
|
|
} else if (poolKey.url.startsWith("https://")) {
|
|
|
|
httpURL = new HttpsURL(poolKey.userName, poolKey.password,
|
2006-12-12 18:57:24 -05:00
|
|
|
urlObject.getHost(), urlObject.getPort());
|
|
|
|
} else {
|
2008-12-04 06:50:31 -05:00
|
|
|
throw new IllegalArgumentException("Invalid URL: " + poolKey.url);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
wdr = new WebdavResource(httpURL, WebdavResource.NOACTION, 0);
|
|
|
|
|
|
|
|
// 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-12-04 06:50:31 -05:00
|
|
|
HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, poolKey.url);
|
2008-08-24 12:01:03 -04:00
|
|
|
|
2008-01-09 11:33:57 -05:00
|
|
|
if (!isBasicAuthentication) {
|
2008-12-04 06:50:31 -05:00
|
|
|
method = formLogin(httpClient, method, poolKey.userName, poolKey.password);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2008-12-01 07:38:49 -05:00
|
|
|
int status = method.getStatusCode();
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-19 08:01:14 -05:00
|
|
|
if (status == HttpStatus.SC_UNAUTHORIZED) {
|
|
|
|
throw new AuthenticationException("Authentication failed: invalid user or password");
|
|
|
|
} else 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();
|
2008-12-01 07:38:49 -05:00
|
|
|
if (queryString != null && queryString.contains("reason=2")) {
|
2008-11-03 20:47:10 -05:00
|
|
|
method.releaseConnection();
|
2008-12-04 06:50:31 -05:00
|
|
|
if (poolKey.userName != null && poolKey.userName.contains("\\")) {
|
2008-12-19 08:01:14 -05:00
|
|
|
throw new AuthenticationException("Authentication failed: invalid user or password");
|
2008-11-26 19:56:28 -05:00
|
|
|
} else {
|
2008-12-19 08:01:14 -05:00
|
|
|
throw new AuthenticationException("Authentication failed: invalid user or password, " +
|
2008-11-26 19:56:28 -05:00
|
|
|
"retry with domain\\user");
|
|
|
|
}
|
2008-08-24 12:01:03 -04:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-25 17:16:24 -05:00
|
|
|
buildMailPath(method);
|
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);
|
2008-12-09 03:54:08 -05:00
|
|
|
getWellKnownFolders();
|
2006-12-12 18:57:24 -05:00
|
|
|
// set current folder to Inbox
|
|
|
|
wdr.setPath(URIUtil.getPath(inboxUrl));
|
|
|
|
|
2008-12-19 08:01:14 -05:00
|
|
|
} catch (AuthenticationException exc) {
|
|
|
|
LOGGER.error(exc.toString());
|
|
|
|
throw exc;
|
2008-12-17 10:27:56 -05:00
|
|
|
} catch (IOException 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);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-09 03:54:08 -05:00
|
|
|
protected void getWellKnownFolders() throws IOException {
|
|
|
|
// Retrieve well known URLs
|
|
|
|
Enumeration foldersEnum = wdr.propfindMethod(0, WELL_KNOWN_FOLDERS);
|
|
|
|
if (!foldersEnum.hasMoreElements()) {
|
|
|
|
throw new IOException("Unable to get mail folders");
|
|
|
|
}
|
|
|
|
ResponseEntity inboxResponse = (ResponseEntity) foldersEnum.
|
|
|
|
nextElement();
|
|
|
|
Enumeration inboxPropsEnum = inboxResponse.getProperties();
|
|
|
|
if (!inboxPropsEnum.hasMoreElements()) {
|
|
|
|
throw new IOException("Unable to get mail folders");
|
|
|
|
}
|
|
|
|
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 ("sentitems".equals(inboxProp.getLocalName())) {
|
|
|
|
sentitemsUrl = URIUtil.decode(inboxProp.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("sendmsg".equals(inboxProp.getLocalName())) {
|
|
|
|
sendmsgUrl = URIUtil.decode(inboxProp.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("drafts".equals(inboxProp.getLocalName())) {
|
|
|
|
draftsUrl = URIUtil.decode(inboxProp.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("calendar".equals(inboxProp.getLocalName())) {
|
|
|
|
calendarUrl = URIUtil.decode(inboxProp.getPropertyAsString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOGGER.debug("Inbox URL : " + inboxUrl +
|
|
|
|
" Trash URL : " + deleteditemsUrl +
|
|
|
|
" Sent URL : " + sentitemsUrl +
|
|
|
|
" Send URL : " + sendmsgUrl +
|
|
|
|
" Drafts URL : " + draftsUrl +
|
|
|
|
" Calendar URL : " + calendarUrl
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
*
|
2009-02-09 05:12:09 -05:00
|
|
|
* @param folderUrl Exchange folder URL
|
|
|
|
* @param messageName message name
|
|
|
|
* @param properties message properties (flags)
|
|
|
|
* @param messageBody mail body
|
2007-03-14 07:55:37 -04:00
|
|
|
* @throws java.io.IOException when unable to create message
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
2009-02-05 11:06:34 -05:00
|
|
|
public void createMessage(String folderUrl, String messageName, HashMap<String, String> properties, String messageBody) throws IOException {
|
2009-02-03 16:02:33 -05:00
|
|
|
String messageUrl = URIUtil.encodePathQuery(folderUrl + "/" + messageName + ".EML");
|
2009-02-03 18:54:48 -05:00
|
|
|
PropPatchMethod patchMethod;
|
2009-02-03 11:13:00 -05:00
|
|
|
// create the message first as draft
|
2009-02-03 18:54:48 -05:00
|
|
|
if (properties.containsKey("draft")) {
|
|
|
|
patchMethod = new PropPatchMethod(messageUrl);
|
|
|
|
try {
|
|
|
|
// update message with blind carbon copy and other flags
|
|
|
|
addProperties(patchMethod, properties);
|
|
|
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(patchMethod);
|
|
|
|
if (statusCode != HttpStatus.SC_MULTI_STATUS) {
|
|
|
|
throw new IOException("Unable to create message " + messageUrl + ": " + statusCode + " " + patchMethod.getStatusLine());
|
2009-02-03 11:13:00 -05:00
|
|
|
}
|
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
} finally {
|
|
|
|
patchMethod.releaseConnection();
|
|
|
|
}
|
2009-02-03 11:13:00 -05:00
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
PutMethod putmethod = new PutMethod(messageUrl);
|
2008-11-26 19:56:28 -05:00
|
|
|
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
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
if (code != HttpStatus.SC_OK && code != HttpStatus.SC_CREATED) {
|
2009-01-18 16:41:05 -05:00
|
|
|
throw new IOException("Unable to create message " + messageUrl + ": " + code + " " + putmethod.getStatusLine());
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} 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
|
|
|
}
|
2008-12-09 05:10:38 -05:00
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
// add bcc and other properties
|
2009-02-04 15:38:07 -05:00
|
|
|
if (properties.size() > 0) {
|
|
|
|
patchMethod = new PropPatchMethod(messageUrl);
|
|
|
|
try {
|
|
|
|
// update message with blind carbon copy and other flags
|
|
|
|
addProperties(patchMethod, properties);
|
|
|
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(patchMethod);
|
|
|
|
if (statusCode != HttpStatus.SC_MULTI_STATUS) {
|
|
|
|
throw new IOException("Unable to patch message " + messageUrl + ": " + statusCode + " " + patchMethod.getStatusLine());
|
|
|
|
}
|
2009-02-03 18:54:48 -05:00
|
|
|
|
2009-02-04 15:38:07 -05:00
|
|
|
} finally {
|
|
|
|
patchMethod.releaseConnection();
|
|
|
|
}
|
2009-02-03 18:54:48 -05:00
|
|
|
}
|
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();
|
2009-01-26 18:51:08 -05:00
|
|
|
} else if ("read".equals(localName)) {
|
|
|
|
message.read = "1".equals(prop.getPropertyAsString());
|
2009-02-03 09:38:05 -05:00
|
|
|
} else if ("x10830003".equals(localName)) {
|
|
|
|
message.junk = "1".equals(prop.getPropertyAsString());
|
|
|
|
} else if ("x10900003".equals(localName)) {
|
|
|
|
message.flagged = "2".equals(prop.getPropertyAsString());
|
2009-02-03 11:13:00 -05:00
|
|
|
} else if ("x0E070003".equals(localName)) {
|
|
|
|
message.draft = "9".equals(prop.getPropertyAsString());
|
2009-02-03 18:54:48 -05:00
|
|
|
} else if ("x10810003".equals(localName)) {
|
2009-02-04 19:18:29 -05:00
|
|
|
message.answered = "102".equals(prop.getPropertyAsString()) || "103".equals(prop.getPropertyAsString());
|
|
|
|
message.forwarded = "104".equals(prop.getPropertyAsString());
|
2009-02-04 18:33:29 -05:00
|
|
|
} else if ("isdeleted".equals(localName)) {
|
2009-02-04 18:06:30 -05:00
|
|
|
message.deleted = "1".equals(prop.getPropertyAsString());
|
2009-01-23 05:30:48 -05:00
|
|
|
} else if ("message-id".equals(prop.getLocalName())) {
|
|
|
|
message.messageId = prop.getPropertyAsString();
|
2009-02-02 02:19:57 -05:00
|
|
|
if (message.messageId.startsWith("<") && message.messageId.endsWith(">")) {
|
2009-02-02 18:29:44 -05:00
|
|
|
message.messageId = message.messageId.substring(1, message.messageId.length() - 1);
|
2009-02-02 02:19:57 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Message getMessage(String messageUrl) throws IOException {
|
|
|
|
|
2007-05-09 18:32:01 -04:00
|
|
|
Enumeration messageEnum = wdr.propfindMethod(messageUrl, 0, MESSAGE_REQUEST_PROPERTIES);
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-04 06:50:31 -05:00
|
|
|
if ((wdr.getStatusCode() != HttpURLConnection.HTTP_OK)
|
2007-05-09 18:32:01 -04:00
|
|
|
|| !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
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
protected void addProperties(PropPatchMethod patchMethod, Map<String, String> properties) {
|
|
|
|
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
|
|
|
if ("read".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("read", entry.getValue(), "e", "urn:schemas:httpmail:");
|
|
|
|
} else if ("junk".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("x10830003", entry.getValue(), "f", "http://schemas.microsoft.com/mapi/proptag/");
|
|
|
|
} else if ("flagged".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("x10900003", entry.getValue(), "f", "http://schemas.microsoft.com/mapi/proptag/");
|
|
|
|
} else if ("answered".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("x10810003", entry.getValue(), "f", "http://schemas.microsoft.com/mapi/proptag/");
|
2009-02-04 19:18:29 -05:00
|
|
|
if ("102".equals(entry.getValue())) {
|
|
|
|
patchMethod.addPropertyToSet("x10800003", "261", "f", "http://schemas.microsoft.com/mapi/proptag/");
|
|
|
|
}
|
|
|
|
} else if ("forwarded".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("x10810003", entry.getValue(), "f", "http://schemas.microsoft.com/mapi/proptag/");
|
|
|
|
if ("104".equals(entry.getValue())) {
|
|
|
|
patchMethod.addPropertyToSet("x10800003", "262", "f", "http://schemas.microsoft.com/mapi/proptag/");
|
|
|
|
}
|
2009-02-03 18:54:48 -05:00
|
|
|
} else if ("bcc".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("bcc", entry.getValue(), "b", "urn:schemas:mailheader:");
|
|
|
|
} else if ("draft".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("x0E070003", entry.getValue(), "f", "http://schemas.microsoft.com/mapi/proptag/");
|
2009-02-04 18:06:30 -05:00
|
|
|
} else if ("deleted".equals(entry.getKey())) {
|
2009-02-04 18:33:29 -05:00
|
|
|
patchMethod.addPropertyToSet("isdeleted", entry.getValue(), "d", "DAV:");
|
2009-02-05 12:15:30 -05:00
|
|
|
} else if ("datereceived".equals(entry.getKey())) {
|
|
|
|
patchMethod.addPropertyToSet("datereceived", entry.getValue(), "e", "urn:schemas:httpmail:");
|
2009-02-03 18:54:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-26 18:51:08 -05:00
|
|
|
public void updateMessage(Message message, Map<String, String> properties) throws IOException {
|
|
|
|
PropPatchMethod patchMethod = new PropPatchMethod(URIUtil.encodePathQuery(message.messageUrl));
|
2009-02-02 02:19:57 -05:00
|
|
|
try {
|
2009-02-03 18:54:48 -05:00
|
|
|
addProperties(patchMethod, properties);
|
2009-02-02 02:19:57 -05:00
|
|
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(patchMethod);
|
|
|
|
if (statusCode != HttpStatus.SC_MULTI_STATUS) {
|
|
|
|
throw new IOException("Unable to update message properties");
|
|
|
|
}
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
patchMethod.releaseConnection();
|
|
|
|
}
|
2009-01-26 18:51:08 -05:00
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
|
|
|
|
public MessageList getAllMessages(String folderName) throws IOException {
|
2009-01-22 19:59:41 -05:00
|
|
|
String folderUrl = getFolderPath(folderName);
|
2009-02-02 02:19:57 -05:00
|
|
|
MessageList messages = new MessageList();
|
2008-12-17 10:27:56 -05:00
|
|
|
String searchRequest = "Select \"DAV:uid\", \"http://schemas.microsoft.com/mapi/proptag/x0e080003\"" +
|
2009-02-03 09:38:05 -05:00
|
|
|
" ,\"http://schemas.microsoft.com/mapi/proptag/x10830003\", \"http://schemas.microsoft.com/mapi/proptag/x10900003\"" +
|
2009-02-03 18:54:48 -05:00
|
|
|
" ,\"http://schemas.microsoft.com/mapi/proptag/x0E070003\", \"http://schemas.microsoft.com/mapi/proptag/x10810003\"" +
|
2009-02-04 18:33:29 -05:00
|
|
|
" ,\"urn:schemas:mailheader:message-id\", \"urn:schemas:httpmail:read\", \"DAV:isdeleted\"" +
|
2009-01-22 19:59:41 -05:00
|
|
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderUrl + "\"')\n" +
|
2009-02-04 18:33:29 -05:00
|
|
|
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = False\n" +
|
2008-12-17 10:27:56 -05:00
|
|
|
" ORDER BY \"urn:schemas:httpmail:date\" ASC";
|
2009-01-22 19:59:41 -05:00
|
|
|
Enumeration folderEnum = DavGatewayHttpClientFacade.executeSearchMethod(wdr.retrieveSessionInstance(), folderUrl, searchRequest);
|
2008-12-01 12:56:18 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
while (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
Message message = buildMessage(entity);
|
|
|
|
messages.add(message);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
Collections.sort(messages);
|
2006-12-12 18:57:24 -05:00
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
2009-01-23 08:01:46 -05:00
|
|
|
public List<Folder> getSubFolders(String folderName, boolean recursive) throws IOException {
|
|
|
|
String mode = recursive ? "DEEP" : "SHALLOW";
|
2009-01-22 19:59:41 -05:00
|
|
|
List<Folder> folders = new ArrayList<Folder>();
|
|
|
|
String searchRequest = "Select \"DAV:nosubs\", \"DAV:hassubs\"," +
|
|
|
|
" \"DAV:hassubs\",\"urn:schemas:httpmail:unreadcount\"" +
|
2009-01-23 08:01:46 -05:00
|
|
|
" FROM Scope('" + mode + " TRAVERSAL OF \"" + getFolderPath(folderName) + "\"')\n" +
|
2009-02-02 02:19:57 -05:00
|
|
|
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = True \n" +
|
2009-01-26 18:51:08 -05:00
|
|
|
" AND (\"DAV:contentclass\"='urn:content-classes:mailfolder' OR \"DAV:contentclass\"='urn:content-classes:folder')";
|
2009-01-22 19:59:41 -05:00
|
|
|
Enumeration folderEnum = DavGatewayHttpClientFacade.executeSearchMethod(wdr.retrieveSessionInstance(), mailPath, searchRequest);
|
|
|
|
|
|
|
|
while (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
|
|
|
folders.add(buildFolder(entity));
|
|
|
|
}
|
|
|
|
return folders;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected Folder buildFolder(ResponseEntity entity) throws URIException {
|
|
|
|
String href = URIUtil.decode(entity.getHref());
|
|
|
|
Folder folder = new Folder();
|
|
|
|
Enumeration enumeration = entity.getProperties();
|
|
|
|
while (enumeration.hasMoreElements()) {
|
|
|
|
Property property = (Property) enumeration.nextElement();
|
|
|
|
if ("hassubs".equals(property.getLocalName())) {
|
|
|
|
folder.hasChildren = "1".equals(property.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("nosubs".equals(property.getLocalName())) {
|
|
|
|
folder.noInferiors = "1".equals(property.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("objectcount".equals(property.getLocalName())) {
|
|
|
|
folder.objectCount = Integer.parseInt(property.getPropertyAsString());
|
|
|
|
}
|
|
|
|
if ("unreadcount".equals(property.getLocalName())) {
|
|
|
|
folder.unreadCount = Integer.parseInt(property.getPropertyAsString());
|
|
|
|
}
|
2009-01-23 05:30:48 -05:00
|
|
|
if ("getlastmodified".equals(property.getLocalName())) {
|
|
|
|
try {
|
|
|
|
folder.lastModified = dateParser.parse(property.getPropertyAsString()).getTime();
|
|
|
|
} catch (ParseException e) {
|
2009-01-23 08:01:46 -05:00
|
|
|
LOGGER.error("Unable to parse date: " + e);
|
2009-01-23 05:30:48 -05:00
|
|
|
}
|
|
|
|
}
|
2009-01-22 19:59:41 -05:00
|
|
|
}
|
|
|
|
if (href.endsWith("/")) {
|
2009-01-23 08:01:46 -05:00
|
|
|
href = href.substring(0, href.length() - 1);
|
2009-01-22 19:59:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// replace well known folder names
|
|
|
|
if (href.startsWith(inboxUrl)) {
|
|
|
|
folder.folderUrl = href.replaceFirst(inboxUrl, "INBOX");
|
|
|
|
} else if (href.startsWith(sentitemsUrl)) {
|
|
|
|
folder.folderUrl = href.replaceFirst(sentitemsUrl, "Sent");
|
|
|
|
} else if (href.startsWith(draftsUrl)) {
|
|
|
|
folder.folderUrl = href.replaceFirst(draftsUrl, "Drafts");
|
|
|
|
} else if (href.startsWith(deleteditemsUrl)) {
|
|
|
|
folder.folderUrl = href.replaceFirst(deleteditemsUrl, "Trash");
|
|
|
|
} else {
|
2009-01-23 08:01:46 -05:00
|
|
|
int index = href.indexOf(mailPath.substring(0, mailPath.length() - 1));
|
2009-01-22 19:59:41 -05:00
|
|
|
if (index >= 0) {
|
|
|
|
folder.folderUrl = href.substring(index + mailPath.length());
|
|
|
|
} else {
|
|
|
|
throw new URIException("Invalid folder url: " + folder.folderUrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
2008-12-05 05:01:24 -05:00
|
|
|
public void purgeOldestTrashAndSentMessages() throws IOException {
|
2007-02-07 06:51:08 -05:00
|
|
|
int keepDelay = Settings.getIntProperty("davmail.keepDelay");
|
2008-12-05 05:01:24 -05:00
|
|
|
if (keepDelay != 0) {
|
|
|
|
purgeOldestFolderMessages(deleteditemsUrl, keepDelay);
|
|
|
|
}
|
|
|
|
// this is a new feature, default is : do nothing
|
|
|
|
int sentKeepDelay = Settings.getIntProperty("davmail.sentKeepDelay");
|
|
|
|
if (sentKeepDelay != 0) {
|
|
|
|
purgeOldestFolderMessages(sentitemsUrl, sentKeepDelay);
|
2007-02-07 06:51:08 -05:00
|
|
|
}
|
2008-12-05 05:01:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void purgeOldestFolderMessages(String folderUrl, int keepDelay) throws IOException {
|
2007-02-07 06:32:44 -05:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -keepDelay);
|
2008-12-05 05:01:24 -05:00
|
|
|
LOGGER.debug("Delete messages in " + folderUrl + " since " + cal.getTime());
|
2008-12-05 04:29:44 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
String searchRequest = "Select \"DAV:uid\"" +
|
2008-12-05 05:01:24 -05:00
|
|
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderUrl + "\"')\n" +
|
2008-12-05 04:29:44 -05:00
|
|
|
" WHERE \"DAV:isfolder\" = False\n" +
|
2008-12-17 10:27:56 -05:00
|
|
|
" AND \"DAV:getlastmodified\" < '" + dateFormatter.format(cal.getTime()) + "'\n";
|
|
|
|
Enumeration folderEnum = DavGatewayHttpClientFacade.executeSearchMethod(wdr.retrieveSessionInstance(), folderUrl, searchRequest);
|
2008-12-05 04:29:44 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
while (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
|
|
|
String messageUrl = URIUtil.decode(entity.getHref());
|
2008-12-05 04:29:44 -05:00
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
LOGGER.debug("Delete " + messageUrl);
|
|
|
|
wdr.deleteMethod(messageUrl);
|
2007-02-07 06:32:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-09 05:10:38 -05:00
|
|
|
public void sendMessage(List<String> recipients, BufferedReader reader) throws IOException {
|
2006-12-12 18:57:24 -05:00
|
|
|
String line = reader.readLine();
|
2008-12-09 05:10:38 -05:00
|
|
|
StringBuilder mailBuffer = new StringBuilder();
|
|
|
|
StringBuilder recipientBuffer = new StringBuilder();
|
|
|
|
boolean inHeader = true;
|
|
|
|
boolean inRecipientHeader = false;
|
|
|
|
while (!".".equals(line)) {
|
2009-02-12 06:33:19 -05:00
|
|
|
mailBuffer.append(line).append((char) 13).append((char) 10);
|
2006-12-12 18:57:24 -05:00
|
|
|
line = reader.readLine();
|
|
|
|
|
2008-12-09 05:10:38 -05:00
|
|
|
if (inHeader && line.length() == 0) {
|
|
|
|
inHeader = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inRecipientHeader = inRecipientHeader && line.startsWith(" ");
|
|
|
|
|
|
|
|
if ((inHeader && line.length() >= 3) || inRecipientHeader) {
|
|
|
|
String prefix = line.substring(0, 3).toLowerCase();
|
|
|
|
if ("to:".equals(prefix) || "cc:".equals(prefix) || inRecipientHeader) {
|
|
|
|
inRecipientHeader = true;
|
|
|
|
recipientBuffer.append(line);
|
2008-12-02 05:20:46 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
2008-12-23 09:09:46 -05:00
|
|
|
// Exchange 2007 : skip From: header
|
|
|
|
if ((inHeader && line.length() >= 5)) {
|
|
|
|
String prefix = line.substring(0, 5).toLowerCase();
|
|
|
|
if ("from:".equals(prefix)) {
|
|
|
|
line = reader.readLine();
|
|
|
|
}
|
|
|
|
}
|
2008-12-09 05:10:38 -05:00
|
|
|
// patch thunderbird html in reply for correct outlook display
|
2009-01-13 16:30:29 -05:00
|
|
|
if (line != null && line.startsWith("<head>")) {
|
2009-02-12 06:33:19 -05:00
|
|
|
mailBuffer.append(line).append((char) 13).append((char) 10);
|
2008-12-17 10:27:56 -05:00
|
|
|
line = " <style> blockquote { display: block; margin: 1em 0px; padding-left: 1em; border-left: solid; border-color: blue; border-width: thin;}</style>";
|
2008-12-09 05:10:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// remove visible recipients from list
|
|
|
|
List<String> visibleRecipients = new ArrayList<String>();
|
|
|
|
for (String recipient : recipients) {
|
|
|
|
if (recipientBuffer.indexOf(recipient) >= 0) {
|
|
|
|
visibleRecipients.add(recipient);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
recipients.removeAll(visibleRecipients);
|
|
|
|
|
|
|
|
StringBuffer bccBuffer = new StringBuffer();
|
|
|
|
for (String recipient : recipients) {
|
|
|
|
if (bccBuffer.length() > 0) {
|
|
|
|
bccBuffer.append(',');
|
|
|
|
}
|
|
|
|
bccBuffer.append("<");
|
|
|
|
bccBuffer.append(recipient);
|
|
|
|
bccBuffer.append(">");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-02-03 18:54:48 -05:00
|
|
|
String bcc = bccBuffer.toString();
|
|
|
|
HashMap<String, String> properties = new HashMap<String, String>();
|
|
|
|
if (bcc.length() > 0) {
|
|
|
|
properties.put("bcc", bcc);
|
|
|
|
}
|
|
|
|
|
2008-12-19 07:27:33 -05:00
|
|
|
String messageName = UUID.randomUUID().toString();
|
|
|
|
|
2009-02-05 11:06:34 -05:00
|
|
|
createMessage(draftsUrl, messageName, properties, mailBuffer.toString());
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
// warning : slide library expects *unencoded* urls
|
2008-12-23 09:09:46 -05:00
|
|
|
String tempUrl = draftsUrl + "/" + messageName + ".EML";
|
2006-12-12 18:57:24 -05:00
|
|
|
boolean sent = wdr.moveMethod(tempUrl, sendmsgUrl);
|
|
|
|
if (!sent) {
|
|
|
|
throw new IOException("Unable to send message: " + wdr.getStatusCode()
|
|
|
|
+ " " + wdr.getStatusMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-01-22 19:59:41 -05:00
|
|
|
public String getFolderPath(String folderName) {
|
|
|
|
String folderPath;
|
|
|
|
if (folderName.startsWith("INBOX")) {
|
|
|
|
folderPath = folderName.replaceFirst("INBOX", inboxUrl);
|
|
|
|
} else if (folderName.startsWith("Trash")) {
|
|
|
|
folderPath = folderName.replaceFirst("Trash", deleteditemsUrl);
|
|
|
|
} else if (folderName.startsWith("Drafts")) {
|
|
|
|
folderPath = folderName.replaceFirst("Drafts", draftsUrl);
|
|
|
|
} else if (folderName.startsWith("Sent")) {
|
|
|
|
folderPath = folderName.replaceFirst("Sent", sentitemsUrl);
|
2009-01-23 08:01:46 -05:00
|
|
|
// absolute folder path
|
2009-01-23 08:03:32 -05:00
|
|
|
} else if (folderName.startsWith("/")) {
|
2009-01-22 19:59:41 -05:00
|
|
|
folderPath = folderName;
|
|
|
|
} else {
|
|
|
|
folderPath = mailPath + folderName;
|
|
|
|
}
|
|
|
|
return folderPath;
|
|
|
|
}
|
|
|
|
|
2007-11-08 12:12:36 -05:00
|
|
|
/**
|
|
|
|
* Select current folder.
|
2008-12-23 09:09:46 -05:00
|
|
|
* Folder name can be logical names INBOX, DRAFTS or TRASH (translated to local names),
|
2007-11-08 12:12:36 -05:00
|
|
|
* 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
|
|
|
|
*/
|
2009-01-22 19:59:41 -05:00
|
|
|
public Folder getFolder(String folderName) throws IOException {
|
2006-12-12 18:57:24 -05:00
|
|
|
Vector<String> reqProps = new Vector<String>();
|
2009-01-22 19:59:41 -05:00
|
|
|
reqProps.add("DAV:hassubs");
|
|
|
|
reqProps.add("DAV:nosubs");
|
|
|
|
reqProps.add("DAV:objectcount");
|
2006-12-12 18:57:24 -05:00
|
|
|
reqProps.add("urn:schemas:httpmail:unreadcount");
|
2009-01-23 05:30:48 -05:00
|
|
|
reqProps.add("DAV:getlastmodified");
|
2009-01-23 06:20:20 -05:00
|
|
|
Enumeration folderEnum = wdr.propfindMethod(getFolderPath(folderName), 0, reqProps);
|
|
|
|
Folder folder = null;
|
2006-12-12 18:57:24 -05:00
|
|
|
if (folderEnum.hasMoreElements()) {
|
|
|
|
ResponseEntity entity = (ResponseEntity) folderEnum.nextElement();
|
2009-01-22 19:59:41 -05:00
|
|
|
folder = buildFolder(entity);
|
2009-01-23 06:20:20 -05:00
|
|
|
folder.folderName = folderName;
|
2009-01-23 08:01:46 -05:00
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
2009-01-23 08:01:46 -05:00
|
|
|
public void createFolder(String folderName) throws IOException {
|
|
|
|
String folderPath = getFolderPath(folderName);
|
|
|
|
PropPatchMethod method = new PropPatchMethod(folderPath) {
|
|
|
|
public String getName() {
|
|
|
|
return "MKCOL";
|
|
|
|
}
|
|
|
|
};
|
2009-01-23 08:03:32 -05:00
|
|
|
method.addPropertyToSet("outlookfolderclass", "IPF.Note", "ex", "http://schemas.microsoft.com/exchange/");
|
2009-01-26 18:51:08 -05:00
|
|
|
try {
|
2009-02-02 02:19:57 -05:00
|
|
|
wdr.retrieveSessionInstance().executeMethod(method);
|
|
|
|
// ok or alredy exists
|
|
|
|
if (method.getStatusCode() != HttpStatus.SC_MULTI_STATUS && method.getStatusCode() != HttpStatus.SC_METHOD_NOT_ALLOWED) {
|
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(method.getStatusCode());
|
|
|
|
ex.setReason(method.getStatusText());
|
|
|
|
throw ex;
|
|
|
|
}
|
2009-01-26 18:51:08 -05:00
|
|
|
} finally {
|
|
|
|
method.releaseConnection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-02 18:29:44 -05:00
|
|
|
public void copyMessage(String messageUrl, String targetName) throws IOException {
|
|
|
|
String targetPath = getFolderPath(targetName) + messageUrl.substring(messageUrl.lastIndexOf('/'));
|
|
|
|
CopyMethod method = new CopyMethod(URIUtil.encodePath(messageUrl),
|
|
|
|
URIUtil.encodePath(targetPath));
|
|
|
|
method.setOverwrite(false);
|
|
|
|
method.addRequestHeader("Allow-Rename", "t");
|
|
|
|
try {
|
|
|
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(method);
|
|
|
|
if (statusCode == HttpStatus.SC_PRECONDITION_FAILED) {
|
|
|
|
throw new HttpException("Unable to move message, target already exists");
|
|
|
|
} else if (statusCode != HttpStatus.SC_CREATED) {
|
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(method.getStatusCode());
|
|
|
|
ex.setReason(method.getStatusText());
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
method.releaseConnection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-26 18:51:08 -05:00
|
|
|
public void moveFolder(String folderName, String targetName) throws IOException {
|
|
|
|
String folderPath = getFolderPath(folderName);
|
|
|
|
String targetPath = getFolderPath(targetName);
|
|
|
|
MoveMethod method = new MoveMethod(URIUtil.encodePath(folderPath),
|
2009-02-02 02:19:57 -05:00
|
|
|
URIUtil.encodePath(targetPath));
|
2009-01-26 18:51:08 -05:00
|
|
|
method.setOverwrite(false);
|
2009-02-02 02:19:57 -05:00
|
|
|
try {
|
|
|
|
int statusCode = wdr.retrieveSessionInstance().executeMethod(method);
|
|
|
|
if (statusCode == HttpStatus.SC_PRECONDITION_FAILED) {
|
|
|
|
throw new HttpException("Unable to move folder, target already exists");
|
|
|
|
} else if (statusCode != HttpStatus.SC_CREATED) {
|
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(method.getStatusCode());
|
|
|
|
ex.setReason(method.getStatusText());
|
|
|
|
throw ex;
|
|
|
|
}
|
2009-01-26 18:51:08 -05:00
|
|
|
} finally {
|
|
|
|
method.releaseConnection();
|
|
|
|
}
|
2009-01-23 08:01:46 -05:00
|
|
|
}
|
|
|
|
|
2008-12-02 05:20:46 -05:00
|
|
|
public static class Folder {
|
2006-12-12 18:57:24 -05:00
|
|
|
public String folderUrl;
|
2009-01-22 19:59:41 -05:00
|
|
|
public int objectCount;
|
2006-12-12 18:57:24 -05:00
|
|
|
public int unreadCount;
|
2009-01-22 19:59:41 -05:00
|
|
|
public boolean hasChildren;
|
|
|
|
public boolean noInferiors;
|
2009-01-23 05:30:48 -05:00
|
|
|
public long lastModified;
|
2009-01-23 06:20:20 -05:00
|
|
|
public String folderName;
|
2009-01-22 19:59:41 -05:00
|
|
|
|
|
|
|
public String getFlags() {
|
|
|
|
if (noInferiors) {
|
|
|
|
return "\\NoInferiors";
|
|
|
|
} else if (hasChildren) {
|
|
|
|
return "\\HasChildren";
|
|
|
|
} else {
|
|
|
|
return "\\HasNoChildren";
|
|
|
|
}
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-02-02 02:19:57 -05:00
|
|
|
public class Message implements Comparable {
|
2006-12-12 18:57:24 -05:00
|
|
|
public String messageUrl;
|
|
|
|
public String uid;
|
|
|
|
public int size;
|
2009-01-23 05:30:48 -05:00
|
|
|
public String messageId;
|
2009-01-26 18:51:08 -05:00
|
|
|
public boolean read;
|
2009-02-02 18:29:44 -05:00
|
|
|
public boolean deleted;
|
2009-02-03 09:38:05 -05:00
|
|
|
public boolean junk;
|
|
|
|
public boolean flagged;
|
2009-02-03 11:13:00 -05:00
|
|
|
public boolean draft;
|
2009-02-03 18:54:48 -05:00
|
|
|
public boolean answered;
|
2009-02-04 19:18:29 -05:00
|
|
|
public boolean forwarded;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2009-02-02 02:19:57 -05:00
|
|
|
public long getUidAsLong() {
|
|
|
|
byte[] decodedValue = Base64.decode(uid.getBytes());
|
|
|
|
|
|
|
|
long result = 0;
|
|
|
|
for (int i = 2; i < 9; i++) {
|
|
|
|
result = result << 8;
|
|
|
|
result |= decodedValue[i] & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-02-02 18:29:44 -05:00
|
|
|
public String getImapFlags() {
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
if (read) {
|
|
|
|
buffer.append("\\Seen ");
|
|
|
|
}
|
|
|
|
if (deleted) {
|
2009-02-03 09:38:05 -05:00
|
|
|
buffer.append("\\Deleted ");
|
|
|
|
}
|
|
|
|
if (flagged) {
|
|
|
|
buffer.append("\\Flagged ");
|
|
|
|
}
|
|
|
|
if (junk) {
|
|
|
|
buffer.append("Junk ");
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
2009-02-03 11:13:00 -05:00
|
|
|
if (draft) {
|
|
|
|
buffer.append("\\Draft ");
|
|
|
|
}
|
2009-02-03 18:54:48 -05:00
|
|
|
if (answered) {
|
|
|
|
buffer.append("\\Answered ");
|
|
|
|
}
|
2009-02-04 19:18:29 -05:00
|
|
|
if (forwarded) {
|
|
|
|
buffer.append("$Forwarded ");
|
|
|
|
}
|
2009-02-03 09:38:05 -05:00
|
|
|
return buffer.toString().trim();
|
2009-02-02 18:29:44 -05:00
|
|
|
}
|
|
|
|
|
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-30 13:05:36 -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 {
|
2009-02-03 16:02:33 -05:00
|
|
|
wdr.deleteMethod(messageUrl);
|
|
|
|
if (wdr.getStatusCode() != HttpStatus.SC_OK) {
|
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(wdr.getStatusCode());
|
|
|
|
ex.setReason(wdr.getStatusMessage());
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void moveToTrash() throws IOException {
|
2006-12-12 18:57:24 -05:00
|
|
|
String destination = deleteditemsUrl + messageUrl.substring(messageUrl.lastIndexOf("/"));
|
2007-05-09 18:32:01 -04:00
|
|
|
LOGGER.debug("Deleting : " + messageUrl + " to " + destination);
|
2009-02-05 11:28:54 -05:00
|
|
|
// mark message as read
|
2009-02-09 05:12:09 -05:00
|
|
|
HashMap<String, String> properties = new HashMap<String, String>();
|
2009-02-05 11:28:54 -05:00
|
|
|
properties.put("read", "1");
|
|
|
|
updateMessage(this, properties);
|
|
|
|
MoveMethod method = new MoveMethod(URIUtil.encodePath(messageUrl),
|
2009-02-09 05:12:09 -05:00
|
|
|
URIUtil.encodePath(destination));
|
2009-02-05 11:28:54 -05:00
|
|
|
method.addRequestHeader("Overwrite", "f");
|
|
|
|
method.addRequestHeader("Allow-rename", "t");
|
|
|
|
method.setDebug(4);
|
|
|
|
|
|
|
|
int status = wdr.retrieveSessionInstance().executeMethod(method);
|
|
|
|
if (status != HttpStatus.SC_CREATED) {
|
2009-02-09 05:12:09 -05:00
|
|
|
HttpException ex = new HttpException();
|
2009-02-05 11:28:54 -05:00
|
|
|
ex.setReasonCode(status);
|
|
|
|
ex.setReason(method.getStatusText());
|
|
|
|
throw ex;
|
|
|
|
}
|
2009-02-09 05:12:09 -05:00
|
|
|
if (method.getResponseHeader("Location") != null) {
|
2009-02-05 11:28:54 -05:00
|
|
|
destination = method.getResponseHeader("Location").getValue();
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2009-02-02 02:19:57 -05:00
|
|
|
public int compareTo(Object message) {
|
2009-02-02 18:29:44 -05:00
|
|
|
return (int) (getUidAsLong() - ((Message) message).getUidAsLong());
|
2009-02-02 02:19:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class MessageList extends ArrayList<Message> {
|
|
|
|
HashMap<Long, Message> uidMessageMap = new HashMap<Long, Message>();
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean add(Message message) {
|
|
|
|
uidMessageMap.put(message.getUidAsLong(), message);
|
|
|
|
return super.add(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Message getByUid(long uid) {
|
|
|
|
return uidMessageMap.get(uid);
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
public WebdavResource getWebDavResource() {
|
2008-11-26 19:56:28 -05:00
|
|
|
return wdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public class Event {
|
|
|
|
protected String href;
|
|
|
|
protected String etag;
|
|
|
|
|
|
|
|
public String getICS() throws IOException {
|
2008-12-17 10:27:56 -05:00
|
|
|
LOGGER.debug("Get event: " + href);
|
2008-11-26 19:56:28 -05:00
|
|
|
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) {
|
2008-12-17 10:27:56 -05:00
|
|
|
LOGGER.warn("Unable to get event at " + href + " status: " + status);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
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();
|
|
|
|
}
|
2008-12-09 10:49:20 -05:00
|
|
|
return fixICS(buffer.toString(), true);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2008-12-17 10:27:56 -05:00
|
|
|
public String getPath() {
|
2008-11-26 19:56:28 -05:00
|
|
|
return href.substring(calendarUrl.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getEtag() {
|
|
|
|
return etag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Event> getAllEvents() throws IOException {
|
2008-12-05 11:33:40 -05:00
|
|
|
int caldavPastDelay = Settings.getIntProperty("davmail.caldavPastDelay", Integer.MAX_VALUE);
|
2008-12-05 05:45:23 -05:00
|
|
|
String dateCondition = "";
|
2008-12-05 11:33:40 -05:00
|
|
|
if (caldavPastDelay != Integer.MAX_VALUE) {
|
2008-12-05 05:45:23 -05:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -caldavPastDelay);
|
2008-12-05 09:53:38 -05:00
|
|
|
dateCondition = " AND \"urn:schemas:calendar:dtstart\" > '" + dateFormatter.format(cal.getTime()) + "'\n";
|
2008-12-05 05:45:23 -05:00
|
|
|
}
|
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
List<Event> events = new ArrayList<Event>();
|
|
|
|
String searchRequest = "<?xml version=\"1.0\"?>\n" +
|
|
|
|
"<d:searchrequest xmlns:d=\"DAV:\">\n" +
|
2008-12-09 10:49:20 -05:00
|
|
|
" <d:sql> Select \"DAV:getetag\", \"urn:schemas:calendar:instancetype\"" +
|
2008-11-26 19:56:28 -05:00
|
|
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + calendarUrl + "\"')\n" +
|
2009-01-08 05:35:06 -05:00
|
|
|
" WHERE (\"urn:schemas:calendar:instancetype\" = 1\n" +
|
|
|
|
" OR (\"urn:schemas:calendar:instancetype\" = 0\n" +
|
2008-12-05 05:45:23 -05:00
|
|
|
dateCondition +
|
2009-01-08 05:35:06 -05:00
|
|
|
" )) AND \"DAV:contentclass\" = 'urn:content-classes:appointment'\n" +
|
2008-12-05 11:33:40 -05:00
|
|
|
" ORDER BY \"urn:schemas:calendar:dtstart\" DESC\n" +
|
2008-11-26 19:56:28 -05:00
|
|
|
" </d:sql>\n" +
|
|
|
|
"</d:searchrequest>";
|
2008-12-09 03:54:08 -05:00
|
|
|
SearchMethod searchMethod = new SearchMethod(URIUtil.encodePath(calendarUrl), searchRequest);
|
2008-11-26 19:56:28 -05:00
|
|
|
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()) {
|
2008-12-17 10:27:56 -05:00
|
|
|
events.add(buildEvent((ResponseEntity) calendarEnum.nextElement()));
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
searchMethod.releaseConnection();
|
|
|
|
}
|
|
|
|
return events;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Event getEvent(String path) throws IOException {
|
2008-12-09 08:24:17 -05:00
|
|
|
Enumeration calendarEnum = wdr.propfindMethod(calendarUrl + "/" + URIUtil.decode(path), 0, EVENT_REQUEST_PROPERTIES);
|
2008-11-26 19:56:28 -05:00
|
|
|
if (!calendarEnum.hasMoreElements()) {
|
|
|
|
throw new IOException("Unable to get calendar event");
|
|
|
|
}
|
2008-12-17 10:27:56 -05:00
|
|
|
return buildEvent((ResponseEntity) calendarEnum.
|
|
|
|
nextElement());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected Event buildEvent(ResponseEntity calendarResponse) throws URIException {
|
|
|
|
Event event = new Event();
|
2008-11-26 19:56:28 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-12-09 10:49:20 -05:00
|
|
|
protected String fixICS(String icsBody, boolean fromServer) throws IOException {
|
2008-12-09 08:24:17 -05:00
|
|
|
// first pass : detect
|
2008-12-09 10:49:20 -05:00
|
|
|
class AllDayState {
|
2008-12-17 10:27:56 -05:00
|
|
|
boolean isAllDay = false;
|
|
|
|
boolean hasCdoAllDay = false;
|
|
|
|
boolean isCdoAllDay = false;
|
2008-12-09 10:49:20 -05:00
|
|
|
}
|
|
|
|
List<AllDayState> allDayStates = new ArrayList<AllDayState>();
|
|
|
|
AllDayState currentAllDayState = new AllDayState();
|
2008-12-09 08:24:17 -05:00
|
|
|
BufferedReader reader = null;
|
|
|
|
try {
|
2009-01-04 09:21:50 -05:00
|
|
|
reader = new ICSBufferedReader(new StringReader(icsBody));
|
2008-12-09 08:24:17 -05:00
|
|
|
String line;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
int index = line.indexOf(':');
|
|
|
|
if (index >= 0) {
|
|
|
|
String key = line.substring(0, index);
|
|
|
|
String value = line.substring(index + 1);
|
|
|
|
if ("DTSTART;VALUE=DATE".equals(key)) {
|
2008-12-09 10:49:20 -05:00
|
|
|
currentAllDayState.isAllDay = true;
|
2008-12-09 08:24:17 -05:00
|
|
|
} else if ("X-MICROSOFT-CDO-ALLDAYEVENT".equals(key)) {
|
2008-12-09 10:49:20 -05:00
|
|
|
currentAllDayState.hasCdoAllDay = true;
|
|
|
|
currentAllDayState.isCdoAllDay = "TRUE".equals(value);
|
|
|
|
} else if ("END:VEVENT".equals(line)) {
|
|
|
|
allDayStates.add(currentAllDayState);
|
|
|
|
currentAllDayState = new AllDayState();
|
2008-12-09 08:24:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (reader != null) {
|
|
|
|
reader.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// second pass : fix
|
2008-12-09 10:49:20 -05:00
|
|
|
int count = 0;
|
2009-01-04 09:21:50 -05:00
|
|
|
ICSBufferedWriter result = new ICSBufferedWriter();
|
2008-12-09 08:24:17 -05:00
|
|
|
try {
|
2009-01-04 09:21:50 -05:00
|
|
|
reader = new ICSBufferedReader(new StringReader(icsBody));
|
2008-12-09 08:24:17 -05:00
|
|
|
String line;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
2008-12-09 10:49:20 -05:00
|
|
|
if (currentAllDayState.isAllDay && "X-MICROSOFT-CDO-ALLDAYEVENT:FALSE".equals(line)) {
|
2008-12-09 08:24:17 -05:00
|
|
|
line = "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE";
|
2008-12-09 10:49:20 -05:00
|
|
|
} else if ("END:VEVENT".equals(line) && currentAllDayState.isAllDay && !currentAllDayState.hasCdoAllDay) {
|
2009-01-04 09:21:50 -05:00
|
|
|
result.writeLine("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE");
|
2008-12-09 10:49:20 -05:00
|
|
|
} else if (!currentAllDayState.isAllDay && "X-MICROSOFT-CDO-ALLDAYEVENT:TRUE".equals(line)) {
|
2008-12-09 08:24:17 -05:00
|
|
|
line = "X-MICROSOFT-CDO-ALLDAYEVENT:FALSE";
|
2008-12-09 10:49:20 -05:00
|
|
|
} else if (fromServer && currentAllDayState.isCdoAllDay && line.startsWith("DTSTART;TZID")) {
|
2008-12-09 10:06:52 -05:00
|
|
|
line = getAllDayLine(line);
|
2008-12-09 10:49:20 -05:00
|
|
|
} else if (fromServer && currentAllDayState.isCdoAllDay && line.startsWith("DTEND;TZID")) {
|
2008-12-09 10:06:52 -05:00
|
|
|
line = getAllDayLine(line);
|
2008-12-09 10:49:20 -05:00
|
|
|
} else if ("BEGIN:VEVENT".equals(line)) {
|
|
|
|
currentAllDayState = allDayStates.get(count++);
|
2008-12-09 08:24:17 -05:00
|
|
|
}
|
2009-01-04 09:21:50 -05:00
|
|
|
result.writeLine(line);
|
2008-12-09 08:24:17 -05:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
reader.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
2008-12-09 10:06:52 -05:00
|
|
|
protected String getAllDayLine(String line) throws IOException {
|
|
|
|
int keyIndex = line.indexOf(';');
|
|
|
|
int valueIndex = line.lastIndexOf(':');
|
|
|
|
int valueEndIndex = line.lastIndexOf('T');
|
2008-12-17 10:27:56 -05:00
|
|
|
if (keyIndex < 0 || valueIndex < 0 || valueEndIndex < 0) {
|
2008-12-09 10:06:52 -05:00
|
|
|
throw new IOException("Invalid ICS line: " + line);
|
|
|
|
}
|
|
|
|
String dateValue = line.substring(valueIndex + 1);
|
|
|
|
String key = line.substring(0, keyIndex);
|
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
|
|
|
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
|
|
Date date;
|
|
|
|
try {
|
|
|
|
date = dateFormat.parse(dateValue);
|
|
|
|
} catch (ParseException e) {
|
|
|
|
throw new IOException("Invalid ICS line: " + line);
|
|
|
|
}
|
|
|
|
if ("DTEND".equals(key)) {
|
|
|
|
date.setTime(date.getTime() - 1);
|
|
|
|
}
|
2008-12-17 10:27:56 -05:00
|
|
|
return line.substring(0, keyIndex) + ";VALUE=DATE:" + line.substring(valueIndex + 1, valueEndIndex);
|
2008-12-09 10:06:52 -05:00
|
|
|
}
|
|
|
|
|
2009-02-09 05:12:09 -05:00
|
|
|
|
2009-02-11 20:33:49 -05:00
|
|
|
public class EventResult {
|
|
|
|
public int status;
|
|
|
|
public String etag;
|
|
|
|
}
|
|
|
|
|
2009-02-09 05:12:09 -05:00
|
|
|
public int sendEvent(String icsBody) throws IOException {
|
|
|
|
String messageUrl = URIUtil.encodePathQuery(draftsUrl + "/" + UUID.randomUUID().toString() + ".EML");
|
2009-02-12 06:33:19 -05:00
|
|
|
int status = internalCreateOrUpdateEvent(messageUrl, "urn:content-classes:calendarmessage", icsBody, null, null).status;
|
2009-02-09 05:12:09 -05:00
|
|
|
if (status != HttpStatus.SC_CREATED) {
|
|
|
|
return status;
|
|
|
|
} else {
|
|
|
|
boolean sent = wdr.moveMethod(messageUrl, sendmsgUrl);
|
|
|
|
if (!sent) {
|
|
|
|
throw new IOException("Unable to send message: " + wdr.getStatusCode()
|
|
|
|
+ " " + wdr.getStatusMessage());
|
|
|
|
}
|
|
|
|
return wdr.getStatusCode();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-11 20:33:49 -05:00
|
|
|
public EventResult createOrUpdateEvent(String path, String icsBody, String etag, String noneMatch) throws IOException {
|
2008-11-26 19:56:28 -05:00
|
|
|
String messageUrl = URIUtil.encodePathQuery(calendarUrl + "/" + URIUtil.decode(path));
|
2009-02-12 06:33:19 -05:00
|
|
|
return internalCreateOrUpdateEvent(messageUrl, "urn:content-classes:appointment", icsBody, etag, noneMatch);
|
2009-02-09 05:12:09 -05:00
|
|
|
}
|
|
|
|
|
2009-02-12 06:33:19 -05:00
|
|
|
protected String getICSMethod(String icsBody) throws IOException {
|
|
|
|
int methodIndex = icsBody.indexOf("METHOD:");
|
|
|
|
if (methodIndex < 0) {
|
|
|
|
return "REQUEST";
|
|
|
|
}
|
|
|
|
int startIndex = methodIndex + "METHOD:".length();
|
|
|
|
int endIndex = icsBody.indexOf("\r", startIndex);
|
|
|
|
if (endIndex < 0) {
|
|
|
|
return "REQUEST";
|
|
|
|
}
|
|
|
|
return icsBody.substring(startIndex, endIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String getRecipients(String icsBody) throws IOException {
|
|
|
|
StringBuilder recipients = new StringBuilder();
|
|
|
|
BufferedReader reader = null;
|
|
|
|
try {
|
|
|
|
reader = new ICSBufferedReader(new StringReader(icsBody));
|
|
|
|
String line;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
int index = line.indexOf(':');
|
|
|
|
if (index >= 0) {
|
|
|
|
String key = line.substring(0, index);
|
|
|
|
String value = line.substring(index + 1);
|
|
|
|
int semiColon = key.indexOf(';');
|
|
|
|
if (semiColon >= 0) {
|
|
|
|
key = key.substring(0, semiColon);
|
|
|
|
}
|
|
|
|
if ("ATTENDEE".equals(key)) {
|
|
|
|
int colonIndex = value.indexOf(':');
|
|
|
|
if (colonIndex >= 0) {
|
|
|
|
value = value.substring(colonIndex + 1);
|
|
|
|
}
|
|
|
|
// exclude current user from recipients
|
|
|
|
if (!email.equalsIgnoreCase(value)) {
|
|
|
|
if (recipients.length() > 0) {
|
|
|
|
recipients.append(", ");
|
|
|
|
}
|
|
|
|
recipients.append(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (reader != null) {
|
|
|
|
reader.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return recipients.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected EventResult internalCreateOrUpdateEvent(String messageUrl, String contentClass, String icsBody, String etag, String noneMatch) throws IOException {
|
2009-02-09 05:12:09 -05:00
|
|
|
String uid = UUID.randomUUID().toString();
|
2008-11-26 19:56:28 -05:00
|
|
|
PutMethod putmethod = new PutMethod(messageUrl);
|
|
|
|
putmethod.setRequestHeader("Translate", "f");
|
|
|
|
putmethod.setRequestHeader("Overwrite", "f");
|
|
|
|
if (etag != null) {
|
|
|
|
putmethod.setRequestHeader("If-Match", etag);
|
|
|
|
}
|
2009-01-18 17:48:53 -05:00
|
|
|
if (noneMatch != null) {
|
|
|
|
putmethod.setRequestHeader("If-None-Match", noneMatch);
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
2009-02-12 06:33:19 -05:00
|
|
|
String method = getICSMethod(icsBody);
|
2008-11-26 19:56:28 -05:00
|
|
|
StringBuilder body = new StringBuilder();
|
2009-02-12 06:33:19 -05:00
|
|
|
body.append("Content-Transfer-Encoding: 7bit\r\n" +
|
|
|
|
"Content-class: ").append(contentClass).append("\r\n");
|
|
|
|
if ("urn:content-classes:calendarmessage".equals(contentClass)) {
|
|
|
|
// need to parse attendees to build recipients
|
|
|
|
body.append("To: ").append(getRecipients(icsBody)).append("\r\n");
|
|
|
|
}
|
|
|
|
body.append("MIME-Version: 1.0\r\n" +
|
|
|
|
"Content-Type: multipart/alternative;\r\n" +
|
|
|
|
"\tboundary=\"----=_NextPart_").append(uid).append("\"\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"This is a multi-part message in MIME format.\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"------=_NextPart_").append(uid).append("\r\n" +
|
|
|
|
"Content-class: ").append(contentClass).append("\r\n" +
|
|
|
|
"Content-Type: text/calendar;\r\n" +
|
|
|
|
"\tmethod=").append(method).append(";\r\n" +
|
|
|
|
"\tcharset=\"utf-8\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: 8bit\r\n\r\n");
|
2008-12-09 10:49:20 -05:00
|
|
|
body.append(new String(fixICS(icsBody, false).getBytes("UTF-8"), "ISO-8859-1"));
|
2009-02-12 06:33:19 -05:00
|
|
|
body.append("------=_NextPart_").append(uid).append("--\r\n");
|
2008-11-26 19:56:28 -05:00
|
|
|
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) {
|
2008-12-05 03:47:54 -05:00
|
|
|
LOGGER.warn("Unable to create or update message " + status + " " + putmethod.getStatusLine());
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
putmethod.releaseConnection();
|
|
|
|
}
|
2009-02-11 20:33:49 -05:00
|
|
|
EventResult eventResult = new EventResult();
|
|
|
|
eventResult.status = status;
|
|
|
|
if (putmethod.getResponseHeader("GetETag") != null) {
|
2009-02-12 06:33:19 -05:00
|
|
|
eventResult.etag = putmethod.getResponseHeader("GetETag").getValue();
|
2009-02-11 20:33:49 -05:00
|
|
|
}
|
|
|
|
return eventResult;
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-02-02 18:29:44 -05:00
|
|
|
|
|
|
|
public void deleteFolder(String path) throws IOException {
|
|
|
|
wdr.deleteMethod(getFolderPath(path));
|
|
|
|
int status = wdr.getStatusCode();
|
|
|
|
if (status != HttpStatus.SC_OK) {
|
|
|
|
HttpException ex = new HttpException();
|
|
|
|
ex.setReasonCode(status);
|
|
|
|
ex.setReason(wdr.getStatusMessage());
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int deleteMessage(String path) throws IOException {
|
|
|
|
wdr.deleteMethod(path);
|
|
|
|
return wdr.getStatusCode();
|
|
|
|
}
|
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
public int deleteEvent(String path) throws IOException {
|
2008-11-29 09:24:12 -05:00
|
|
|
wdr.deleteMethod(calendarUrl + "/" + URIUtil.decode(path));
|
2008-12-01 12:56:18 -05:00
|
|
|
return wdr.getStatusCode();
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-01-02 18:17:04 -05:00
|
|
|
public String getCalendarCtag() throws IOException {
|
2009-01-02 18:58:34 -05:00
|
|
|
String ctag = null;
|
2008-11-26 19:56:28 -05:00
|
|
|
Enumeration calendarEnum = wdr.propfindMethod(calendarUrl, 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())) {
|
2009-01-02 18:58:34 -05:00
|
|
|
ctag = property.getPropertyAsString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctag == null) {
|
|
|
|
throw new IOException("Unable to get calendar ctag");
|
|
|
|
}
|
|
|
|
return ctag;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getCalendarEtag() throws IOException {
|
|
|
|
String etag = null;
|
|
|
|
Enumeration calendarEnum = wdr.propfindMethod(calendarUrl, 0, EVENT_REQUEST_PROPERTIES);
|
|
|
|
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 ("getetag".equals(property.getLocalName())) {
|
2008-11-26 19:56:28 -05:00
|
|
|
etag = property.getPropertyAsString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (etag == null) {
|
|
|
|
throw new IOException("Unable to get calendar etag");
|
|
|
|
}
|
|
|
|
return etag;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-01-08 04:58:31 -05:00
|
|
|
* Get current Exchange alias name from login name
|
|
|
|
*
|
|
|
|
* @return user name
|
|
|
|
* @throws java.io.IOException on error
|
|
|
|
*/
|
|
|
|
protected String getAliasFromLogin() throws IOException {
|
|
|
|
// Exchange 2007 : userName is login without domain
|
|
|
|
String userName = poolKey.userName;
|
|
|
|
int index = userName.indexOf('\\');
|
|
|
|
if (index >= 0) {
|
|
|
|
userName = userName.substring(index + 1);
|
|
|
|
}
|
|
|
|
return userName;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get current Exchange alias name from mailbox name
|
2008-11-26 19:56:28 -05:00
|
|
|
*
|
|
|
|
* @return user name
|
|
|
|
* @throws java.io.IOException on error
|
|
|
|
*/
|
2009-01-08 04:58:31 -05:00
|
|
|
protected String getAliasFromMailPath() throws IOException {
|
2008-12-25 17:16:24 -05:00
|
|
|
if (mailPath == null) {
|
2009-01-08 04:58:31 -05:00
|
|
|
throw new IOException("Empty mail path");
|
|
|
|
}
|
|
|
|
int index = mailPath.lastIndexOf("/", mailPath.length() - 2);
|
|
|
|
if (index >= 0 && mailPath.endsWith("/")) {
|
|
|
|
return mailPath.substring(index + 1, mailPath.length() - 1);
|
2008-11-26 19:56:28 -05:00
|
|
|
} else {
|
2009-01-08 04:58:31 -05:00
|
|
|
throw new IOException("Invalid mail path: " + mailPath);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-08 04:58:31 -05:00
|
|
|
public String getEmail(String alias) throws IOException {
|
|
|
|
String emailResult = null;
|
|
|
|
GetMethod getMethod = new GetMethod("/public/?Cmd=galfind&AN=" + alias);
|
2008-11-26 19:56:28 -05:00
|
|
|
try {
|
|
|
|
int status = wdr.retrieveSessionInstance().executeMethod(getMethod);
|
|
|
|
if (status != HttpStatus.SC_OK) {
|
|
|
|
throw new IOException("Unable to get user email from: " + getMethod.getPath());
|
|
|
|
}
|
2008-12-28 16:48:33 -05:00
|
|
|
Map<String, Map<String, String>> results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "item", "AN");
|
2009-01-08 04:58:31 -05:00
|
|
|
Map<String, String> result = results.get(alias.toLowerCase());
|
2008-12-28 16:48:33 -05:00
|
|
|
if (result != null) {
|
2009-01-08 04:58:31 -05:00
|
|
|
emailResult = result.get("EM");
|
2008-12-28 16:48:33 -05:00
|
|
|
}
|
2009-01-08 04:58:31 -05:00
|
|
|
|
2008-11-26 19:56:28 -05:00
|
|
|
} finally {
|
|
|
|
getMethod.releaseConnection();
|
|
|
|
}
|
2009-01-08 04:58:31 -05:00
|
|
|
return emailResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void buildEmail() throws IOException {
|
|
|
|
// first try to get email from login name
|
|
|
|
email = getEmail(getAliasFromLogin());
|
|
|
|
// failover: use mailbox name as alias
|
2008-11-26 19:56:28 -05:00
|
|
|
if (email == null) {
|
2009-01-08 04:58:31 -05:00
|
|
|
email = getEmail(getAliasFromMailPath());
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
2009-01-08 04:58:31 -05:00
|
|
|
if (email == null) {
|
|
|
|
throw new IOException("Unable to get user email with alias " + getAliasFromLogin() + " or " + getAliasFromMailPath());
|
|
|
|
}
|
|
|
|
// normalize email
|
|
|
|
email = email.toLowerCase();
|
2008-12-25 17:16:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get current user email
|
|
|
|
*
|
|
|
|
* @return user email
|
|
|
|
* @throws java.io.IOException on error
|
|
|
|
*/
|
|
|
|
public String getEmail() throws IOException {
|
2008-11-26 19:56:28 -05:00
|
|
|
return email;
|
|
|
|
}
|
|
|
|
|
2008-12-03 06:38:35 -05:00
|
|
|
/**
|
|
|
|
* Search users in global address book
|
|
|
|
*
|
2008-12-05 09:53:38 -05:00
|
|
|
* @param searchAttribute exchange search attribute
|
|
|
|
* @param searchValue search value
|
2008-12-03 06:38:35 -05:00
|
|
|
* @return List of users
|
2008-12-05 09:53:38 -05:00
|
|
|
* @throws java.io.IOException on error
|
2008-12-03 06:38:35 -05:00
|
|
|
*/
|
|
|
|
public Map<String, Map<String, String>> galFind(String searchAttribute, String searchValue) throws IOException {
|
2008-12-05 09:53:38 -05:00
|
|
|
Map<String, Map<String, String>> results;
|
2008-12-03 06:38:35 -05:00
|
|
|
GetMethod getMethod = new GetMethod(URIUtil.encodePathQuery("/public/?Cmd=galfind&" + searchAttribute + "=" + searchValue));
|
|
|
|
try {
|
|
|
|
int status = wdr.retrieveSessionInstance().executeMethod(getMethod);
|
|
|
|
if (status != HttpStatus.SC_OK) {
|
|
|
|
throw new IOException(status + "Unable to find users from: " + getMethod.getURI());
|
|
|
|
}
|
|
|
|
results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "item", "AN");
|
|
|
|
} finally {
|
|
|
|
getMethod.releaseConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void galLookup(Map<String, String> person) {
|
2008-12-23 10:20:50 -05:00
|
|
|
if (!disableGalLookup) {
|
|
|
|
GetMethod getMethod = null;
|
|
|
|
try {
|
|
|
|
getMethod = new GetMethod(URIUtil.encodePathQuery("/public/?Cmd=gallookup&ADDR=" + person.get("EM")));
|
|
|
|
int status = wdr.retrieveSessionInstance().executeMethod(getMethod);
|
|
|
|
if (status != HttpStatus.SC_OK) {
|
|
|
|
throw new IOException(status + "Unable to find users from: " + getMethod.getURI());
|
|
|
|
}
|
|
|
|
Map<String, Map<String, String>> results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "person", "alias");
|
|
|
|
// add detailed information
|
|
|
|
if (results.size() > 0) {
|
2009-02-09 08:57:10 -05:00
|
|
|
Map<String, String> fullperson = results.get(person.get("AN").toLowerCase());
|
2008-12-23 10:20:50 -05:00
|
|
|
for (Map.Entry<String, String> entry : fullperson.entrySet()) {
|
|
|
|
person.put(entry.getKey(), entry.getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.warn("Unable to gallookup person: " + person + ", disable GalLookup");
|
|
|
|
disableGalLookup = true;
|
|
|
|
} finally {
|
|
|
|
if (getMethod != null) {
|
|
|
|
getMethod.releaseConnection();
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-12 06:33:19 -05:00
|
|
|
public String getFreebusy(String attendee, Map<String, String> valueMap) throws IOException {
|
2008-11-26 19:56:28 -05:00
|
|
|
String result = null;
|
|
|
|
|
|
|
|
String startDateValue = valueMap.get("DTSTART");
|
|
|
|
String endDateValue = valueMap.get("DTEND");
|
|
|
|
if (attendee.startsWith("mailto:")) {
|
|
|
|
attendee = attendee.substring("mailto:".length());
|
|
|
|
}
|
2009-01-15 08:19:24 -05:00
|
|
|
int interval = 15;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
|
|
|
SimpleDateFormat icalParser = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
|
2009-01-15 08:19:24 -05:00
|
|
|
icalParser.setTimeZone(GMT_TIMEZONE);
|
2008-11-26 19:56:28 -05:00
|
|
|
|
|
|
|
SimpleDateFormat shortIcalParser = new SimpleDateFormat("yyyyMMdd");
|
2009-01-15 08:19:24 -05:00
|
|
|
shortIcalParser.setTimeZone(GMT_TIMEZONE);
|
2008-11-26 19:56:28 -05:00
|
|
|
|
|
|
|
SimpleDateFormat owaFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
2009-01-15 08:19:24 -05:00
|
|
|
owaFormatter.setTimeZone(GMT_TIMEZONE);
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2008-12-01 12:56:18 -05:00
|
|
|
String url;
|
|
|
|
Date startDate;
|
|
|
|
Date endDate;
|
2008-11-26 19:56:28 -05:00
|
|
|
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("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());
|
|
|
|
}
|
|
|
|
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);
|
2009-01-18 16:41:05 -05:00
|
|
|
if (fbdata.length() > 0) {
|
|
|
|
Calendar currentCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
|
|
|
currentCal.setTime(startDate);
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2009-01-18 16:41:05 -05:00
|
|
|
StringBuilder busyBuffer = new StringBuilder();
|
|
|
|
boolean isBusy = fbdata.charAt(0) != '0' && fbdata.charAt(0) != '4';
|
|
|
|
if (isBusy) {
|
2008-11-26 19:56:28 -05:00
|
|
|
busyBuffer.append(icalParser.format(currentCal.getTime()));
|
|
|
|
}
|
2009-01-18 16:41:05 -05:00
|
|
|
boolean knownAttendee = fbdata.charAt(0) != '4';
|
|
|
|
for (int i = 1; i < fbdata.length(); i++) {
|
|
|
|
knownAttendee = knownAttendee || fbdata.charAt(i) != '4';
|
|
|
|
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') && fbdata.charAt(0) != '4') {
|
|
|
|
// non busy -> busy
|
|
|
|
if (busyBuffer.length() > 0) {
|
|
|
|
busyBuffer.append(',');
|
|
|
|
}
|
|
|
|
busyBuffer.append(icalParser.format(currentCal.getTime()));
|
|
|
|
}
|
|
|
|
isBusy = fbdata.charAt(i) != '0' && fbdata.charAt(0) != '4';
|
|
|
|
}
|
|
|
|
// still busy at end
|
|
|
|
if (isBusy) {
|
|
|
|
busyBuffer.append('/').append(icalParser.format(currentCal.getTime()));
|
|
|
|
}
|
|
|
|
if (knownAttendee) {
|
|
|
|
result = busyBuffer.toString();
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
getMethod.releaseConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|