/*
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
* Copyright (C) 2010 Mickael Guessant
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package davmail.exchange.dav;
import davmail.exception.DavMailAuthenticationException;
import davmail.exception.DavMailException;
import davmail.exchange.ExchangeSession;
import davmail.http.DavGatewayHttpClientFacade;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.xml.Namespace;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Webdav Exchange adapter.
* Compatible with Exchange 2003 and 2007 with webdav available.
*/
public class DavExchangeSession extends ExchangeSession {
/**
* @inheritDoc
*/
public DavExchangeSession(String url, String userName, String password) throws IOException {
super(url, userName, password);
}
@Override
protected void buildSessionInfo(HttpMethod method) throws DavMailException {
buildMailPath(method);
// get base http mailbox http urls
getWellKnownFolders();
}
static final String BASE_HREF = " attributeMap = new HashMap();
static {
attributeMap.put("folderclass", "http://schemas.microsoft.com/exchange/outlookfolderclass");
attributeMap.put("contentclass", "DAV:contentclass");
}
static final Map operatorMap = new HashMap();
static {
operatorMap.put(Operator.IsEqualTo, "=");
}
protected static class AttributeCondition extends ExchangeSession.AttributeCondition {
protected AttributeCondition(String attributeName, Operator operator, String value) {
super(attributeName, operator, value);
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append('"').append(attributeMap.get(attributeName)).append('"');
buffer.append(operatorMap.get(operator));
buffer.append('\'').append(value).append('\'');
}
}
protected static class IsNullCondition extends ExchangeSession.IsNullCondition {
protected IsNullCondition(String attributeName) {
super(attributeName);
}
@Override
public void appendTo(StringBuilder buffer) {
buffer.append('"').append(attributeMap.get(attributeName)).append('"');
buffer.append(" is null");
}
}
@Override
public Condition and(Condition... condition) {
return new MultiCondition(Operator.And, condition);
}
@Override
public Condition or(Condition... condition) {
return new MultiCondition(Operator.Or, condition);
}
@Override
public Condition not(Condition condition) {
return new NotCondition(condition);
}
@Override
public Condition equals(String attributeName, String value) {
return new AttributeCondition(attributeName, Operator.IsEqualTo, value);
}
@Override
public Condition isNull(String attributeName) {
return new IsNullCondition(attributeName);
}
protected Folder buildFolder(MultiStatusResponse entity) throws IOException {
String href = URIUtil.decode(entity.getHref());
Folder folder = new Folder();
DavPropertySet properties = entity.getProperties(HttpStatus.SC_OK);
folder.folderClass = getPropertyIfExists(properties, "outlookfolderclass", SCHEMAS_EXCHANGE);
folder.hasChildren = "1".equals(getPropertyIfExists(properties, "hassubs", DAV));
folder.noInferiors = "1".equals(getPropertyIfExists(properties, "nosubs", DAV));
folder.unreadCount = getIntPropertyIfExists(properties, "unreadcount", URN_SCHEMAS_HTTPMAIL);
folder.ctag = getPropertyIfExists(properties, "contenttag", Namespace.getNamespace("http://schemas.microsoft.com/repl/"));
folder.etag = getPropertyIfExists(properties, "x30080040", SCHEMAS_MAPI_PROPTAG);
// replace well known folder names
if (href.startsWith(inboxUrl)) {
folder.folderPath = href.replaceFirst(inboxUrl, INBOX);
} else if (href.startsWith(sentitemsUrl)) {
folder.folderPath = href.replaceFirst(sentitemsUrl, SENT);
} else if (href.startsWith(draftsUrl)) {
folder.folderPath = href.replaceFirst(draftsUrl, DRAFTS);
} else if (href.startsWith(deleteditemsUrl)) {
folder.folderPath = href.replaceFirst(deleteditemsUrl, TRASH);
} else if (href.startsWith(calendarUrl)) {
folder.folderPath = href.replaceFirst(calendarUrl, CALENDAR);
} else if (href.startsWith(contactsUrl)) {
folder.folderPath = href.replaceFirst(contactsUrl, CONTACTS);
} else {
int index = href.indexOf(mailPath.substring(0, mailPath.length() - 1));
if (index >= 0) {
if (index + mailPath.length() > href.length()) {
folder.folderPath = "";
} else {
folder.folderPath = href.substring(index + mailPath.length());
}
} else {
try {
URI folderURI = new URI(href, false);
folder.folderPath = folderURI.getPath();
} catch (URIException e) {
throw new DavMailException("EXCEPTION_INVALID_FOLDER_URL", href);
}
}
}
if (folder.folderPath.endsWith("/")) {
folder.folderPath = folder.folderPath.substring(0, folder.folderPath.length() - 1);
}
return folder;
}
/**
* @inheritDoc
*/
@Override
public Folder getFolder(String folderName) throws IOException {
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(
httpClient, URIUtil.encodePath(getFolderPath(folderName)), 0, FOLDER_PROPERTIES);
Folder folder = null;
if (responses.length > 0) {
folder = buildFolder(responses[0]);
folder.folderName = folderName;
}
return folder;
}
/**
* @inheritDoc
*/
@Override
public List getSubFolders(String folderName, Condition condition, boolean recursive) throws IOException {
boolean isPublic = folderName.startsWith("/public");
String mode = (!isPublic && recursive) ? "DEEP" : "SHALLOW";
List folders = new ArrayList();
StringBuilder searchRequest = new StringBuilder();
searchRequest.append("Select \"DAV:nosubs\", \"DAV:hassubs\", \"http://schemas.microsoft.com/exchange/outlookfolderclass\", " +
"\"http://schemas.microsoft.com/repl/contenttag\", \"http://schemas.microsoft.com/mapi/proptag/x30080040\", " +
"\"urn:schemas:httpmail:unreadcount\" FROM Scope('").append(mode).append(" TRAVERSAL OF \"").append(getFolderPath(folderName)).append("\"')\n" +
" WHERE \"DAV:ishidden\" = False AND \"DAV:isfolder\" = True \n");
if (condition != null) {
searchRequest.append(" AND ");
condition.appendTo(searchRequest);
}
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(
httpClient, URIUtil.encodePath(getFolderPath(folderName)), searchRequest.toString());
for (MultiStatusResponse response : responses) {
Folder folder = buildFolder(response);
folders.add(buildFolder(response));
if (isPublic && recursive) {
getSubFolders(folder.folderPath, condition, recursive);
}
}
return folders;
}
}