2009-07-21 04:39:18 -04:00
|
|
|
/*
|
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
|
|
|
* Copyright (C) 2009 Mickael Guessant
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
package davmail.exchange;
|
|
|
|
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.BundleMessage;
|
2009-10-06 17:12:26 -04:00
|
|
|
import davmail.Settings;
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.exception.DavMailAuthenticationException;
|
|
|
|
import davmail.exception.DavMailException;
|
2008-11-03 20:47:10 -05:00
|
|
|
import davmail.http.DavGatewayHttpClientFacade;
|
2009-11-23 04:53:04 -05:00
|
|
|
import davmail.http.DavGatewayOTPPrompt;
|
2009-11-27 18:27:50 -05:00
|
|
|
import davmail.util.StringUtil;
|
2008-11-30 13:05:36 -05:00
|
|
|
import org.apache.commons.httpclient.*;
|
2010-06-16 09:30:36 -04:00
|
|
|
import org.apache.commons.httpclient.methods.GetMethod;
|
|
|
|
import org.apache.commons.httpclient.methods.PostMethod;
|
2009-10-06 17:12:26 -04:00
|
|
|
import org.apache.commons.httpclient.params.HttpClientParams;
|
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;
|
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
|
|
|
|
2010-07-12 12:35:46 -04:00
|
|
|
import javax.mail.Address;
|
2009-02-25 05:23:07 -05:00
|
|
|
import javax.mail.MessagingException;
|
2010-07-12 12:35:46 -04:00
|
|
|
import javax.mail.internet.InternetAddress;
|
2009-02-25 05:23:07 -05:00
|
|
|
import javax.mail.internet.MimeMessage;
|
2010-04-23 09:42:37 -04:00
|
|
|
import javax.mail.util.SharedByteArrayInputStream;
|
2008-11-26 19:56:28 -05:00
|
|
|
import java.io.*;
|
2009-10-06 17:12:26 -04:00
|
|
|
import java.net.NoRouteToHostException;
|
2009-05-05 16:05:57 -04:00
|
|
|
import java.net.UnknownHostException;
|
2006-12-12 18:57:24 -05:00
|
|
|
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)
|
2009-09-28 19:28:23 -04:00
|
|
|
*/
|
2010-06-02 04:46:46 -04:00
|
|
|
public abstract class ExchangeSession {
|
2010-06-29 04:45:06 -04:00
|
|
|
|
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-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Reference GMT timezone to format dates
|
|
|
|
*/
|
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
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
protected static final Set<String> USER_NAME_FIELDS = new HashSet<String>();
|
2009-11-25 16:58:25 -05:00
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
static {
|
|
|
|
USER_NAME_FIELDS.add("username");
|
|
|
|
USER_NAME_FIELDS.add("txtUserName");
|
|
|
|
USER_NAME_FIELDS.add("userid");
|
|
|
|
USER_NAME_FIELDS.add("SafeWordUser");
|
|
|
|
}
|
2009-11-25 16:58:25 -05:00
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
protected static final Set<String> PASSWORD_FIELDS = new HashSet<String>();
|
2009-11-25 16:58:25 -05:00
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
static {
|
|
|
|
PASSWORD_FIELDS.add("password");
|
|
|
|
PASSWORD_FIELDS.add("txtUserPass");
|
|
|
|
PASSWORD_FIELDS.add("pw");
|
|
|
|
PASSWORD_FIELDS.add("basicPassword");
|
|
|
|
}
|
2009-11-25 16:58:25 -05:00
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
protected static final Set<String> TOKEN_FIELDS = new HashSet<String>();
|
2009-11-25 16:58:25 -05:00
|
|
|
|
2009-11-23 04:53:04 -05:00
|
|
|
static {
|
|
|
|
TOKEN_FIELDS.add("SafeWordPassword");
|
|
|
|
}
|
|
|
|
|
2009-03-20 12:05:45 -04:00
|
|
|
protected static final int FREE_BUSY_INTERVAL = 15;
|
|
|
|
|
2010-06-08 06:44:47 -04:00
|
|
|
protected static final String PUBLIC_ROOT = "/public";
|
|
|
|
protected static final String CALENDAR = "calendar";
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Contacts folder logical name
|
|
|
|
*/
|
2010-07-01 08:47:57 -04:00
|
|
|
public static final String CONTACTS = "contacts";
|
2010-06-20 18:12:36 -04:00
|
|
|
protected static final String ADDRESSBOOK = "addressbook";
|
2010-06-08 06:44:47 -04:00
|
|
|
protected static final String INBOX = "INBOX";
|
2010-06-20 18:12:36 -04:00
|
|
|
protected static final String LOWER_CASE_INBOX = "inbox";
|
2010-06-08 06:44:47 -04:00
|
|
|
protected static final String SENT = "Sent";
|
|
|
|
protected static final String DRAFTS = "Drafts";
|
|
|
|
protected static final String TRASH = "Trash";
|
|
|
|
protected static final String JUNK = "Junk";
|
|
|
|
protected static final String UNSENT = "Unsent Messages";
|
|
|
|
|
2010-06-02 04:46:46 -04:00
|
|
|
protected String publicFolderUrl;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Base user mailboxes path (used to select folder)
|
|
|
|
*/
|
2010-06-02 04:46:46 -04:00
|
|
|
protected String mailPath;
|
2010-06-20 18:12:36 -04:00
|
|
|
protected String rootPath;
|
2010-06-02 04:46:46 -04:00
|
|
|
protected String email;
|
2010-06-19 17:35:09 -04:00
|
|
|
protected String alias;
|
2010-07-28 13:11:18 -04:00
|
|
|
/**
|
|
|
|
* Lower case Caldav path to current user mailbox.
|
|
|
|
* /users/<i>email</i>
|
|
|
|
*/
|
|
|
|
protected String currentMailboxPath;
|
2010-06-02 04:46:46 -04:00
|
|
|
protected final HttpClient httpClient;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2009-09-30 17:54:53 -04:00
|
|
|
private final String userName;
|
2009-10-06 17:12:26 -04:00
|
|
|
|
2010-07-27 09:19:28 -04:00
|
|
|
protected static final String YYYY_MM_DD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";
|
2009-04-11 08:27:10 -04:00
|
|
|
private static final String YYYYMMDD_T_HHMMSS_Z = "yyyyMMdd'T'HHmmss'Z'";
|
2010-07-27 09:19:28 -04:00
|
|
|
protected static final String YYYY_MM_DD_T_HHMMSS_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
2010-07-20 12:02:59 -04:00
|
|
|
private static final String YYYY_MM_DD = "yyyy-MM-dd";
|
2009-09-03 19:08:35 -04:00
|
|
|
private static final String YYYY_MM_DD_T_HHMMSS_SSS_Z = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
2008-12-23 10:20:50 -05:00
|
|
|
|
2009-09-08 04:05:05 -04:00
|
|
|
/**
|
|
|
|
* Logon form user name field, default is username.
|
|
|
|
*/
|
|
|
|
private String userNameInput = "username";
|
|
|
|
/**
|
|
|
|
* Logon form password field, default is password.
|
|
|
|
*/
|
|
|
|
private String passwordInput = "password";
|
|
|
|
|
2006-12-12 18:57:24 -05:00
|
|
|
/**
|
|
|
|
* Create an exchange session for the given URL.
|
2009-09-30 17:54:53 -04:00
|
|
|
* The session is established for given userName and password
|
2008-12-04 06:50:31 -05:00
|
|
|
*
|
2009-10-06 17:12:26 -04:00
|
|
|
* @param url Exchange url
|
2009-09-30 17:54:53 -04:00
|
|
|
* @param userName user login name
|
|
|
|
* @param password user password
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException on error
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
2010-05-18 07:50:00 -04:00
|
|
|
public ExchangeSession(String url, String userName, String password) throws IOException {
|
2009-09-30 17:54:53 -04:00
|
|
|
this.userName = userName;
|
2009-03-26 19:29:09 -04:00
|
|
|
try {
|
2009-09-30 17:54:53 -04:00
|
|
|
boolean isBasicAuthentication = isBasicAuthentication(url);
|
2009-03-26 19:29:09 -04:00
|
|
|
|
2009-09-30 17:54:53 -04:00
|
|
|
httpClient = DavGatewayHttpClientFacade.getInstance(url, userName, password);
|
2009-03-26 19:29:09 -04:00
|
|
|
|
|
|
|
// get webmail root url
|
|
|
|
// providing credentials
|
|
|
|
// manually follow redirect
|
2009-09-30 17:54:53 -04:00
|
|
|
HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, url);
|
2009-03-26 19:29:09 -04:00
|
|
|
|
2009-04-25 06:31:33 -04:00
|
|
|
if (isBasicAuthentication) {
|
|
|
|
int status = method.getStatusCode();
|
2009-03-26 19:29:09 -04:00
|
|
|
|
2009-04-25 06:31:33 -04:00
|
|
|
if (status == HttpStatus.SC_UNAUTHORIZED) {
|
|
|
|
method.releaseConnection();
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
|
2009-04-25 06:31:33 -04:00
|
|
|
} else if (status != HttpStatus.SC_OK) {
|
|
|
|
method.releaseConnection();
|
|
|
|
throw DavGatewayHttpClientFacade.buildHttpException(method);
|
2009-03-26 19:29:09 -04:00
|
|
|
}
|
2009-04-25 06:31:33 -04:00
|
|
|
} else {
|
2009-09-30 17:54:53 -04:00
|
|
|
method = formLogin(httpClient, method, userName, password);
|
2009-03-26 19:29:09 -04:00
|
|
|
}
|
|
|
|
|
2010-02-01 17:52:36 -05:00
|
|
|
// avoid 401 roundtrips, only if NTLM is disabled
|
|
|
|
if (!DavGatewayHttpClientFacade.hasNTLM(httpClient)) {
|
|
|
|
httpClient.getParams().setParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
|
|
|
|
}
|
2009-11-01 16:58:04 -05:00
|
|
|
|
2010-06-02 04:46:46 -04:00
|
|
|
buildSessionInfo(method);
|
2009-03-26 19:29:09 -04:00
|
|
|
|
2009-04-27 19:03:58 -04:00
|
|
|
} catch (DavMailAuthenticationException exc) {
|
2010-05-04 04:35:40 -04:00
|
|
|
LOGGER.error(exc.getMessage());
|
2009-03-26 19:29:09 -04:00
|
|
|
throw exc;
|
2009-05-05 16:05:57 -04:00
|
|
|
} catch (UnknownHostException exc) {
|
2009-07-15 18:47:31 -04:00
|
|
|
BundleMessage message = new BundleMessage("EXCEPTION_CONNECT", exc.getClass().getName(), exc.getMessage());
|
2009-05-05 16:05:57 -04:00
|
|
|
ExchangeSession.LOGGER.error(message);
|
|
|
|
throw new DavMailException("EXCEPTION_DAVMAIL_CONFIGURATION", message);
|
2009-03-26 19:29:09 -04:00
|
|
|
} catch (IOException exc) {
|
2009-04-27 19:03:58 -04:00
|
|
|
LOGGER.error(BundleMessage.formatLog("EXCEPTION_EXCHANGE_LOGIN_FAILED", exc));
|
|
|
|
throw new DavMailException("EXCEPTION_EXCHANGE_LOGIN_FAILED", exc);
|
2009-03-26 19:29:09 -04:00
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
LOGGER.debug("Session " + this + " created");
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2010-07-23 04:58:39 -04:00
|
|
|
/**
|
|
|
|
* Format date to exchange search format.
|
|
|
|
*
|
|
|
|
* @param date date object
|
|
|
|
* @return formatted search date
|
|
|
|
*/
|
2010-07-27 09:19:28 -04:00
|
|
|
public abstract String formatSearchDate(Date date);
|
2009-03-19 04:58:55 -04:00
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Return standard zulu date formatter.
|
2010-07-20 10:09:40 -04:00
|
|
|
*
|
2010-07-18 15:59:13 -04:00
|
|
|
* @return zulu date formatter
|
|
|
|
*/
|
2010-07-09 17:25:18 -04:00
|
|
|
public static SimpleDateFormat getZuluDateFormat() {
|
2009-04-11 08:27:10 -04:00
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat(YYYYMMDD_T_HHMMSS_Z, Locale.ENGLISH);
|
2009-04-07 08:37:28 -04:00
|
|
|
dateFormat.setTimeZone(GMT_TIMEZONE);
|
|
|
|
return dateFormat;
|
|
|
|
}
|
|
|
|
|
2010-07-20 12:02:59 -04:00
|
|
|
protected static SimpleDateFormat getVcardBdayFormat() {
|
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD, Locale.ENGLISH);
|
|
|
|
dateFormat.setTimeZone(GMT_TIMEZONE);
|
|
|
|
return dateFormat;
|
|
|
|
}
|
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
protected static SimpleDateFormat getExchangeZuluDateFormat() {
|
2009-04-11 08:27:10 -04:00
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD_T_HHMMSS_Z, Locale.ENGLISH);
|
|
|
|
dateFormat.setTimeZone(GMT_TIMEZONE);
|
|
|
|
return dateFormat;
|
2009-04-07 08:37:28 -04:00
|
|
|
}
|
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
protected static SimpleDateFormat getExchangeZuluDateFormatMillisecond() {
|
2009-09-03 19:08:35 -04:00
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD_T_HHMMSS_SSS_Z, Locale.ENGLISH);
|
|
|
|
dateFormat.setTimeZone(GMT_TIMEZONE);
|
|
|
|
return dateFormat;
|
|
|
|
}
|
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
protected static Date parseDate(String dateString) throws ParseException {
|
2009-04-07 08:37:28 -04:00
|
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
|
|
|
|
dateFormat.setTimeZone(GMT_TIMEZONE);
|
|
|
|
return dateFormat.parse(dateString);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Test if the session expired.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-28 02:39:27 -04:00
|
|
|
* @return true this session expired
|
|
|
|
* @throws NoRouteToHostException on error
|
2009-08-04 16:48:35 -04:00
|
|
|
* @throws UnknownHostException on error
|
2009-07-28 02:39:27 -04:00
|
|
|
*/
|
2010-07-28 13:11:18 -04:00
|
|
|
public boolean isExpired() throws NoRouteToHostException, UnknownHostException {
|
|
|
|
boolean isExpired = false;
|
|
|
|
try {
|
|
|
|
getFolder("");
|
|
|
|
} catch (UnknownHostException exc) {
|
|
|
|
throw exc;
|
|
|
|
} catch (NoRouteToHostException exc) {
|
|
|
|
throw exc;
|
|
|
|
} catch (IOException e) {
|
|
|
|
isExpired = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isExpired;
|
|
|
|
}
|
2008-12-04 06:50:31 -05:00
|
|
|
|
2008-01-09 11:33:57 -05:00
|
|
|
/**
|
|
|
|
* Test authentication mode : form based or basic.
|
|
|
|
*
|
|
|
|
* @param url exchange base URL
|
|
|
|
* @return true if basic authentication detected
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException unable to connect to exchange
|
2008-01-09 11:33:57 -05:00
|
|
|
*/
|
|
|
|
protected boolean isBasicAuthentication(String url) throws IOException {
|
2008-11-03 20:47:10 -05:00
|
|
|
return DavGatewayHttpClientFacade.getHttpStatus(url) == HttpStatus.SC_UNAUTHORIZED;
|
|
|
|
}
|
|
|
|
|
2009-09-02 12:05:03 -04:00
|
|
|
protected String getAbsoluteUri(HttpMethod method, String path) throws URIException {
|
|
|
|
URI uri = method.getURI();
|
|
|
|
if (path != null) {
|
2009-10-27 05:26:12 -04:00
|
|
|
// reset query string
|
|
|
|
uri.setQuery(null);
|
2009-09-02 12:05:03 -04:00
|
|
|
if (path.startsWith("/")) {
|
2009-09-02 12:37:33 -04:00
|
|
|
// path is absolute, replace method path
|
2009-09-02 12:05:03 -04:00
|
|
|
uri.setPath(path);
|
2009-10-29 13:19:00 -04:00
|
|
|
} else if (path.startsWith("http://") || path.startsWith("https://")) {
|
2009-10-25 18:24:53 -04:00
|
|
|
return path;
|
2009-02-27 06:45:38 -05:00
|
|
|
} else {
|
2009-09-02 12:37:33 -04:00
|
|
|
// relative path, build new path
|
2009-09-02 12:05:03 -04:00
|
|
|
String currentPath = method.getPath();
|
|
|
|
int end = currentPath.lastIndexOf('/');
|
|
|
|
if (end >= 0) {
|
|
|
|
uri.setPath(currentPath.substring(0, end + 1) + path);
|
|
|
|
} else {
|
|
|
|
throw new URIException(uri.getURI());
|
|
|
|
}
|
2008-12-19 07:27:33 -05:00
|
|
|
}
|
|
|
|
}
|
2009-09-02 12:05:03 -04:00
|
|
|
return uri.getURI();
|
2008-12-19 07:27:33 -05:00
|
|
|
}
|
|
|
|
|
2009-09-17 10:42:45 -04:00
|
|
|
protected String getScriptBasedFormURL(HttpMethod initmethod, String pathQuery) throws URIException {
|
|
|
|
URI initmethodURI = initmethod.getURI();
|
|
|
|
int queryIndex = pathQuery.indexOf('?');
|
|
|
|
if (queryIndex >= 0) {
|
|
|
|
if (queryIndex > 0) {
|
2009-09-28 19:28:23 -04:00
|
|
|
// update path
|
|
|
|
String newPath = pathQuery.substring(0, queryIndex);
|
|
|
|
if (newPath.startsWith("/")) {
|
|
|
|
// absolute path
|
|
|
|
initmethodURI.setPath(newPath);
|
|
|
|
} else {
|
|
|
|
String currentPath = initmethodURI.getPath();
|
|
|
|
int folderIndex = currentPath.lastIndexOf('/');
|
|
|
|
if (folderIndex >= 0) {
|
|
|
|
// replace relative path
|
|
|
|
initmethodURI.setPath(currentPath.substring(0, folderIndex + 1) + newPath);
|
|
|
|
} else {
|
|
|
|
// should not happen
|
|
|
|
initmethodURI.setPath('/' + newPath);
|
|
|
|
}
|
|
|
|
}
|
2009-09-17 10:42:45 -04:00
|
|
|
}
|
|
|
|
initmethodURI.setQuery(pathQuery.substring(queryIndex + 1));
|
|
|
|
}
|
|
|
|
return initmethodURI.getURI();
|
|
|
|
}
|
|
|
|
|
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
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws 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);
|
2010-02-01 18:20:56 -05:00
|
|
|
TagNode logonForm = null;
|
|
|
|
// select form
|
2008-12-01 07:38:49 -05:00
|
|
|
if (forms.size() == 1) {
|
2010-02-01 18:20:56 -05:00
|
|
|
logonForm = (TagNode) forms.get(0);
|
|
|
|
} else if (forms.size() > 1) {
|
|
|
|
for (Object form : forms) {
|
|
|
|
if ("logonForm".equals(((TagNode) form).getAttributeByName("name"))) {
|
|
|
|
logonForm = ((TagNode) form);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (logonForm != null) {
|
|
|
|
String logonMethodPath = logonForm.getAttributeByName("action");
|
2008-12-01 07:38:49 -05:00
|
|
|
|
2009-09-02 12:05:03 -04:00
|
|
|
logonMethod = new PostMethod(getAbsoluteUri(initmethod, logonMethodPath));
|
2008-12-01 07:38:49 -05:00
|
|
|
|
2010-02-01 18:20:56 -05:00
|
|
|
List inputList = logonForm.getElementListByName("input", true);
|
2008-12-17 10:27:56 -05:00
|
|
|
for (Object input : inputList) {
|
|
|
|
String type = ((TagNode) input).getAttributeByName("type");
|
|
|
|
String name = ((TagNode) input).getAttributeByName("name");
|
|
|
|
String value = ((TagNode) input).getAttributeByName("value");
|
2009-11-02 18:15:12 -05:00
|
|
|
if ("hidden".equalsIgnoreCase(type) && name != null && value != null) {
|
2008-12-01 07:38:49 -05:00
|
|
|
logonMethod.addParameter(name, value);
|
|
|
|
}
|
2009-09-08 04:05:05 -04:00
|
|
|
// custom login form
|
2009-11-23 04:53:04 -05:00
|
|
|
if (USER_NAME_FIELDS.contains(name)) {
|
2009-11-01 17:27:23 -05:00
|
|
|
userNameInput = name;
|
2009-11-23 04:53:04 -05:00
|
|
|
} else if (PASSWORD_FIELDS.contains(name)) {
|
2009-11-01 17:27:23 -05:00
|
|
|
passwordInput = name;
|
2009-10-25 18:24:53 -04:00
|
|
|
} else if ("addr".equals(name)) {
|
|
|
|
// this is not a logon form but a redirect form
|
|
|
|
HttpMethod newInitMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
|
|
|
|
logonMethod = buildLogonMethod(httpClient, newInitMethod);
|
2009-11-23 04:53:04 -05:00
|
|
|
} else if (TOKEN_FIELDS.contains(name)) {
|
|
|
|
// one time password, ask user
|
|
|
|
logonMethod.addParameter(name, DavGatewayOTPPrompt.getOneTimePassword());
|
2009-09-08 04:05:05 -04:00
|
|
|
}
|
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();
|
2009-11-03 05:01:33 -05:00
|
|
|
String sUrl = StringUtil.getToken(scriptValue, "var a_sUrl = \"", "\"");
|
|
|
|
String sLgn = StringUtil.getToken(scriptValue, "var a_sLgn = \"", "\"");
|
|
|
|
if (sLgn == null) {
|
|
|
|
sLgn = StringUtil.getToken(scriptValue, "var a_sLgnQS = \"", "\"");
|
|
|
|
}
|
|
|
|
if (sUrl != null && sLgn != null) {
|
|
|
|
String src = getScriptBasedFormURL(initmethod, sLgn + sUrl);
|
|
|
|
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
|
|
|
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);
|
2009-11-01 16:58:04 -05:00
|
|
|
if (logonMethod == null) {
|
|
|
|
throw new DavMailException("EXCEPTION_AUTHENTICATION_FORM_NOT_FOUND", initmethod.getURI());
|
|
|
|
}
|
|
|
|
|
2009-11-01 17:27:23 -05:00
|
|
|
// make sure username and password fields are empty
|
|
|
|
((PostMethod) logonMethod).removeParameter(userNameInput);
|
|
|
|
((PostMethod) logonMethod).removeParameter(passwordInput);
|
2009-12-07 16:41:45 -05:00
|
|
|
((PostMethod) logonMethod).removeParameter("trusted");
|
|
|
|
((PostMethod) logonMethod).removeParameter("flags");
|
2009-09-08 04:05:05 -04:00
|
|
|
((PostMethod) logonMethod).addParameter(userNameInput, userName);
|
|
|
|
((PostMethod) logonMethod).addParameter(passwordInput, password);
|
2009-10-28 19:13:56 -04:00
|
|
|
((PostMethod) logonMethod).addParameter("trusted", "4");
|
2009-12-07 16:41:45 -05:00
|
|
|
((PostMethod) logonMethod).addParameter("flags", "4");
|
2010-07-27 06:26:19 -04:00
|
|
|
|
|
|
|
// add exchange 2010 cookie
|
|
|
|
logonMethod.addRequestHeader("Cookie", "PBack=0");
|
2008-12-01 07:38:49 -05:00
|
|
|
logonMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
|
2009-04-25 06:31:33 -04:00
|
|
|
|
|
|
|
// test form based authentication
|
|
|
|
checkFormLoginQueryString(logonMethod);
|
2009-04-28 17:01:40 -04:00
|
|
|
|
2009-04-25 06:31:33 -04:00
|
|
|
// workaround for post logon script redirect
|
2009-11-01 16:58:04 -05:00
|
|
|
if (!isAuthenticated()) {
|
|
|
|
// try to get new method from script based redirection
|
2009-04-25 06:31:33 -04:00
|
|
|
logonMethod = buildLogonMethod(httpClient, logonMethod);
|
2009-11-01 16:58:04 -05:00
|
|
|
|
|
|
|
if (logonMethod != null) {
|
|
|
|
// if logonMethod is not null, try to follow redirection
|
|
|
|
logonMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
|
|
|
|
checkFormLoginQueryString(logonMethod);
|
2009-11-23 04:53:04 -05:00
|
|
|
// also check cookies
|
|
|
|
if (!isAuthenticated()) {
|
|
|
|
throwAuthenticationFailed();
|
|
|
|
}
|
2009-11-01 16:58:04 -05:00
|
|
|
} else {
|
|
|
|
// authentication failed
|
|
|
|
throwAuthenticationFailed();
|
|
|
|
}
|
2009-04-25 06:31:33 -04:00
|
|
|
}
|
|
|
|
|
2008-12-01 07:38:49 -05:00
|
|
|
return logonMethod;
|
2008-11-03 20:47:10 -05:00
|
|
|
}
|
|
|
|
|
2009-11-01 16:58:04 -05:00
|
|
|
/**
|
|
|
|
* Look for session cookies.
|
|
|
|
*
|
|
|
|
* @return true if session cookies are available
|
|
|
|
*/
|
2009-11-02 04:36:51 -05:00
|
|
|
protected boolean isAuthenticated() {
|
2009-11-01 16:58:04 -05:00
|
|
|
boolean authenticated = false;
|
|
|
|
for (Cookie cookie : httpClient.getState().getCookies()) {
|
2009-11-18 06:13:06 -05:00
|
|
|
// Exchange 2003 cookies
|
|
|
|
if (cookie.getName().startsWith("cadata") || "sessionid".equals(cookie.getName())
|
|
|
|
// Exchange 2007 cookie
|
|
|
|
|| "UserContext".equals(cookie.getName())) {
|
2009-11-01 16:58:04 -05:00
|
|
|
authenticated = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return authenticated;
|
|
|
|
}
|
|
|
|
|
2009-04-27 19:03:58 -04:00
|
|
|
protected void checkFormLoginQueryString(HttpMethod logonMethod) throws DavMailAuthenticationException {
|
2009-04-28 17:01:40 -04:00
|
|
|
String queryString = logonMethod.getQueryString();
|
2010-08-17 02:56:38 -04:00
|
|
|
if (queryString != null && (queryString.contains("reason=2") || queryString.contains("reason=4"))) {
|
2009-04-25 06:31:33 -04:00
|
|
|
logonMethod.releaseConnection();
|
2009-11-01 16:58:04 -05:00
|
|
|
throwAuthenticationFailed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void throwAuthenticationFailed() throws DavMailAuthenticationException {
|
|
|
|
if (this.userName != null && this.userName.contains("\\")) {
|
|
|
|
throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED");
|
|
|
|
} else {
|
|
|
|
throw new DavMailAuthenticationException("EXCEPTION_AUTHENTICATION_FAILED_RETRY");
|
2009-04-25 06:31:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-02 04:46:46 -04:00
|
|
|
protected abstract void buildSessionInfo(HttpMethod method) throws DavMailException;
|
2008-12-22 19:14:41 -05:00
|
|
|
|
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-07-28 02:53:46 -04:00
|
|
|
* @param folderPath Exchange folder path
|
2009-02-09 05:12:09 -05:00
|
|
|
* @param messageName message name
|
|
|
|
* @param properties message properties (flags)
|
|
|
|
* @param messageBody mail body
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException when unable to create message
|
2006-12-12 18:57:24 -05:00
|
|
|
*/
|
2010-07-12 12:35:46 -04:00
|
|
|
public abstract void createMessage(String folderPath, String messageName, HashMap<String, String> properties, byte[] messageBody) throws IOException;
|
2009-02-03 18:54:48 -05:00
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Update given properties on message.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
|
|
|
* @param message Exchange message
|
2009-07-28 02:39:27 -04:00
|
|
|
* @param properties Webdav properties map
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-09 05:13:13 -04:00
|
|
|
public abstract void updateMessage(Message message, Map<String, String> properties) throws IOException;
|
2009-02-02 02:19:57 -05:00
|
|
|
|
2010-06-09 05:52:12 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete Exchange message.
|
|
|
|
*
|
|
|
|
* @param message Exchange message
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public abstract void deleteMessage(Message message) throws IOException;
|
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
/**
|
|
|
|
* Send message to recipients, properties contains bcc recipients and other non MIME flags.
|
2010-06-09 05:52:12 -04:00
|
|
|
*
|
2010-06-09 05:13:13 -04:00
|
|
|
* @param messageBody MIME message body
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-12 12:35:46 -04:00
|
|
|
public abstract void sendMessage(byte[] messageBody) throws IOException;
|
2009-02-02 02:19:57 -05:00
|
|
|
|
2010-07-05 06:23:51 -04:00
|
|
|
/**
|
|
|
|
* Get raw MIME message content
|
|
|
|
*
|
|
|
|
* @param message Exchange message
|
|
|
|
* @return message body
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
protected abstract byte[] getContent(Message message) throws IOException;
|
|
|
|
|
2010-07-01 08:47:57 -04:00
|
|
|
protected static final Set<String> POP_MESSAGE_ATTRIBUTES = new HashSet<String>();
|
2010-06-08 18:21:53 -04:00
|
|
|
|
|
|
|
static {
|
|
|
|
POP_MESSAGE_ATTRIBUTES.add("uid");
|
2010-07-19 05:05:44 -04:00
|
|
|
POP_MESSAGE_ATTRIBUTES.add("imapUid");
|
2010-06-08 18:21:53 -04:00
|
|
|
POP_MESSAGE_ATTRIBUTES.add("messageSize");
|
|
|
|
}
|
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Return folder message list with id and size only (for POP3 listener).
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-28 02:39:27 -04:00
|
|
|
* @param folderName Exchange folder name
|
|
|
|
* @return folder message list
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-05-10 10:52:09 -04:00
|
|
|
public MessageList getAllMessageUidAndSize(String folderName) throws IOException {
|
2010-06-08 18:21:53 -04:00
|
|
|
return searchMessages(folderName, POP_MESSAGE_ATTRIBUTES, null);
|
|
|
|
}
|
|
|
|
|
2010-07-01 08:47:57 -04:00
|
|
|
protected static final Set<String> IMAP_MESSAGE_ATTRIBUTES = new HashSet<String>();
|
2010-06-08 18:21:53 -04:00
|
|
|
|
|
|
|
static {
|
2010-07-05 12:28:10 -04:00
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("permanenturl");
|
2010-07-25 10:36:27 -04:00
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("urlcompname");
|
2010-06-08 18:21:53 -04:00
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("uid");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("messageSize");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("imapUid");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("junk");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("flagStatus");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("messageFlags");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("lastVerbExecuted");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("read");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("deleted");
|
|
|
|
IMAP_MESSAGE_ATTRIBUTES.add("date");
|
|
|
|
}
|
|
|
|
|
2010-07-01 08:47:57 -04:00
|
|
|
protected static final Set<String> UID_MESSAGE_ATTRIBUTES = new HashSet<String>();
|
2010-06-09 05:13:13 -04:00
|
|
|
|
|
|
|
static {
|
|
|
|
UID_MESSAGE_ATTRIBUTES.add("uid");
|
|
|
|
}
|
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
/**
|
|
|
|
* Get all folder messages.
|
|
|
|
*
|
2010-06-21 16:37:20 -04:00
|
|
|
* @param folderPath Exchange folder name
|
2010-06-08 18:21:53 -04:00
|
|
|
* @return message list
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-21 16:37:20 -04:00
|
|
|
public MessageList searchMessages(String folderPath) throws IOException {
|
|
|
|
return searchMessages(folderPath, IMAP_MESSAGE_ATTRIBUTES, null);
|
2009-02-17 17:59:32 -05:00
|
|
|
}
|
2009-02-19 17:00:36 -05:00
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Search folder for messages matching conditions, with attributes needed by IMAP listener.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-28 02:39:27 -04:00
|
|
|
* @param folderName Exchange folder name
|
2010-06-08 18:21:53 -04:00
|
|
|
* @param condition search filter
|
2009-07-28 02:39:27 -04:00
|
|
|
* @return message list
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public MessageList searchMessages(String folderName, Condition condition) throws IOException {
|
|
|
|
return searchMessages(folderName, IMAP_MESSAGE_ATTRIBUTES, condition);
|
2009-10-25 18:24:53 -04:00
|
|
|
}
|
2009-05-10 10:52:09 -04:00
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Search folder for messages matching conditions, with given attributes.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-28 02:39:27 -04:00
|
|
|
* @param folderName Exchange folder name
|
|
|
|
* @param attributes requested Webdav attributes
|
2010-06-21 16:37:20 -04:00
|
|
|
* @param condition search filter
|
2009-07-28 02:39:27 -04:00
|
|
|
* @return message list
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-01 08:47:57 -04:00
|
|
|
public abstract MessageList searchMessages(String folderName, Set<String> attributes, Condition condition) throws IOException;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
@SuppressWarnings({"JavaDoc"})
|
|
|
|
public enum Operator {
|
2010-07-22 19:10:24 -04:00
|
|
|
Or, And, Not, IsEqualTo,
|
|
|
|
IsGreaterThan, IsGreaterThanOrEqualTo,
|
2010-07-25 15:55:51 -04:00
|
|
|
IsLessThan, IsLessThanOrEqualTo,
|
2010-07-22 19:10:24 -04:00
|
|
|
IsNull, IsTrue, IsFalse,
|
2010-06-17 18:37:33 -04:00
|
|
|
Like, StartsWith, Contains
|
2010-06-07 17:17:07 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Exchange search filter.
|
|
|
|
*/
|
2010-06-30 17:38:01 -04:00
|
|
|
public static interface Condition {
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Append condition to buffer.
|
|
|
|
*
|
|
|
|
* @param buffer search filter buffer
|
|
|
|
*/
|
2010-07-01 10:23:31 -04:00
|
|
|
public void appendTo(StringBuilder buffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if condition is empty.
|
|
|
|
*
|
|
|
|
* @return true if condition is empty
|
|
|
|
*/
|
|
|
|
public boolean isEmpty();
|
2010-08-07 17:32:37 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if the contact matches current condition.
|
2010-08-09 16:15:28 -04:00
|
|
|
*
|
2010-08-07 17:32:37 -04:00
|
|
|
* @param contact Exchange Contact
|
|
|
|
* @return true if contact matches condition
|
|
|
|
*/
|
|
|
|
public abstract boolean isMatch(ExchangeSession.Contact contact);
|
2010-06-07 17:17:07 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Attribute condition.
|
|
|
|
*/
|
2010-06-30 17:38:01 -04:00
|
|
|
public abstract static class AttributeCondition implements Condition {
|
2010-07-19 05:22:42 -04:00
|
|
|
protected final String attributeName;
|
|
|
|
protected final Operator operator;
|
|
|
|
protected final String value;
|
2010-06-07 17:17:07 -04:00
|
|
|
|
|
|
|
protected AttributeCondition(String attributeName, Operator operator, String value) {
|
|
|
|
this.attributeName = attributeName;
|
|
|
|
this.operator = operator;
|
|
|
|
this.value = value;
|
|
|
|
}
|
2010-07-01 10:23:31 -04:00
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
return false;
|
|
|
|
}
|
2010-08-09 16:15:28 -04:00
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
/**
|
|
|
|
* Get attribute name.
|
|
|
|
*
|
|
|
|
* @return attribute name
|
|
|
|
*/
|
2010-08-09 16:15:28 -04:00
|
|
|
public String getAttributeName() {
|
|
|
|
return attributeName;
|
|
|
|
}
|
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
/**
|
|
|
|
* Condition value.
|
|
|
|
*
|
|
|
|
* @return value
|
|
|
|
*/
|
2010-08-09 16:15:28 -04:00
|
|
|
public String getValue() {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2010-06-07 17:17:07 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Multiple condition.
|
|
|
|
*/
|
2010-06-30 17:38:01 -04:00
|
|
|
public abstract static class MultiCondition implements Condition {
|
2010-07-19 05:22:42 -04:00
|
|
|
protected final Operator operator;
|
|
|
|
protected final List<Condition> conditions;
|
2010-06-07 17:17:07 -04:00
|
|
|
|
2010-06-07 18:30:23 -04:00
|
|
|
protected MultiCondition(Operator operator, Condition... conditions) {
|
2010-06-07 17:17:07 -04:00
|
|
|
this.operator = operator;
|
2010-06-30 17:59:56 -04:00
|
|
|
this.conditions = new ArrayList<Condition>();
|
2010-07-13 03:53:39 -04:00
|
|
|
for (Condition condition : conditions) {
|
2010-07-09 19:53:28 -04:00
|
|
|
if (condition != null) {
|
|
|
|
this.conditions.add(condition);
|
|
|
|
}
|
|
|
|
}
|
2010-06-08 18:21:53 -04:00
|
|
|
}
|
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
/**
|
|
|
|
* Conditions list.
|
|
|
|
*
|
|
|
|
* @return conditions
|
|
|
|
*/
|
2010-08-16 08:24:16 -04:00
|
|
|
public List<Condition> getConditions() {
|
|
|
|
return conditions;
|
|
|
|
}
|
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
/**
|
|
|
|
* Condition operator.
|
|
|
|
*
|
|
|
|
* @return operator
|
|
|
|
*/
|
2010-08-16 08:24:16 -04:00
|
|
|
public Operator getOperator() {
|
|
|
|
return operator;
|
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Add a new condition.
|
|
|
|
*
|
|
|
|
* @param condition single condition
|
|
|
|
*/
|
2010-06-17 18:37:33 -04:00
|
|
|
public void add(Condition condition) {
|
2010-06-08 18:21:53 -04:00
|
|
|
if (condition != null) {
|
|
|
|
conditions.add(condition);
|
|
|
|
}
|
2010-06-07 18:30:23 -04:00
|
|
|
}
|
2010-07-01 10:23:31 -04:00
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
boolean isEmpty = true;
|
2010-07-05 06:23:51 -04:00
|
|
|
for (Condition condition : conditions) {
|
2010-07-01 10:23:31 -04:00
|
|
|
if (!condition.isEmpty()) {
|
|
|
|
isEmpty = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return isEmpty;
|
|
|
|
}
|
2010-08-07 17:32:37 -04:00
|
|
|
|
|
|
|
public boolean isMatch(ExchangeSession.Contact contact) {
|
|
|
|
if (operator == Operator.And) {
|
|
|
|
for (Condition condition : conditions) {
|
|
|
|
if (!condition.isMatch(contact)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (operator == Operator.Or) {
|
|
|
|
for (Condition condition : conditions) {
|
|
|
|
if (condition.isMatch(contact)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2010-08-09 16:15:28 -04:00
|
|
|
|
2010-06-07 18:30:23 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Not condition.
|
|
|
|
*/
|
2010-06-30 17:38:01 -04:00
|
|
|
public abstract static class NotCondition implements Condition {
|
2010-07-19 05:22:42 -04:00
|
|
|
protected final Condition condition;
|
2010-06-07 18:30:23 -04:00
|
|
|
|
|
|
|
protected NotCondition(Condition condition) {
|
|
|
|
this.condition = condition;
|
2010-06-07 17:17:07 -04:00
|
|
|
}
|
2010-07-01 10:23:31 -04:00
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
return condition.isEmpty();
|
|
|
|
}
|
|
|
|
|
2010-08-07 17:32:37 -04:00
|
|
|
public boolean isMatch(ExchangeSession.Contact contact) {
|
|
|
|
return !condition.isMatch(contact);
|
|
|
|
}
|
2010-06-07 17:17:07 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Single search filter condition.
|
|
|
|
*/
|
2010-06-30 17:38:01 -04:00
|
|
|
public abstract static class MonoCondition implements Condition {
|
2010-07-19 05:22:42 -04:00
|
|
|
protected final String attributeName;
|
|
|
|
protected final Operator operator;
|
2010-06-08 06:44:47 -04:00
|
|
|
|
2010-06-08 18:21:53 -04:00
|
|
|
protected MonoCondition(String attributeName, Operator operator) {
|
2010-06-08 06:44:47 -04:00
|
|
|
this.attributeName = attributeName;
|
2010-06-08 18:21:53 -04:00
|
|
|
this.operator = operator;
|
2010-06-08 06:44:47 -04:00
|
|
|
}
|
2010-07-01 10:23:31 -04:00
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
return false;
|
|
|
|
}
|
2010-08-07 17:32:37 -04:00
|
|
|
|
|
|
|
public boolean isMatch(ExchangeSession.Contact contact) {
|
|
|
|
String actualValue = contact.get(attributeName);
|
2010-08-09 16:15:28 -04:00
|
|
|
return (operator == Operator.IsNull && actualValue == null) ||
|
|
|
|
(operator == Operator.IsFalse && "false".equals(actualValue)) ||
|
|
|
|
(operator == Operator.IsTrue && "true".equals(actualValue));
|
2010-08-07 17:32:37 -04:00
|
|
|
}
|
2010-06-08 06:44:47 -04:00
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* And search filter.
|
|
|
|
*
|
|
|
|
* @param condition search conditions
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract MultiCondition and(Condition... condition);
|
2010-06-08 06:44:47 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Or search filter.
|
|
|
|
*
|
|
|
|
* @param condition search conditions
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract MultiCondition or(Condition... condition);
|
2010-06-07 17:17:07 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Not search filter.
|
|
|
|
*
|
|
|
|
* @param condition search condition
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 06:44:47 -04:00
|
|
|
public abstract Condition not(Condition condition);
|
2010-06-07 17:17:07 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Equals condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-07-27 09:19:28 -04:00
|
|
|
public abstract Condition isEqualTo(String attributeName, String value);
|
2010-06-07 18:30:23 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Equals condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-07-27 09:19:28 -04:00
|
|
|
public abstract Condition isEqualTo(String attributeName, int value);
|
2010-06-16 08:06:59 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* MIME header equals condition.
|
|
|
|
*
|
|
|
|
* @param headerName MIME header name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-07-27 09:19:28 -04:00
|
|
|
public abstract Condition headerIsEqualTo(String headerName, String value);
|
2010-06-08 18:21:53 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Greater than or equals condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract Condition gte(String attributeName, String value);
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Greater than condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract Condition gt(String attributeName, String value);
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Lower than condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract Condition lt(String attributeName, String value);
|
|
|
|
|
2010-07-22 19:10:24 -04:00
|
|
|
/**
|
|
|
|
* Lower than or equals condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
|
|
|
public abstract Condition lte(String attributeName, String value);
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Contains condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
|
|
|
public abstract Condition contains(String attributeName, String value);
|
2010-06-08 18:21:53 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Starts with condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @param value attribute value
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-17 18:37:33 -04:00
|
|
|
public abstract Condition startsWith(String attributeName, String value);
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Is null condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 06:44:47 -04:00
|
|
|
public abstract Condition isNull(String attributeName);
|
2010-06-07 17:17:07 -04:00
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Is true condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract Condition isTrue(String attributeName);
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Is false condition.
|
|
|
|
*
|
|
|
|
* @param attributeName logical Exchange attribute name
|
|
|
|
* @return condition
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public abstract Condition isFalse(String attributeName);
|
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
2010-06-07 18:30:23 -04:00
|
|
|
* Search mail and generic folders under given folder.
|
|
|
|
* Exclude calendar and contacts folders
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-28 02:39:27 -04:00
|
|
|
* @param folderName Exchange folder name
|
2009-08-04 16:48:35 -04:00
|
|
|
* @param recursive deep search if true
|
2009-07-28 02:39:27 -04:00
|
|
|
* @return list of folders
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-01-23 08:01:46 -05:00
|
|
|
public List<Folder> getSubFolders(String folderName, boolean recursive) throws IOException {
|
2010-07-27 09:19:28 -04:00
|
|
|
return getSubFolders(folderName, or(isEqualTo("folderclass", "IPF.Note"), isNull("folderclass")),
|
2010-06-08 06:44:47 -04:00
|
|
|
recursive);
|
2009-09-09 17:50:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search calendar folders under given folder.
|
|
|
|
*
|
|
|
|
* @param folderName Exchange folder name
|
|
|
|
* @param recursive deep search if true
|
|
|
|
* @return list of folders
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public List<Folder> getSubCalendarFolders(String folderName, boolean recursive) throws IOException {
|
2010-07-27 09:19:28 -04:00
|
|
|
return getSubFolders(folderName, isEqualTo("folderclass", "IPF.Appointment"), recursive);
|
2009-09-09 17:50:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search folders under given folder matching filter.
|
|
|
|
*
|
|
|
|
* @param folderName Exchange folder name
|
2010-06-07 17:17:07 -04:00
|
|
|
* @param condition search filter
|
2009-09-09 17:50:11 -04:00
|
|
|
* @param recursive deep search if true
|
|
|
|
* @return list of folders
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-07 17:17:07 -04:00
|
|
|
public abstract List<Folder> getSubFolders(String folderName, Condition condition, boolean recursive) throws IOException;
|
2009-01-22 19:59:41 -05:00
|
|
|
|
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) {
|
2010-06-09 05:13:13 -04:00
|
|
|
purgeOldestFolderMessages(TRASH, keepDelay);
|
2008-12-05 05:01:24 -05:00
|
|
|
}
|
|
|
|
// this is a new feature, default is : do nothing
|
|
|
|
int sentKeepDelay = Settings.getIntProperty("davmail.sentKeepDelay");
|
|
|
|
if (sentKeepDelay != 0) {
|
2010-06-09 05:13:13 -04:00
|
|
|
purgeOldestFolderMessages(SENT, sentKeepDelay);
|
2007-02-07 06:51:08 -05:00
|
|
|
}
|
2008-12-05 05:01:24 -05:00
|
|
|
}
|
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
protected void purgeOldestFolderMessages(String folderPath, int keepDelay) throws IOException {
|
2007-02-07 06:32:44 -05:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -keepDelay);
|
2010-06-09 05:13:13 -04:00
|
|
|
LOGGER.debug("Delete messages in " + folderPath + " not modified since " + cal.getTime());
|
2008-12-05 04:29:44 -05:00
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
MessageList messages = searchMessages(folderPath, UID_MESSAGE_ATTRIBUTES,
|
|
|
|
lt("lastmodified", formatSearchDate(cal.getTime())));
|
2008-12-05 04:29:44 -05:00
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
for (Message message : messages) {
|
|
|
|
message.delete();
|
2007-02-07 06:32:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-28 02:39:27 -04:00
|
|
|
/**
|
|
|
|
* Send message in reader to recipients.
|
|
|
|
* Detect visible recipients in message body to determine bcc recipients
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2010-07-18 15:59:13 -04:00
|
|
|
* @param rcptToRecipients recipients list
|
2010-07-20 10:09:40 -04:00
|
|
|
* @param mimeMessage mime message
|
|
|
|
* @throws IOException on error
|
2010-07-18 15:59:13 -04:00
|
|
|
* @throws MessagingException on error
|
2009-07-28 02:39:27 -04:00
|
|
|
*/
|
2010-07-12 12:35:46 -04:00
|
|
|
public void sendMessage(List<String> rcptToRecipients, MimeMessage mimeMessage) throws IOException, MessagingException {
|
2010-07-20 10:09:40 -04:00
|
|
|
// check Sent folder for duplicates
|
2010-07-27 09:19:28 -04:00
|
|
|
ExchangeSession.MessageList messages = searchMessages(SENT, headerIsEqualTo("message-id", mimeMessage.getMessageID()));
|
2010-07-20 10:09:40 -04:00
|
|
|
if (!messages.isEmpty()) {
|
2010-07-20 11:23:12 -04:00
|
|
|
LOGGER.debug("Dropping message id " + mimeMessage.getMessageID() + ": already sent");
|
2010-07-20 10:09:40 -04:00
|
|
|
} else {
|
|
|
|
// remove visible recipients from list
|
|
|
|
Set<String> visibleRecipients = new HashSet<String>();
|
|
|
|
Address[] recipients = mimeMessage.getAllRecipients();
|
|
|
|
for (Address address : recipients) {
|
2010-08-09 16:15:28 -04:00
|
|
|
visibleRecipients.add(((InternetAddress) address).getAddress().toLowerCase());
|
2008-12-09 05:10:38 -05:00
|
|
|
}
|
2010-07-20 10:09:40 -04:00
|
|
|
for (String recipient : rcptToRecipients) {
|
2010-08-06 19:50:55 -04:00
|
|
|
if (!visibleRecipients.contains(recipient.toLowerCase())) {
|
2010-07-20 10:09:40 -04:00
|
|
|
mimeMessage.addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(recipient));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
mimeMessage.writeTo(baos);
|
2009-02-03 18:54:48 -05:00
|
|
|
|
2010-07-20 10:09:40 -04:00
|
|
|
sendMessage(baos.toByteArray());
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2007-11-08 12:12:36 -05:00
|
|
|
/**
|
2009-07-28 02:53:46 -04:00
|
|
|
* Get folder object.
|
|
|
|
* Folder name can be logical names INBOX, Drafts, Trash or calendar,
|
|
|
|
* or a path relative 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
|
2009-07-28 02:53:46 -04:00
|
|
|
* @throws IOException on error
|
2007-11-08 12:12:36 -05:00
|
|
|
*/
|
2010-06-07 05:07:31 -04:00
|
|
|
public abstract Folder getFolder(String folderName) throws IOException;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2009-03-27 06:31:52 -04:00
|
|
|
/**
|
2009-07-28 02:53:46 -04:00
|
|
|
* Check folder ctag and reload messages as needed.
|
2009-03-27 06:31:52 -04:00
|
|
|
*
|
|
|
|
* @param currentFolder current folder
|
2009-12-01 05:14:59 -05:00
|
|
|
* @return true if folder changed
|
2009-03-27 06:31:52 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-12-01 05:14:59 -05:00
|
|
|
public boolean refreshFolder(Folder currentFolder) throws IOException {
|
2010-06-10 16:47:55 -04:00
|
|
|
Folder newFolder = getFolder(currentFolder.folderPath);
|
2010-04-01 05:12:44 -04:00
|
|
|
if (currentFolder.ctag == null || !currentFolder.ctag.equals(newFolder.ctag)) {
|
2009-03-27 06:31:52 -04:00
|
|
|
if (LOGGER.isDebugEnabled()) {
|
2010-06-10 16:47:55 -04:00
|
|
|
LOGGER.debug("Contenttag changed on " + currentFolder.folderPath + ' '
|
2010-04-01 05:12:44 -04:00
|
|
|
+ currentFolder.ctag + " => " + newFolder.ctag + ", reloading messages");
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
2009-12-01 05:14:59 -05:00
|
|
|
currentFolder.hasChildren = newFolder.hasChildren;
|
|
|
|
currentFolder.noInferiors = newFolder.noInferiors;
|
|
|
|
currentFolder.unreadCount = newFolder.unreadCount;
|
2010-04-01 05:12:44 -04:00
|
|
|
currentFolder.ctag = newFolder.ctag;
|
2010-06-10 16:47:55 -04:00
|
|
|
currentFolder.etag = newFolder.etag;
|
2009-12-01 05:14:59 -05:00
|
|
|
currentFolder.loadMessages();
|
|
|
|
return true;
|
2009-03-27 06:31:52 -04:00
|
|
|
} else {
|
2009-12-01 05:14:59 -05:00
|
|
|
return false;
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
2009-10-27 19:32:00 -04:00
|
|
|
* Create Exchange message folder.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param folderName logical folder name
|
2010-07-24 18:23:14 -04:00
|
|
|
* @return status
|
2010-07-28 13:11:18 -04:00
|
|
|
* @throws IOException on error
|
2009-07-31 02:21:46 -04:00
|
|
|
*/
|
2010-07-24 18:23:14 -04:00
|
|
|
public int createMessageFolder(String folderName) throws IOException {
|
|
|
|
return createFolder(folderName, "IPF.Note", null);
|
2009-10-27 19:32:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create Exchange calendar folder.
|
|
|
|
*
|
|
|
|
* @param folderName logical folder name
|
2010-07-26 11:08:06 -04:00
|
|
|
* @param properties folder properties
|
2010-07-24 18:23:14 -04:00
|
|
|
* @return status
|
2010-07-28 13:11:18 -04:00
|
|
|
* @throws IOException on error
|
2009-10-27 19:32:00 -04:00
|
|
|
*/
|
2010-07-24 18:23:14 -04:00
|
|
|
public int createCalendarFolder(String folderName, Map<String, String> properties) throws IOException {
|
|
|
|
return createFolder(folderName, "IPF.Appointment", properties);
|
2009-10-27 19:32:00 -04:00
|
|
|
}
|
|
|
|
|
2010-06-29 04:45:06 -04:00
|
|
|
/**
|
|
|
|
* Create Exchange contact folder.
|
|
|
|
*
|
|
|
|
* @param folderName logical folder name
|
2010-07-26 11:08:06 -04:00
|
|
|
* @param properties folder properties
|
2010-07-24 18:23:14 -04:00
|
|
|
* @return status
|
2010-07-28 13:11:18 -04:00
|
|
|
* @throws IOException on error
|
2010-06-29 04:45:06 -04:00
|
|
|
*/
|
2010-07-24 18:23:14 -04:00
|
|
|
public int createContactFolder(String folderName, Map<String, String> properties) throws IOException {
|
|
|
|
return createFolder(folderName, "IPF.Contact", properties);
|
2010-06-29 04:45:06 -04:00
|
|
|
}
|
|
|
|
|
2009-10-27 19:32:00 -04:00
|
|
|
/**
|
|
|
|
* Create Exchange folder with given folder class.
|
|
|
|
*
|
2009-10-29 13:19:00 -04:00
|
|
|
* @param folderName logical folder name
|
|
|
|
* @param folderClass folder class
|
2010-07-28 13:11:18 -04:00
|
|
|
* @param properties folder properties
|
2010-07-24 18:23:14 -04:00
|
|
|
* @return status
|
2009-10-27 19:32:00 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-24 18:23:14 -04:00
|
|
|
public abstract int createFolder(String folderName, String folderClass, Map<String, String> properties) throws IOException;
|
2009-01-26 18:51:08 -05:00
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Delete Exchange folder.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderName logical folder name
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-09 05:13:13 -04:00
|
|
|
public abstract void deleteFolder(String folderName) throws IOException;
|
2009-08-13 04:34:51 -04:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Copy message to target folder
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
|
|
|
* @param message Exchange message
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param targetFolder target folder
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-09 05:13:13 -04:00
|
|
|
public abstract void copyMessage(Message message, String targetFolder) throws IOException;
|
2009-02-02 18:29:44 -05:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Move folder to target name.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param folderName current folder name/path
|
|
|
|
* @param targetName target folder name/path
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-09 05:13:13 -04:00
|
|
|
public abstract void moveFolder(String folderName, String targetName) throws IOException;
|
2009-03-05 05:26:20 -05:00
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
protected abstract void moveToTrash(Message message) throws IOException;
|
2009-03-05 05:26:20 -05:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Exchange folder with IMAP properties
|
|
|
|
*/
|
2009-03-27 06:31:52 -04:00
|
|
|
public class Folder {
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Logical (IMAP) folder path.
|
|
|
|
*/
|
|
|
|
public String folderPath;
|
2010-04-01 05:12:44 -04:00
|
|
|
|
2010-06-16 09:30:36 -04:00
|
|
|
/**
|
|
|
|
* Display Name.
|
|
|
|
*/
|
|
|
|
public String displayName;
|
2010-04-01 05:12:44 -04:00
|
|
|
/**
|
2010-06-07 17:17:07 -04:00
|
|
|
* Folder class (PR_CONTAINER_CLASS).
|
2010-04-01 05:12:44 -04:00
|
|
|
*/
|
2010-06-07 17:17:07 -04:00
|
|
|
public String folderClass;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Folder unread message count.
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
public int unreadCount;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* true if folder has subfolders (DAV:hassubs).
|
|
|
|
*/
|
2009-01-22 19:59:41 -05:00
|
|
|
public boolean hasChildren;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* true if folder has no subfolders (DAV:nosubs).
|
|
|
|
*/
|
2009-01-22 19:59:41 -05:00
|
|
|
public boolean noInferiors;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Folder content tag (to detect folder content changes).
|
|
|
|
*/
|
2010-04-01 05:12:44 -04:00
|
|
|
public String ctag;
|
|
|
|
/**
|
|
|
|
* Folder etag (to detect folder object changes).
|
|
|
|
*/
|
|
|
|
public String etag;
|
2010-07-23 04:58:39 -04:00
|
|
|
/**
|
|
|
|
* Next IMAP uid
|
|
|
|
*/
|
|
|
|
public int uidNext;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Folder message list, empty before loadMessages call.
|
|
|
|
*/
|
2009-03-27 06:31:52 -04:00
|
|
|
public ExchangeSession.MessageList messages;
|
2010-01-05 05:06:47 -05:00
|
|
|
/**
|
2010-07-01 12:19:24 -04:00
|
|
|
* Permanent uid (PR_SEARCH_KEY) to IMAP UID map.
|
2010-01-05 05:06:47 -05:00
|
|
|
*/
|
2010-07-05 12:28:10 -04:00
|
|
|
private final HashMap<String, Long> permanentUrlToImapUidMap = new HashMap<String, Long>();
|
2010-01-21 05:49:23 -05:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Get IMAP folder flags.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return folder flags in IMAP format
|
|
|
|
*/
|
2009-01-22 19:59:41 -05:00
|
|
|
public String getFlags() {
|
|
|
|
if (noInferiors) {
|
|
|
|
return "\\NoInferiors";
|
|
|
|
} else if (hasChildren) {
|
|
|
|
return "\\HasChildren";
|
|
|
|
} else {
|
|
|
|
return "\\HasNoChildren";
|
|
|
|
}
|
|
|
|
}
|
2009-03-27 06:31:52 -04:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Load folder messages.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-27 06:31:52 -04:00
|
|
|
public void loadMessages() throws IOException {
|
2010-06-08 18:21:53 -04:00
|
|
|
messages = ExchangeSession.this.searchMessages(folderPath, null);
|
2010-01-21 05:49:23 -05:00
|
|
|
fixUids(messages);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search messages in folder matching query.
|
|
|
|
*
|
2010-06-08 18:21:53 -04:00
|
|
|
* @param condition search query
|
2010-01-21 05:49:23 -05:00
|
|
|
* @return message list
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public MessageList searchMessages(Condition condition) throws IOException {
|
2010-06-10 16:47:55 -04:00
|
|
|
MessageList localMessages = ExchangeSession.this.searchMessages(folderPath, condition);
|
2010-01-21 05:49:23 -05:00
|
|
|
fixUids(localMessages);
|
|
|
|
return localMessages;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Restore previous uids changed by a PROPPATCH (flag change).
|
|
|
|
*
|
|
|
|
* @param messages message list
|
|
|
|
*/
|
|
|
|
protected void fixUids(MessageList messages) {
|
|
|
|
boolean sortNeeded = false;
|
|
|
|
for (Message message : messages) {
|
2010-07-05 12:28:10 -04:00
|
|
|
if (permanentUrlToImapUidMap.containsKey(message.permanentUrl)) {
|
|
|
|
long previousUid = permanentUrlToImapUidMap.get(message.permanentUrl);
|
2010-01-21 05:49:23 -05:00
|
|
|
if (message.getImapUid() != previousUid) {
|
2010-07-05 12:28:10 -04:00
|
|
|
LOGGER.debug("Restoring IMAP uid " + message.getImapUid() + " -> " + previousUid + " for message " + message.permanentUrl);
|
2010-01-21 05:49:23 -05:00
|
|
|
message.setImapUid(previousUid);
|
|
|
|
sortNeeded = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// add message to uid map
|
2010-07-05 12:28:10 -04:00
|
|
|
permanentUrlToImapUidMap.put(message.permanentUrl, message.getImapUid());
|
2010-01-21 05:49:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sortNeeded) {
|
|
|
|
Collections.sort(messages);
|
|
|
|
}
|
2009-03-27 06:31:52 -04:00
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Folder message count.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return message count
|
|
|
|
*/
|
|
|
|
public int count() {
|
2009-03-27 06:31:52 -04:00
|
|
|
return messages.size();
|
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Compute IMAP uidnext.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
|
|
|
* @return max(messageuids)+1
|
2009-07-31 02:21:46 -04:00
|
|
|
*/
|
2009-03-27 06:31:52 -04:00
|
|
|
public long getUidNext() {
|
|
|
|
return messages.get(messages.size() - 1).getImapUid() + 1;
|
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Get message at index.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param index message index
|
|
|
|
* @return message
|
|
|
|
*/
|
2009-03-27 06:31:52 -04:00
|
|
|
public Message get(int index) {
|
|
|
|
return messages.get(index);
|
|
|
|
}
|
2010-04-01 05:12:44 -04:00
|
|
|
|
|
|
|
/**
|
2010-04-22 17:50:16 -04:00
|
|
|
* Get current folder messages imap uids
|
2010-04-23 09:42:37 -04:00
|
|
|
*
|
2010-04-22 17:50:16 -04:00
|
|
|
* @return imap uid list
|
2010-04-01 05:12:44 -04:00
|
|
|
*/
|
2010-04-22 17:50:16 -04:00
|
|
|
public List<Long> getImapUidList() {
|
|
|
|
ArrayList<Long> imapUidList = new ArrayList<Long>();
|
|
|
|
for (ExchangeSession.Message message : messages) {
|
|
|
|
imapUidList.add(message.getImapUid());
|
|
|
|
}
|
|
|
|
return imapUidList;
|
|
|
|
}
|
|
|
|
|
2010-04-23 09:42:37 -04:00
|
|
|
/**
|
|
|
|
* Calendar folder flag.
|
|
|
|
*
|
|
|
|
* @return true if this is a calendar folder
|
|
|
|
*/
|
2010-04-01 05:12:44 -04:00
|
|
|
public boolean isCalendar() {
|
2010-06-07 17:17:07 -04:00
|
|
|
return "IPF.Appointment".equals(folderClass);
|
2010-04-01 05:12:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contact folder flag.
|
2010-04-06 05:20:09 -04:00
|
|
|
*
|
2010-04-01 05:12:44 -04:00
|
|
|
* @return true if this is a calendar folder
|
|
|
|
*/
|
|
|
|
public boolean isContact() {
|
2010-06-07 17:17:07 -04:00
|
|
|
return "IPF.Contact".equals(folderClass);
|
2010-04-01 05:12:44 -04:00
|
|
|
}
|
2010-04-22 17:50:16 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* drop cached message
|
|
|
|
*/
|
|
|
|
public void clearCache() {
|
2010-04-23 09:42:37 -04:00
|
|
|
messages.cachedMimeBody = null;
|
2010-04-22 17:50:16 -04:00
|
|
|
messages.cachedMimeMessage = null;
|
|
|
|
messages.cachedMessageImapUid = 0;
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Exchange message.
|
|
|
|
*/
|
2009-11-02 04:53:42 -05:00
|
|
|
public class Message implements Comparable<Message> {
|
2010-04-06 05:20:09 -04:00
|
|
|
/**
|
|
|
|
* enclosing message list
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public MessageList messageList;
|
2010-04-06 05:20:09 -04:00
|
|
|
/**
|
|
|
|
* Message url.
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public String messageUrl;
|
2010-04-06 05:20:09 -04:00
|
|
|
/**
|
|
|
|
* Message permanent url (does not change on message move).
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public String permanentUrl;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message uid.
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public String uid;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message IMAP uid, unique in folder (x0e230003).
|
|
|
|
*/
|
2010-06-08 18:21:53 -04:00
|
|
|
public long imapUid;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* MAPI message size.
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
public int size;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message date (urn:schemas:mailheader:date).
|
|
|
|
*/
|
2009-02-23 12:42:49 -05:00
|
|
|
public String date;
|
2009-07-31 02:21:46 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Message flag: read.
|
|
|
|
*/
|
2009-01-26 18:51:08 -05:00
|
|
|
public boolean read;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: deleted.
|
|
|
|
*/
|
2009-02-02 18:29:44 -05:00
|
|
|
public boolean deleted;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: junk.
|
|
|
|
*/
|
2009-02-03 09:38:05 -05:00
|
|
|
public boolean junk;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: flagged.
|
|
|
|
*/
|
2009-02-03 09:38:05 -05:00
|
|
|
public boolean flagged;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: draft.
|
|
|
|
*/
|
2009-02-03 11:13:00 -05:00
|
|
|
public boolean draft;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: answered.
|
|
|
|
*/
|
2009-02-03 18:54:48 -05:00
|
|
|
public boolean answered;
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Message flag: fowarded.
|
|
|
|
*/
|
2009-02-04 19:18:29 -05:00
|
|
|
public boolean forwarded;
|
2006-12-12 18:57:24 -05:00
|
|
|
|
2010-04-23 09:42:37 -04:00
|
|
|
/**
|
|
|
|
* Unparsed message content.
|
|
|
|
*/
|
|
|
|
protected SharedByteArrayInputStream mimeBody;
|
|
|
|
|
2009-10-29 13:19:00 -04:00
|
|
|
/**
|
|
|
|
* Message content parsed in a MIME message.
|
|
|
|
*/
|
|
|
|
protected MimeMessage mimeMessage;
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* IMAP uid , unique in folder (x0e230003)
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return IMAP uid
|
|
|
|
*/
|
2009-03-19 08:41:11 -04:00
|
|
|
public long getImapUid() {
|
|
|
|
return imapUid;
|
2009-02-02 02:19:57 -05:00
|
|
|
}
|
|
|
|
|
2010-01-21 05:49:23 -05:00
|
|
|
/**
|
|
|
|
* Set IMAP uid.
|
2010-02-01 18:20:56 -05:00
|
|
|
*
|
2010-01-21 05:49:23 -05:00
|
|
|
* @param imapUid new uid
|
|
|
|
*/
|
|
|
|
public void setImapUid(long imapUid) {
|
|
|
|
this.imapUid = imapUid;
|
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Exchange uid.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return uid
|
|
|
|
*/
|
|
|
|
public String getUid() {
|
|
|
|
return uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return message flags in IMAP format.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return IMAP flags
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
2009-10-29 18:45:17 -04:00
|
|
|
/**
|
|
|
|
* Load message content in a Mime message
|
2009-11-01 16:58:04 -05:00
|
|
|
*
|
|
|
|
* @throws IOException on error
|
2009-10-29 18:45:17 -04:00
|
|
|
* @throws MessagingException on error
|
|
|
|
*/
|
2010-04-23 09:42:37 -04:00
|
|
|
protected void loadMimeMessage() throws IOException, MessagingException {
|
2009-10-29 13:19:00 -04:00
|
|
|
if (mimeMessage == null) {
|
2010-04-06 05:20:09 -04:00
|
|
|
// try to get message content from cache
|
|
|
|
if (this.imapUid == messageList.cachedMessageImapUid) {
|
2010-04-23 09:42:37 -04:00
|
|
|
mimeBody = messageList.cachedMimeBody;
|
2010-04-06 05:20:09 -04:00
|
|
|
mimeMessage = messageList.cachedMimeMessage;
|
2010-04-23 09:42:37 -04:00
|
|
|
LOGGER.debug("Got message content for " + imapUid + " from cache");
|
2010-04-06 05:20:09 -04:00
|
|
|
} else {
|
2010-04-23 09:42:37 -04:00
|
|
|
// load and parse message
|
2010-07-05 06:23:51 -04:00
|
|
|
mimeBody = new SharedByteArrayInputStream(getContent(this));
|
2010-04-23 09:42:37 -04:00
|
|
|
mimeMessage = new MimeMessage(null, mimeBody);
|
2010-07-23 04:58:39 -04:00
|
|
|
LOGGER.debug("Downloaded full message content for IMAP UID " + imapUid + " (" + mimeBody.available() + " bytes)");
|
2010-04-06 05:20:09 -04:00
|
|
|
}
|
2009-10-29 13:19:00 -04:00
|
|
|
}
|
2010-04-23 09:42:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get message content as a Mime message.
|
|
|
|
*
|
|
|
|
* @return mime message
|
|
|
|
* @throws IOException on error
|
|
|
|
* @throws MessagingException on error
|
|
|
|
*/
|
|
|
|
public MimeMessage getMimeMessage() throws IOException, MessagingException {
|
|
|
|
loadMimeMessage();
|
2009-10-29 13:19:00 -04:00
|
|
|
return mimeMessage;
|
|
|
|
}
|
|
|
|
|
2010-04-23 09:42:37 -04:00
|
|
|
/**
|
|
|
|
* Get message body size.
|
2010-05-07 04:30:12 -04:00
|
|
|
*
|
2010-04-23 09:42:37 -04:00
|
|
|
* @return mime message size
|
2010-05-07 04:30:12 -04:00
|
|
|
* @throws IOException on error
|
2010-04-23 09:42:37 -04:00
|
|
|
* @throws MessagingException on error
|
|
|
|
*/
|
|
|
|
public int getMimeMessageSize() throws IOException, MessagingException {
|
|
|
|
loadMimeMessage();
|
|
|
|
mimeBody.reset();
|
|
|
|
return mimeBody.available();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get message body input stream.
|
2010-05-07 04:30:12 -04:00
|
|
|
*
|
2010-04-23 09:42:37 -04:00
|
|
|
* @return mime message InputStream
|
2010-05-07 04:30:12 -04:00
|
|
|
* @throws IOException on error
|
2010-04-23 09:42:37 -04:00
|
|
|
* @throws MessagingException on error
|
|
|
|
*/
|
|
|
|
public InputStream getRawInputStream() throws IOException, MessagingException {
|
|
|
|
loadMimeMessage();
|
|
|
|
mimeBody.reset();
|
|
|
|
return mimeBody;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 07:14:47 -04:00
|
|
|
/**
|
2010-04-06 05:20:09 -04:00
|
|
|
* Drop mime message to avoid keeping message content in memory,
|
|
|
|
* keep a single message in MessageList cache to handle chunked fetch.
|
2009-10-30 07:14:47 -04:00
|
|
|
*/
|
|
|
|
public void dropMimeMessage() {
|
2010-04-06 05:20:09 -04:00
|
|
|
// update single message cache
|
|
|
|
if (mimeMessage != null) {
|
|
|
|
messageList.cachedMessageImapUid = imapUid;
|
2010-04-23 09:42:37 -04:00
|
|
|
messageList.cachedMimeBody = mimeBody;
|
2010-04-06 05:20:09 -04:00
|
|
|
messageList.cachedMimeMessage = mimeMessage;
|
|
|
|
}
|
2009-10-30 07:14:47 -04:00
|
|
|
mimeMessage = null;
|
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Delete message.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2006-12-12 18:57:24 -05:00
|
|
|
public void delete() throws IOException {
|
2010-06-09 05:52:12 -04:00
|
|
|
deleteMessage(this);
|
2009-02-03 16:02:33 -05:00
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Move message to trash, mark message read.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-02-03 16:02:33 -05:00
|
|
|
public void moveToTrash() throws IOException {
|
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);
|
|
|
|
|
2010-06-09 05:13:13 -04:00
|
|
|
ExchangeSession.this.moveToTrash(this);
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Comparator to sort messages by IMAP uid
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param message other message
|
|
|
|
* @return imapUid comparison result
|
|
|
|
*/
|
2009-11-02 04:53:42 -05:00
|
|
|
public int compareTo(Message message) {
|
|
|
|
long compareValue = (imapUid - message.imapUid);
|
2009-03-03 10:59:06 -05:00
|
|
|
if (compareValue > 0) {
|
|
|
|
return 1;
|
|
|
|
} else if (compareValue < 0) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
}
|
2009-02-12 09:31:11 -05:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Override equals, compare IMAP uids
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @param message other message
|
|
|
|
* @return true if IMAP uids are equal
|
|
|
|
*/
|
2009-02-12 09:31:11 -05:00
|
|
|
@Override
|
|
|
|
public boolean equals(Object message) {
|
2009-04-16 17:16:40 -04:00
|
|
|
return message instanceof Message && imapUid == ((Message) message).imapUid;
|
2009-02-12 09:31:11 -05:00
|
|
|
}
|
2009-03-27 09:00:47 -04:00
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
|
|
|
* Override hashCode, return imapUid hashcode.
|
2009-08-04 16:48:35 -04:00
|
|
|
*
|
2009-07-31 02:21:46 -04:00
|
|
|
* @return imapUid hashcode
|
|
|
|
*/
|
2009-03-27 09:00:47 -04:00
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
2009-04-01 11:51:12 -04:00
|
|
|
return (int) (imapUid ^ (imapUid >>> 32));
|
2009-03-27 09:00:47 -04:00
|
|
|
}
|
2009-02-02 02:19:57 -05:00
|
|
|
}
|
|
|
|
|
2009-07-31 02:21:46 -04:00
|
|
|
/**
|
2010-04-06 05:20:09 -04:00
|
|
|
* Message list, includes a single messsage cache
|
2009-07-31 02:21:46 -04:00
|
|
|
*/
|
2009-08-04 16:48:35 -04:00
|
|
|
public static class MessageList extends ArrayList<Message> {
|
2010-04-06 05:20:09 -04:00
|
|
|
/**
|
|
|
|
* Cached message content parsed in a MIME message.
|
|
|
|
*/
|
2010-04-12 04:54:53 -04:00
|
|
|
protected transient MimeMessage cachedMimeMessage;
|
2010-04-06 05:20:09 -04:00
|
|
|
/**
|
|
|
|
* Cached message uid.
|
|
|
|
*/
|
2010-04-12 04:54:53 -04:00
|
|
|
protected transient long cachedMessageImapUid;
|
2010-04-23 09:42:37 -04:00
|
|
|
/**
|
|
|
|
* Cached unparsed message
|
|
|
|
*/
|
|
|
|
protected transient SharedByteArrayInputStream cachedMimeBody;
|
2010-04-06 05:20:09 -04:00
|
|
|
|
2009-08-04 16:48:35 -04:00
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
2010-05-06 15:26:44 -04:00
|
|
|
* Generic folder item.
|
2009-08-13 04:34:51 -04:00
|
|
|
*/
|
2010-06-21 16:37:20 -04:00
|
|
|
public abstract static class Item extends HashMap<String, String> {
|
2010-07-06 05:46:55 -04:00
|
|
|
protected String folderPath;
|
|
|
|
protected String itemName;
|
2010-07-07 05:20:42 -04:00
|
|
|
protected String permanentUrl;
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* Display name.
|
|
|
|
*/
|
2010-06-15 10:51:49 -04:00
|
|
|
public String displayName;
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
|
|
|
* item etag
|
|
|
|
*/
|
2010-06-15 10:51:49 -04:00
|
|
|
public String etag;
|
2009-11-27 04:34:20 -05:00
|
|
|
protected String noneMatch;
|
2010-05-06 15:26:44 -04:00
|
|
|
|
|
|
|
/**
|
2010-05-07 04:30:12 -04:00
|
|
|
* Build item instance.
|
|
|
|
*
|
2010-07-06 05:46:55 -04:00
|
|
|
* @param folderPath folder path
|
|
|
|
* @param itemName item name class
|
|
|
|
* @param etag item etag
|
|
|
|
* @param noneMatch none match flag
|
|
|
|
*/
|
|
|
|
public Item(String folderPath, String itemName, String etag, String noneMatch) {
|
|
|
|
this.folderPath = folderPath;
|
|
|
|
this.itemName = itemName;
|
2010-05-07 04:30:12 -04:00
|
|
|
this.etag = etag;
|
|
|
|
this.noneMatch = noneMatch;
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:51:49 -04:00
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*/
|
|
|
|
protected Item() {
|
|
|
|
}
|
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
/**
|
|
|
|
* Return item content type
|
2010-05-07 04:30:12 -04:00
|
|
|
*
|
2010-05-06 15:26:44 -04:00
|
|
|
* @return content type
|
|
|
|
*/
|
|
|
|
public abstract String getContentType();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve item body from Exchange
|
2010-05-07 04:30:12 -04:00
|
|
|
*
|
2010-05-06 15:26:44 -04:00
|
|
|
* @return item body
|
|
|
|
* @throws HttpException on error
|
|
|
|
*/
|
2010-07-29 17:01:28 -04:00
|
|
|
public abstract String getBody() throws IOException;
|
2010-05-06 15:26:44 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get event name (file name part in URL).
|
|
|
|
*
|
|
|
|
* @return event name
|
|
|
|
*/
|
|
|
|
public String getName() {
|
2010-07-06 05:46:55 -04:00
|
|
|
return itemName;
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get event etag (last change tag).
|
|
|
|
*
|
|
|
|
* @return event etag
|
|
|
|
*/
|
|
|
|
public String getEtag() {
|
|
|
|
return etag;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected HttpException buildHttpException(Exception e) {
|
|
|
|
String message = "Unable to get event " + getName() + " at " + permanentUrl + ": " + e.getMessage();
|
|
|
|
LOGGER.warn(message);
|
|
|
|
return new HttpException(message);
|
|
|
|
}
|
2010-07-06 05:46:55 -04:00
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Set item href.
|
|
|
|
*
|
|
|
|
* @param href item href
|
|
|
|
*/
|
2010-07-06 05:46:55 -04:00
|
|
|
public void setHref(String href) {
|
|
|
|
int index = href.lastIndexOf('/');
|
|
|
|
if (index >= 0) {
|
|
|
|
folderPath = href.substring(0, index);
|
|
|
|
itemName = href.substring(index + 1);
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException(href);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Return item href.
|
|
|
|
*
|
|
|
|
* @return item href
|
|
|
|
*/
|
2010-07-06 05:46:55 -04:00
|
|
|
public String getHref() {
|
|
|
|
return folderPath + '/' + itemName;
|
|
|
|
}
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
2010-05-07 04:30:12 -04:00
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
/**
|
2010-07-06 19:40:48 -04:00
|
|
|
* Contact object
|
2010-05-06 15:26:44 -04:00
|
|
|
*/
|
2010-07-06 19:40:48 -04:00
|
|
|
public abstract class Contact extends Item {
|
2010-06-15 10:51:49 -04:00
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
/**
|
2010-06-15 10:51:49 -04:00
|
|
|
* @inheritDoc
|
2010-05-06 15:26:44 -04:00
|
|
|
*/
|
2010-07-06 05:46:55 -04:00
|
|
|
public Contact(String folderPath, String itemName, Map<String, String> properties, String etag, String noneMatch) {
|
|
|
|
super(folderPath, itemName.endsWith(".vcf") ? itemName.substring(0, itemName.length() - 3) + "EML" : itemName, etag, noneMatch);
|
|
|
|
this.putAll(properties);
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
2010-05-07 04:30:12 -04:00
|
|
|
/**
|
2010-06-15 10:51:49 -04:00
|
|
|
* @inheritDoc
|
2010-05-07 04:30:12 -04:00
|
|
|
*/
|
2010-06-15 10:51:49 -04:00
|
|
|
protected Contact() {
|
2010-05-07 04:30:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert EML extension to vcf.
|
|
|
|
*
|
|
|
|
* @return item name
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public String getName() {
|
|
|
|
String name = super.getName();
|
|
|
|
if (name.endsWith(".EML")) {
|
|
|
|
name = name.substring(0, name.length() - 3) + "vcf";
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2010-08-16 17:50:18 -04:00
|
|
|
/**
|
|
|
|
* Set contact name
|
|
|
|
*
|
|
|
|
* @param name contact name
|
|
|
|
*/
|
2010-08-09 16:15:28 -04:00
|
|
|
public void setName(String name) {
|
|
|
|
this.itemName = name;
|
|
|
|
}
|
|
|
|
|
2010-05-07 04:30:12 -04:00
|
|
|
/**
|
|
|
|
* Compute vcard uid from name.
|
|
|
|
*
|
|
|
|
* @return uid
|
|
|
|
* @throws URIException on error
|
|
|
|
*/
|
|
|
|
protected String getUid() throws URIException {
|
|
|
|
String uid = getName();
|
|
|
|
int dotIndex = uid.lastIndexOf('.');
|
|
|
|
if (dotIndex > 0) {
|
|
|
|
uid = uid.substring(0, dotIndex);
|
|
|
|
}
|
|
|
|
return URIUtil.encodePath(uid);
|
|
|
|
}
|
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
@Override
|
|
|
|
public String getContentType() {
|
|
|
|
return "text/vcard";
|
|
|
|
}
|
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getBody() throws HttpException {
|
|
|
|
// build RFC 2426 VCard from contact information
|
|
|
|
VCardWriter writer = new VCardWriter();
|
|
|
|
writer.startCard();
|
|
|
|
writer.appendProperty("UID", getUid());
|
|
|
|
// common name
|
|
|
|
writer.appendProperty("FN", get("cn"));
|
|
|
|
// RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes
|
|
|
|
writer.appendProperty("N", get("sn"), get("givenName"), get("middlename"), get("personaltitle"), get("namesuffix"));
|
2010-07-01 08:47:57 -04:00
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
writer.appendProperty("TEL;TYPE=cell", get("mobile"));
|
|
|
|
writer.appendProperty("TEL;TYPE=work", get("telephoneNumber"));
|
|
|
|
writer.appendProperty("TEL;TYPE=home", get("homePhone"));
|
|
|
|
writer.appendProperty("TEL;TYPE=fax", get("facsimiletelephonenumber"));
|
|
|
|
writer.appendProperty("TEL;TYPE=pager", get("pager"));
|
2010-07-19 10:07:46 -04:00
|
|
|
writer.appendProperty("TEL;TYPE=car", get("othermobile"));
|
2010-07-26 11:08:06 -04:00
|
|
|
writer.appendProperty("TEL;TYPE=home,fax", get("homefax"));
|
|
|
|
writer.appendProperty("TEL;TYPE=isdn", get("internationalisdnnumber"));
|
|
|
|
writer.appendProperty("TEL;TYPE=msg", get("otherTelephone"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
|
|
|
// The structured type value corresponds, in sequence, to the post office box; the extended address;
|
|
|
|
// the street address; the locality (e.g., city); the region (e.g., state or province);
|
|
|
|
// the postal code; the country name
|
|
|
|
writer.appendProperty("ADR;TYPE=home",
|
2010-07-06 12:26:55 -04:00
|
|
|
get("homepostofficebox"), null, get("homeStreet"), get("homeCity"), get("homeState"), get("homePostalCode"), get("homeCountry"));
|
2010-06-30 17:38:01 -04:00
|
|
|
writer.appendProperty("ADR;TYPE=work",
|
|
|
|
get("postofficebox"), get("roomnumber"), get("street"), get("l"), get("st"), get("postalcode"), get("co"));
|
2010-07-06 12:26:55 -04:00
|
|
|
writer.appendProperty("ADR;TYPE=other",
|
|
|
|
get("otherpostofficebox"), null, get("otherstreet"), get("othercity"), get("otherstate"), get("otherpostalcode"), get("othercountry"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
2010-07-23 06:13:08 -04:00
|
|
|
writer.appendProperty("EMAIL;TYPE=work", get("smtpemail1"));
|
|
|
|
writer.appendProperty("EMAIL;TYPE=home", get("smtpemail2"));
|
|
|
|
writer.appendProperty("EMAIL;TYPE=other", get("smtpemail3"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
|
|
|
writer.appendProperty("ORG", get("o"), get("department"));
|
2010-07-06 10:02:40 -04:00
|
|
|
writer.appendProperty("URL;TYPE=work", get("businesshomepage"));
|
2010-07-19 10:07:46 -04:00
|
|
|
writer.appendProperty("URL;TYPE=home", get("personalHomePage"));
|
2010-06-30 17:38:01 -04:00
|
|
|
writer.appendProperty("TITLE", get("title"));
|
2010-07-06 05:46:55 -04:00
|
|
|
writer.appendProperty("NOTE", get("description"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
|
|
|
writer.appendProperty("CUSTOM1", get("extensionattribute1"));
|
|
|
|
writer.appendProperty("CUSTOM2", get("extensionattribute2"));
|
|
|
|
writer.appendProperty("CUSTOM3", get("extensionattribute3"));
|
|
|
|
writer.appendProperty("CUSTOM4", get("extensionattribute4"));
|
|
|
|
|
|
|
|
writer.appendProperty("ROLE", get("profession"));
|
|
|
|
writer.appendProperty("NICKNAME", get("nickname"));
|
|
|
|
writer.appendProperty("X-AIM", get("im"));
|
2010-07-20 12:33:58 -04:00
|
|
|
|
|
|
|
writer.appendProperty("BDAY", convertZuluDateToBday(get("bday")));
|
2010-07-20 16:18:01 -04:00
|
|
|
writer.appendProperty("ANNIVERSARY", convertZuluDateToBday(get("anniversary")));
|
|
|
|
|
|
|
|
String gender = get("gender");
|
|
|
|
if ("1".equals(gender)) {
|
|
|
|
writer.appendProperty("SEX", "2");
|
|
|
|
} else if ("2".equals(gender)) {
|
|
|
|
writer.appendProperty("SEX", "1");
|
|
|
|
}
|
2010-06-30 17:38:01 -04:00
|
|
|
|
2010-07-13 05:56:48 -04:00
|
|
|
writer.appendProperty("CATEGORIES", get("keywords"));
|
|
|
|
|
2010-07-26 09:22:28 -04:00
|
|
|
writer.appendProperty("FBURL", get("fburl"));
|
|
|
|
|
2010-07-18 08:26:12 -04:00
|
|
|
if ("1".equals(get("private"))) {
|
|
|
|
writer.appendProperty("CLASS", "PRIVATE");
|
|
|
|
}
|
|
|
|
|
2010-07-06 12:49:57 -04:00
|
|
|
writer.appendProperty("X-ASSISTANT", get("secretarycn"));
|
|
|
|
writer.appendProperty("X-MANAGER", get("manager"));
|
|
|
|
writer.appendProperty("X-SPOUSE", get("spousecn"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
2010-07-06 12:49:57 -04:00
|
|
|
writer.appendProperty("REV", get("lastmodified"));
|
2010-06-30 17:38:01 -04:00
|
|
|
|
2010-07-19 08:45:59 -04:00
|
|
|
if ("true".equals(get("haspicture"))) {
|
2010-07-06 19:40:48 -04:00
|
|
|
try {
|
|
|
|
ContactPhoto contactPhoto = getContactPhoto(this);
|
2010-07-24 11:53:06 -04:00
|
|
|
writer.writeLine("PHOTO;BASE64;TYPE=\"" + contactPhoto.contentType + "\";ENCODING=\"b\":");
|
|
|
|
writer.writeLine(contactPhoto.content, true);
|
2010-07-06 19:40:48 -04:00
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.warn("Unable to get photo from contact " + this.get("cn"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-30 17:38:01 -04:00
|
|
|
writer.endCard();
|
|
|
|
return writer.toString();
|
|
|
|
}
|
|
|
|
|
2010-07-20 12:33:58 -04:00
|
|
|
protected String convertZuluDateToBday(String value) {
|
|
|
|
String result = null;
|
|
|
|
if (value != null && value.length() > 0) {
|
|
|
|
try {
|
|
|
|
SimpleDateFormat parser = ExchangeSession.getZuluDateFormat();
|
2010-07-20 13:19:01 -04:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.setTime(parser.parse(value));
|
|
|
|
cal.add(Calendar.HOUR_OF_DAY, 12);
|
|
|
|
result = ExchangeSession.getVcardBdayFormat().format(cal.getTime());
|
2010-07-20 12:33:58 -04:00
|
|
|
} catch (ParseException e) {
|
|
|
|
LOGGER.warn("Invalid date: " + value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-05-07 04:30:12 -04:00
|
|
|
* Calendar event object.
|
2010-05-06 15:26:44 -04:00
|
|
|
*/
|
2010-06-15 10:51:49 -04:00
|
|
|
public abstract class Event extends Item {
|
2010-07-06 05:46:55 -04:00
|
|
|
protected String contentClass;
|
2010-07-31 18:52:45 -04:00
|
|
|
protected VCalendar vCalendar;
|
2010-05-06 15:26:44 -04:00
|
|
|
|
|
|
|
/**
|
2010-06-21 05:38:24 -04:00
|
|
|
* @inheritDoc
|
2010-05-06 15:26:44 -04:00
|
|
|
*/
|
2010-07-31 18:52:45 -04:00
|
|
|
public Event(String folderPath, String itemName, String contentClass, String itemBody, String etag, String noneMatch) throws IOException {
|
2010-07-06 05:46:55 -04:00
|
|
|
super(folderPath, itemName, etag, noneMatch);
|
|
|
|
this.contentClass = contentClass;
|
2010-07-31 18:52:45 -04:00
|
|
|
fixICS(itemBody.getBytes("UTF-8"), false);
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:51:49 -04:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
protected Event() {
|
|
|
|
}
|
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
@Override
|
|
|
|
public String getContentType() {
|
|
|
|
return "text/calendar;charset=UTF-8";
|
|
|
|
}
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
@Override
|
|
|
|
public String getBody() throws IOException {
|
|
|
|
if (vCalendar == null) {
|
|
|
|
fixICS(getEventContent(), true);
|
2010-04-15 08:56:55 -04:00
|
|
|
}
|
2010-07-31 18:52:45 -04:00
|
|
|
return vCalendar.toString();
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
/**
|
|
|
|
* Retrieve item body from Exchange
|
|
|
|
*
|
|
|
|
* @return item content
|
|
|
|
* @throws HttpException on error
|
|
|
|
*/
|
|
|
|
public abstract byte[] getEventContent() throws IOException;
|
2009-11-27 04:34:20 -05:00
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
protected void fixICS(byte[] icsContent, boolean fromServer) throws IOException {
|
2010-07-30 11:09:44 -04:00
|
|
|
if (LOGGER.isDebugEnabled() && fromServer) {
|
2010-07-31 18:52:45 -04:00
|
|
|
dumpIndex++;
|
|
|
|
String icsBody = new String(icsContent);
|
|
|
|
dumpICS(icsBody, fromServer, false);
|
|
|
|
LOGGER.debug("Vcalendar body received from server:\n" + icsBody);
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2010-07-31 18:52:45 -04:00
|
|
|
vCalendar = new VCalendar(icsContent, getEmail(), getVTimezone());
|
2010-07-29 20:04:54 -04:00
|
|
|
vCalendar.fixVCalendar(fromServer);
|
2010-07-30 11:09:44 -04:00
|
|
|
if (LOGGER.isDebugEnabled() && !fromServer) {
|
2010-07-31 18:52:45 -04:00
|
|
|
String resultString = vCalendar.toString();
|
|
|
|
LOGGER.debug("Fixed Vcalendar body to server:\n" + resultString);
|
|
|
|
dumpICS(resultString, fromServer, true);
|
2010-07-30 11:09:44 -04:00
|
|
|
}
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void dumpICS(String icsBody, boolean fromServer, boolean after) {
|
|
|
|
String logFileDirectory = Settings.getLogFileDirectory();
|
|
|
|
|
|
|
|
// additional setting to activate ICS dump (not available in GUI)
|
|
|
|
int dumpMax = Settings.getIntProperty("davmail.dumpICS");
|
|
|
|
if (dumpMax > 0) {
|
|
|
|
if (dumpIndex > dumpMax) {
|
|
|
|
// Delete the oldest dump file
|
|
|
|
final int oldest = dumpIndex - dumpMax;
|
|
|
|
try {
|
|
|
|
File[] oldestFiles = (new File(logFileDirectory)).listFiles(new FilenameFilter() {
|
|
|
|
public boolean accept(File dir, String name) {
|
|
|
|
if (name.endsWith(".ics")) {
|
|
|
|
int dashIndex = name.indexOf('-');
|
|
|
|
if (dashIndex > 0) {
|
|
|
|
try {
|
|
|
|
int fileIndex = Integer.parseInt(name.substring(0, dashIndex));
|
|
|
|
return fileIndex < oldest;
|
|
|
|
} catch (NumberFormatException nfe) {
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
for (File file : oldestFiles) {
|
|
|
|
if (!file.delete()) {
|
|
|
|
LOGGER.warn("Unable to delete " + file.getAbsolutePath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LOGGER.warn("Error deleting ics dump: " + ex.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuilder filePath = new StringBuilder();
|
|
|
|
filePath.append(logFileDirectory).append('/')
|
|
|
|
.append(dumpIndex)
|
|
|
|
.append(after ? "-to" : "-from")
|
|
|
|
.append((after ^ fromServer) ? "-server" : "-client")
|
|
|
|
.append(".ics");
|
|
|
|
if ((icsBody != null) && (icsBody.length() > 0)) {
|
|
|
|
FileWriter fileWriter = null;
|
|
|
|
try {
|
|
|
|
fileWriter = new FileWriter(filePath.toString());
|
|
|
|
fileWriter.write(icsBody);
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error(e);
|
|
|
|
} finally {
|
|
|
|
if (fileWriter != null) {
|
|
|
|
try {
|
|
|
|
fileWriter.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-06-21 16:37:20 -04:00
|
|
|
/**
|
2010-07-26 18:20:34 -04:00
|
|
|
* Build Mime body for event or event message.
|
2010-06-21 16:37:20 -04:00
|
|
|
*
|
2010-07-26 18:20:34 -04:00
|
|
|
* @return mimeContent as byte array or null
|
2010-06-21 16:37:20 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-26 18:20:34 -04:00
|
|
|
public byte[] createMimeContent() throws IOException {
|
2009-11-27 04:34:20 -05:00
|
|
|
String boundary = UUID.randomUUID().toString();
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
2009-12-24 10:33:26 -05:00
|
|
|
MimeOutputStreamWriter writer = new MimeOutputStreamWriter(baos);
|
2009-11-27 04:34:20 -05:00
|
|
|
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("Content-Transfer-Encoding", "7bit");
|
|
|
|
writer.writeHeader("Content-class", contentClass);
|
2009-11-27 04:34:20 -05:00
|
|
|
// append date
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("Date", new Date());
|
|
|
|
|
|
|
|
// Make sure invites have a proper subject line
|
2010-07-31 19:21:07 -04:00
|
|
|
String subject = vCalendar.getFirstVeventPropertyValue("SUMMARY");
|
2010-07-31 18:52:45 -04:00
|
|
|
if (subject == null) {
|
2010-08-09 16:15:28 -04:00
|
|
|
subject = BundleMessage.format("MEETING_REQUEST");
|
2010-07-31 18:52:45 -04:00
|
|
|
}
|
2010-08-16 16:33:56 -04:00
|
|
|
|
|
|
|
// Write a part of the message that contains the
|
|
|
|
// ICS description so that invites contain the description text
|
|
|
|
String description = vCalendar.getFirstVeventPropertyValue("DESCRIPTION");
|
2009-12-24 10:33:26 -05:00
|
|
|
|
2009-11-27 04:34:20 -05:00
|
|
|
if ("urn:content-classes:calendarmessage".equals(contentClass)) {
|
|
|
|
// need to parse attendees and organizer to build recipients
|
2010-07-31 18:52:45 -04:00
|
|
|
VCalendar.Recipients recipients = vCalendar.getRecipients(true);
|
|
|
|
if (email.equalsIgnoreCase(recipients.organizer)) {
|
2009-11-27 04:34:20 -05:00
|
|
|
// current user is organizer => notify all
|
2010-07-31 18:52:45 -04:00
|
|
|
writer.writeHeader("To", recipients.attendees);
|
|
|
|
writer.writeHeader("Cc", recipients.optionalAttendees);
|
2009-12-24 10:33:26 -05:00
|
|
|
// do not send notification if no recipients found
|
2010-07-31 18:52:45 -04:00
|
|
|
if (recipients.attendees == null && recipients.optionalAttendees == null) {
|
2010-07-26 18:20:34 -04:00
|
|
|
return null;
|
2010-01-05 05:06:47 -05:00
|
|
|
}
|
2009-11-27 04:34:20 -05:00
|
|
|
} else {
|
2010-08-16 16:33:56 -04:00
|
|
|
// reset body
|
|
|
|
description = "";
|
2010-08-16 17:50:18 -04:00
|
|
|
|
2010-08-16 16:33:56 -04:00
|
|
|
String status = vCalendar.getAttendeeStatus();
|
|
|
|
if (status != null) {
|
2010-08-16 17:50:18 -04:00
|
|
|
writer.writeHeader("Subject", BundleMessage.format(status) + subject);
|
2010-08-16 16:33:56 -04:00
|
|
|
} else {
|
|
|
|
writer.writeHeader("Subject", subject);
|
|
|
|
}
|
|
|
|
|
2009-11-27 04:34:20 -05:00
|
|
|
// notify only organizer
|
2010-07-31 18:52:45 -04:00
|
|
|
writer.writeHeader("To", recipients.organizer);
|
2009-12-24 10:33:26 -05:00
|
|
|
// do not send notification if no recipients found
|
2010-07-31 18:52:45 -04:00
|
|
|
if (recipients.organizer == null) {
|
2010-07-26 18:20:34 -04:00
|
|
|
return null;
|
2010-01-05 05:06:47 -05:00
|
|
|
}
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2010-07-31 19:21:07 -04:00
|
|
|
if (LOGGER.isDebugEnabled()) {
|
|
|
|
StringBuilder logBuffer = new StringBuilder("Sending notification");
|
|
|
|
if (recipients.attendees != null) {
|
|
|
|
logBuffer.append("to: ").append(recipients.attendees);
|
|
|
|
}
|
|
|
|
if (recipients.optionalAttendees != null) {
|
|
|
|
logBuffer.append("cc: ").append(recipients.optionalAttendees);
|
|
|
|
}
|
|
|
|
}
|
2009-11-27 04:34:20 -05:00
|
|
|
} else {
|
|
|
|
// need to parse attendees and organizer to build recipients
|
2010-07-31 18:52:45 -04:00
|
|
|
VCalendar.Recipients recipients = vCalendar.getRecipients(false);
|
2009-11-27 04:34:20 -05:00
|
|
|
// storing appointment, full recipients header
|
2010-07-31 18:52:45 -04:00
|
|
|
if (recipients.attendees != null) {
|
|
|
|
writer.writeHeader("To", recipients.attendees);
|
2009-11-27 04:34:20 -05:00
|
|
|
} else {
|
2009-12-24 10:33:26 -05:00
|
|
|
// use current user as attendee
|
|
|
|
writer.writeHeader("To", email);
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2010-07-31 18:52:45 -04:00
|
|
|
writer.writeHeader("Cc", recipients.optionalAttendees);
|
2009-11-27 04:34:20 -05:00
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
if (recipients.organizer != null) {
|
|
|
|
writer.writeHeader("From", recipients.organizer);
|
2009-11-27 04:34:20 -05:00
|
|
|
} else {
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("From", email);
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2010-08-06 17:05:21 -04:00
|
|
|
}
|
|
|
|
if (vCalendar.getMethod() == null) {
|
|
|
|
vCalendar.setPropertyValue("METHOD", "REQUEST");
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("MIME-Version", "1.0");
|
2010-01-05 05:06:47 -05:00
|
|
|
writer.writeHeader("Content-Type", "multipart/alternative;\r\n" +
|
|
|
|
"\tboundary=\"----=_NextPart_" + boundary + '\"');
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeLn();
|
|
|
|
writer.writeLn("This is a multi-part message in MIME format.");
|
|
|
|
writer.writeLn();
|
2010-01-05 05:06:47 -05:00
|
|
|
writer.writeLn("------=_NextPart_" + boundary);
|
2009-11-27 04:34:20 -05:00
|
|
|
|
2010-07-31 18:52:45 -04:00
|
|
|
if (description != null && description.length() > 0) {
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("Content-Type", "text/plain;\r\n" +
|
|
|
|
"\tcharset=\"utf-8\"");
|
|
|
|
writer.writeHeader("content-transfer-encoding", "8bit");
|
|
|
|
writer.writeLn();
|
2009-11-27 04:34:20 -05:00
|
|
|
writer.flush();
|
|
|
|
baos.write(description.getBytes("UTF-8"));
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeLn();
|
2010-01-05 05:06:47 -05:00
|
|
|
writer.writeLn("------=_NextPart_" + boundary);
|
2009-11-27 04:34:20 -05:00
|
|
|
}
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeHeader("Content-class", contentClass);
|
2010-01-05 05:06:47 -05:00
|
|
|
writer.writeHeader("Content-Type", "text/calendar;\r\n" +
|
2010-08-06 17:05:21 -04:00
|
|
|
"\tmethod=" + vCalendar.getMethod() + ";\r\n" +
|
2009-12-24 10:33:26 -05:00
|
|
|
"\tcharset=\"utf-8\""
|
|
|
|
);
|
|
|
|
writer.writeHeader("Content-Transfer-Encoding", "8bit");
|
|
|
|
writer.writeLn();
|
2009-11-27 04:34:20 -05:00
|
|
|
writer.flush();
|
2010-07-31 18:52:45 -04:00
|
|
|
baos.write(vCalendar.toString().getBytes("UTF-8"));
|
2009-12-24 10:33:26 -05:00
|
|
|
writer.writeLn();
|
2010-01-05 05:06:47 -05:00
|
|
|
writer.writeLn("------=_NextPart_" + boundary + "--");
|
2009-11-27 04:34:20 -05:00
|
|
|
writer.close();
|
2010-07-26 18:20:34 -04:00
|
|
|
return baos.toByteArray();
|
|
|
|
}
|
2009-11-27 04:34:20 -05:00
|
|
|
|
2010-07-26 18:20:34 -04:00
|
|
|
/**
|
|
|
|
* Create or update item
|
|
|
|
*
|
|
|
|
* @return action result
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-31 18:52:45 -04:00
|
|
|
public abstract ItemResult createOrUpdate() throws IOException;
|
2010-06-15 10:51:49 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Common item properties
|
|
|
|
*/
|
|
|
|
protected static final Set<String> ITEM_PROPERTIES = new HashSet<String>();
|
2010-06-15 10:51:49 -04:00
|
|
|
|
|
|
|
static {
|
|
|
|
ITEM_PROPERTIES.add("etag");
|
|
|
|
ITEM_PROPERTIES.add("displayname");
|
|
|
|
// calendar CdoInstanceType
|
|
|
|
ITEM_PROPERTIES.add("instancetype");
|
2010-07-08 19:26:41 -04:00
|
|
|
ITEM_PROPERTIES.add("urlcompname");
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
/**
|
|
|
|
* Search contacts in provided folder.
|
|
|
|
*
|
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @return list of contacts
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-21 16:37:20 -04:00
|
|
|
public List<ExchangeSession.Contact> getAllContacts(String folderPath) throws IOException {
|
2010-07-27 16:34:43 -04:00
|
|
|
return searchContacts(folderPath, ITEM_PROPERTIES, isEqualTo("outlookmessageclass", "IPM.Contact"), 0);
|
2010-05-06 15:26:44 -04:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:51:49 -04:00
|
|
|
|
2010-05-06 15:26:44 -04:00
|
|
|
/**
|
|
|
|
* Search contacts in provided folder matching the search query.
|
|
|
|
*
|
2010-06-21 16:37:20 -04:00
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @param attributes requested attributes
|
|
|
|
* @param condition Exchange search query
|
2010-07-28 13:11:18 -04:00
|
|
|
* @param maxCount maximum item count
|
2010-05-06 15:26:44 -04:00
|
|
|
* @return list of contacts
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-27 16:34:43 -04:00
|
|
|
public abstract List<Contact> searchContacts(String folderPath, Set<String> attributes, Condition condition, int maxCount) throws IOException;
|
2010-05-06 15:26:44 -04:00
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Search calendar messages in provided folder.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @return list of calendar messages as Event objects
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public List<Event> getEventMessages(String folderPath) throws IOException {
|
2010-06-15 10:51:49 -04:00
|
|
|
return searchEvents(folderPath, ITEM_PROPERTIES,
|
2010-07-27 09:19:28 -04:00
|
|
|
and(isEqualTo("outlookmessageclass", "IPM.Schedule.Meeting.Request"),
|
2010-06-16 08:06:59 -04:00
|
|
|
or(isNull("processed"), isFalse("processed"))));
|
2009-02-20 12:44:30 -05:00
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Search calendar events in provided folder.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @return list of calendar events
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-04-14 19:46:37 -04:00
|
|
|
public List<Event> getAllEvents(String folderPath) throws IOException {
|
2010-04-14 16:01:51 -04:00
|
|
|
int caldavPastDelay = Settings.getIntProperty("davmail.caldavPastDelay");
|
2010-06-15 10:51:49 -04:00
|
|
|
Condition dateCondition = null;
|
2010-04-14 16:01:51 -04:00
|
|
|
if (caldavPastDelay != 0) {
|
2008-12-05 05:45:23 -05:00
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -caldavPastDelay);
|
2010-06-15 10:51:49 -04:00
|
|
|
dateCondition = gt("dtstart", formatSearchDate(cal.getTime()));
|
2008-12-05 05:45:23 -05:00
|
|
|
}
|
|
|
|
|
2010-07-22 19:10:24 -04:00
|
|
|
return searchEvents(folderPath, dateCondition);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search events between start and end.
|
|
|
|
*
|
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @param timeRangeStart date range start in zulu format
|
|
|
|
* @param timeRangeEnd date range start in zulu format
|
|
|
|
* @return list of calendar events
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public List<Event> searchEvents(String folderPath, String timeRangeStart, String timeRangeEnd) throws IOException {
|
|
|
|
try {
|
|
|
|
SimpleDateFormat parser = getZuluDateFormat();
|
|
|
|
ExchangeSession.MultiCondition andCondition = and();
|
|
|
|
if (timeRangeStart != null) {
|
|
|
|
andCondition.add(gte("dtstart", formatSearchDate(parser.parse(timeRangeStart))));
|
|
|
|
}
|
|
|
|
if (timeRangeEnd != null) {
|
|
|
|
andCondition.add(lte("dtend", formatSearchDate(parser.parse(timeRangeEnd))));
|
|
|
|
}
|
2010-08-17 11:10:19 -04:00
|
|
|
return searchEvents(folderPath, andCondition);
|
2010-07-22 19:10:24 -04:00
|
|
|
} catch (ParseException e) {
|
|
|
|
throw new IOException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search calendar events in provided folder.
|
|
|
|
*
|
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @param dateCondition date filter
|
|
|
|
* @return list of calendar events
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public List<Event> searchEvents(String folderPath, Condition dateCondition) throws IOException {
|
|
|
|
|
2010-06-15 10:51:49 -04:00
|
|
|
Condition privateCondition = null;
|
2010-04-12 04:55:47 -04:00
|
|
|
if (isSharedFolder(folderPath)) {
|
2010-03-29 04:50:39 -04:00
|
|
|
LOGGER.debug("Shared or public calendar: exclude private events");
|
2010-07-27 09:19:28 -04:00
|
|
|
privateCondition = isEqualTo("sensitivity", 0);
|
2010-06-15 10:51:49 -04:00
|
|
|
}
|
2010-07-22 19:10:24 -04:00
|
|
|
// instancetype 0 single appointment / 1 master recurring appointment
|
2010-06-15 10:51:49 -04:00
|
|
|
return searchEvents(folderPath, ITEM_PROPERTIES,
|
|
|
|
and(or(isNull("instancetype"),
|
2010-07-27 09:19:28 -04:00
|
|
|
isEqualTo("instancetype", 1),
|
|
|
|
and(isEqualTo("instancetype", 0), dateCondition)),
|
2010-06-15 10:51:49 -04:00
|
|
|
privateCondition));
|
2009-02-20 12:44:30 -05:00
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Search calendar events or messages in provided folder matching the search query.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2010-06-21 16:37:20 -04:00
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @param attributes requested attributes
|
|
|
|
* @param condition Exchange search query
|
2009-08-13 04:34:51 -04:00
|
|
|
* @return list of calendar messages as Event objects
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-22 19:10:24 -04:00
|
|
|
public abstract List<Event> searchEvents(String folderPath, Set<String> attributes, Condition condition) throws IOException;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2010-05-07 06:03:49 -04:00
|
|
|
/**
|
|
|
|
* convert vcf extension to EML.
|
2010-05-11 08:52:15 -04:00
|
|
|
*
|
2010-05-07 06:03:49 -04:00
|
|
|
* @param itemName item name
|
|
|
|
* @return EML item name
|
|
|
|
*/
|
|
|
|
protected String convertItemNameToEML(String itemName) {
|
|
|
|
if (itemName.endsWith(".vcf")) {
|
|
|
|
return itemName.substring(0, itemName.length() - 3) + "EML";
|
|
|
|
} else {
|
|
|
|
return itemName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
2010-05-06 15:26:44 -04:00
|
|
|
* Get item named eventName in folder
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderPath Exchange folder path
|
2010-05-07 04:30:12 -04:00
|
|
|
* @param itemName event name
|
2009-08-13 04:34:51 -04:00
|
|
|
* @return event object
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-15 10:51:49 -04:00
|
|
|
public abstract Item getItem(String folderPath, String itemName) throws IOException;
|
2008-12-17 10:27:56 -05:00
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Contact picture
|
|
|
|
*/
|
|
|
|
public static class ContactPhoto {
|
|
|
|
/**
|
|
|
|
* Contact picture content type (always image/jpeg on read)
|
|
|
|
*/
|
|
|
|
public String contentType;
|
|
|
|
/**
|
|
|
|
* Base64 encoded picture content
|
|
|
|
*/
|
2010-07-06 19:40:48 -04:00
|
|
|
public String content;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve contact photo attached to contact
|
2010-07-07 08:55:13 -04:00
|
|
|
*
|
2010-07-06 19:40:48 -04:00
|
|
|
* @param contact address book contact
|
|
|
|
* @return contact photo
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public abstract ContactPhoto getContactPhoto(Contact contact) throws IOException;
|
|
|
|
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
2010-07-07 08:55:13 -04:00
|
|
|
* Delete event named itemName in folder
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderPath Exchange folder path
|
2010-07-07 08:55:13 -04:00
|
|
|
* @param itemName item name
|
2009-08-13 04:34:51 -04:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-20 05:10:39 -04:00
|
|
|
public abstract void deleteItem(String folderPath, String itemName) throws IOException;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2010-07-07 08:55:13 -04:00
|
|
|
/**
|
|
|
|
* Mark event processed named eventName in folder
|
|
|
|
*
|
|
|
|
* @param folderPath Exchange folder path
|
|
|
|
* @param itemName item name
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-07-20 05:10:39 -04:00
|
|
|
public abstract void processItem(String folderPath, String itemName) throws IOException;
|
2010-07-07 08:55:13 -04:00
|
|
|
|
|
|
|
|
2009-10-07 16:53:21 -04:00
|
|
|
private static int dumpIndex;
|
2009-09-02 06:33:54 -04:00
|
|
|
|
2009-09-14 17:16:15 -04:00
|
|
|
/**
|
|
|
|
* Replace iCal4 (Snow Leopard) principal paths with mailto expression
|
|
|
|
*
|
|
|
|
* @param value attendee value or ics line
|
|
|
|
* @return fixed value
|
|
|
|
*/
|
|
|
|
protected String replaceIcal4Principal(String value) {
|
|
|
|
if (value.contains("/principals/__uuids__/")) {
|
|
|
|
return value.replaceAll("/principals/__uuids__/([^/]*)__AT__([^/]*)/", "mailto:$1@$2");
|
|
|
|
} else {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Event result object to hold HTTP status and event etag from an event creation/update.
|
|
|
|
*/
|
2010-05-07 04:30:12 -04:00
|
|
|
public static class ItemResult {
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* HTTP status
|
|
|
|
*/
|
2009-02-11 20:33:49 -05:00
|
|
|
public int status;
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Event etag from response HTTP header
|
|
|
|
*/
|
2009-02-11 20:33:49 -05:00
|
|
|
public String etag;
|
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Build and send the MIME message for the provided ICS event.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param icsBody event in iCalendar format
|
|
|
|
* @return HTTP status
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-06-16 09:30:36 -04:00
|
|
|
public abstract int sendEvent(String icsBody) throws IOException;
|
2009-02-09 05:12:09 -05:00
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
2010-05-07 04:30:12 -04:00
|
|
|
* Create or update item (event or contact) on the Exchange server
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param folderPath Exchange folder path
|
2010-05-07 04:30:12 -04:00
|
|
|
* @param itemName event name
|
|
|
|
* @param itemBody event body in iCalendar format
|
2009-08-21 05:58:19 -04:00
|
|
|
* @param etag previous event etag to detect concurrent updates
|
|
|
|
* @param noneMatch if-none-match header value
|
2009-08-13 04:34:51 -04:00
|
|
|
* @return HTTP response event result (status and etag)
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-05-07 04:30:12 -04:00
|
|
|
public ItemResult createOrUpdateItem(String folderPath, String itemName, String itemBody, String etag, String noneMatch) throws IOException {
|
|
|
|
if (itemBody.startsWith("BEGIN:VCALENDAR")) {
|
2010-07-06 05:46:55 -04:00
|
|
|
return internalCreateOrUpdateEvent(folderPath, itemName, "urn:content-classes:appointment", itemBody, etag, noneMatch);
|
2010-05-07 04:30:12 -04:00
|
|
|
} else if (itemBody.startsWith("BEGIN:VCARD")) {
|
2010-07-06 05:46:55 -04:00
|
|
|
return createOrUpdateContact(folderPath, itemName, itemBody, etag, noneMatch);
|
2010-05-07 04:30:12 -04:00
|
|
|
} else {
|
|
|
|
throw new IOException(BundleMessage.format("EXCEPTION_INVALID_MESSAGE_CONTENT", itemBody));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-06 06:54:04 -04:00
|
|
|
protected static final String[] VCARD_N_PROPERTIES = {"sn", "givenName", "middlename", "personaltitle", "namesuffix"};
|
2010-07-06 12:26:55 -04:00
|
|
|
protected static final String[] VCARD_ADR_HOME_PROPERTIES = {"homepostofficebox", null, "homeStreet", "homeCity", "homeState", "homePostalCode", "homeCountry"};
|
2010-07-06 09:42:24 -04:00
|
|
|
protected static final String[] VCARD_ADR_WORK_PROPERTIES = {"postofficebox", "roomnumber", "street", "l", "st", "postalcode", "co"};
|
2010-07-06 12:26:55 -04:00
|
|
|
protected static final String[] VCARD_ADR_OTHER_PROPERTIES = {"otherpostofficebox", null, "otherstreet", "othercity", "otherstate", "otherpostalcode", "othercountry"};
|
2010-07-06 10:02:40 -04:00
|
|
|
protected static final String[] VCARD_ORG_PROPERTIES = {"o", "department"};
|
2010-07-06 09:42:24 -04:00
|
|
|
|
|
|
|
protected void convertContactProperties(Map<String, String> properties, String[] contactProperties, List<String> values) {
|
|
|
|
for (int i = 0; i < values.size() && i < contactProperties.length; i++) {
|
|
|
|
if (contactProperties[i] != null) {
|
|
|
|
properties.put(contactProperties[i], values.get(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-07-06 06:54:04 -04:00
|
|
|
|
2010-07-06 05:46:55 -04:00
|
|
|
protected ItemResult createOrUpdateContact(String folderPath, String itemName, String itemBody, String etag, String noneMatch) throws IOException {
|
|
|
|
// parse VCARD body to build contact property map
|
2010-07-13 03:57:25 -04:00
|
|
|
Map<String, String> properties = new HashMap<String, String>();
|
2010-07-06 05:46:55 -04:00
|
|
|
properties.put("outlookmessageclass", "IPM.Contact");
|
|
|
|
|
2010-07-29 12:17:46 -04:00
|
|
|
VObject vcard = new VObject(new ICSBufferedReader(new StringReader(itemBody)));
|
2010-07-31 18:52:45 -04:00
|
|
|
for (VProperty property : vcard.getProperties()) {
|
2010-07-06 05:46:55 -04:00
|
|
|
if ("FN".equals(property.getKey())) {
|
|
|
|
properties.put("cn", property.getValue());
|
|
|
|
properties.put("subject", property.getValue());
|
|
|
|
properties.put("fileas", property.getValue());
|
|
|
|
|
|
|
|
} else if ("N".equals(property.getKey())) {
|
2010-07-06 09:42:24 -04:00
|
|
|
convertContactProperties(properties, VCARD_N_PROPERTIES, property.getValues());
|
2010-07-06 06:54:04 -04:00
|
|
|
} else if ("NICKNAME".equals(property.getKey())) {
|
|
|
|
properties.put("nickname", property.getValue());
|
2010-07-06 05:46:55 -04:00
|
|
|
} else if ("TEL".equals(property.getKey())) {
|
2010-07-19 05:56:49 -04:00
|
|
|
if (property.hasParam("TYPE", "cell") || property.hasParam("X-GROUP", "cell")) {
|
2010-07-06 05:46:55 -04:00
|
|
|
properties.put("mobile", property.getValue());
|
2010-07-26 11:08:06 -04:00
|
|
|
} else if (property.hasParam("TYPE", "work") || property.hasParam("X-GROUP", "work")) {
|
2010-07-06 05:46:55 -04:00
|
|
|
properties.put("telephoneNumber", property.getValue());
|
2010-07-26 11:08:06 -04:00
|
|
|
} else if (property.hasParam("TYPE", "home") || property.hasParam("X-GROUP", "home")) {
|
2010-07-06 05:46:55 -04:00
|
|
|
properties.put("homePhone", property.getValue());
|
2010-07-26 11:08:06 -04:00
|
|
|
} else if (property.hasParam("TYPE", "fax")) {
|
|
|
|
if (property.hasParam("TYPE", "home")) {
|
|
|
|
properties.put("homefax", property.getValue());
|
|
|
|
} else {
|
|
|
|
properties.put("facsimiletelephonenumber", property.getValue());
|
|
|
|
}
|
|
|
|
} else if (property.hasParam("TYPE", "pager")) {
|
2010-07-06 09:42:24 -04:00
|
|
|
properties.put("pager", property.getValue());
|
2010-07-26 11:08:06 -04:00
|
|
|
} else if (property.hasParam("TYPE", "car")) {
|
2010-07-19 10:07:46 -04:00
|
|
|
properties.put("othermobile", property.getValue());
|
2010-07-26 11:08:06 -04:00
|
|
|
} else {
|
|
|
|
properties.put("otherTelephone", property.getValue());
|
2010-07-19 10:07:46 -04:00
|
|
|
}
|
2010-07-06 09:42:24 -04:00
|
|
|
} else if ("ADR".equals(property.getKey())) {
|
|
|
|
// address
|
|
|
|
if (property.hasParam("TYPE", "home")) {
|
|
|
|
convertContactProperties(properties, VCARD_ADR_HOME_PROPERTIES, property.getValues());
|
|
|
|
} else if (property.hasParam("TYPE", "work")) {
|
|
|
|
convertContactProperties(properties, VCARD_ADR_WORK_PROPERTIES, property.getValues());
|
2010-07-28 13:11:18 -04:00
|
|
|
// any other type goes to other address
|
2010-07-26 11:08:06 -04:00
|
|
|
} else {
|
2010-07-06 12:26:55 -04:00
|
|
|
convertContactProperties(properties, VCARD_ADR_OTHER_PROPERTIES, property.getValues());
|
2010-07-06 09:42:24 -04:00
|
|
|
}
|
|
|
|
} else if ("EMAIL".equals(property.getKey())) {
|
|
|
|
if (property.hasParam("TYPE", "home")) {
|
|
|
|
properties.put("email2", property.getValue());
|
2010-07-23 06:13:08 -04:00
|
|
|
properties.put("smtpemail2", property.getValue());
|
2010-07-23 11:56:37 -04:00
|
|
|
} else if (property.hasParam("TYPE", "other")) {
|
2010-07-06 09:42:24 -04:00
|
|
|
properties.put("email3", property.getValue());
|
2010-07-23 06:13:08 -04:00
|
|
|
properties.put("smtpemail3", property.getValue());
|
2010-07-23 11:56:37 -04:00
|
|
|
} else {
|
|
|
|
properties.put("email1", property.getValue());
|
|
|
|
properties.put("smtpemail1", property.getValue());
|
2010-07-06 09:42:24 -04:00
|
|
|
}
|
2010-07-06 10:02:40 -04:00
|
|
|
} else if ("ORG".equals(property.getKey())) {
|
|
|
|
convertContactProperties(properties, VCARD_ORG_PROPERTIES, property.getValues());
|
|
|
|
} else if ("URL".equals(property.getKey())) {
|
|
|
|
if (property.hasParam("TYPE", "work")) {
|
|
|
|
properties.put("businesshomepage", property.getValue());
|
2010-07-19 10:07:46 -04:00
|
|
|
} else if (property.hasParam("TYPE", "home")) {
|
|
|
|
properties.put("personalHomePage", property.getValue());
|
2010-07-20 05:36:21 -04:00
|
|
|
} else {
|
|
|
|
// default: set personal home page
|
|
|
|
properties.put("personalHomePage", property.getValue());
|
2010-07-06 10:02:40 -04:00
|
|
|
}
|
|
|
|
} else if ("TITLE".equals(property.getKey())) {
|
|
|
|
properties.put("title", property.getValue());
|
|
|
|
} else if ("NOTE".equals(property.getKey())) {
|
|
|
|
properties.put("description", property.getValue());
|
|
|
|
} else if ("CUSTOM1".equals(property.getKey())) {
|
|
|
|
properties.put("extensionattribute1", property.getValue());
|
|
|
|
} else if ("CUSTOM2".equals(property.getKey())) {
|
|
|
|
properties.put("extensionattribute2", property.getValue());
|
|
|
|
} else if ("CUSTOM3".equals(property.getKey())) {
|
|
|
|
properties.put("extensionattribute3", property.getValue());
|
|
|
|
} else if ("CUSTOM4".equals(property.getKey())) {
|
|
|
|
properties.put("extensionattribute4", property.getValue());
|
2010-07-06 11:52:38 -04:00
|
|
|
} else if ("ROLE".equals(property.getKey())) {
|
|
|
|
properties.put("profession", property.getValue());
|
|
|
|
} else if ("X-AIM".equals(property.getKey())) {
|
|
|
|
properties.put("im", property.getValue());
|
|
|
|
} else if ("BDAY".equals(property.getKey())) {
|
2010-07-20 12:33:58 -04:00
|
|
|
properties.put("bday", convertBDayToZulu(property.getValue()));
|
2010-07-21 05:20:22 -04:00
|
|
|
} else if ("ANNIVERSARY".equals(property.getKey()) || "X-ANNIVERSARY".equals(property.getKey())) {
|
2010-07-20 16:18:01 -04:00
|
|
|
properties.put("anniversary", convertBDayToZulu(property.getValue()));
|
2010-07-13 05:56:48 -04:00
|
|
|
} else if ("CATEGORIES".equals(property.getKey())) {
|
|
|
|
properties.put("keywords", property.getValue());
|
2010-07-18 08:26:12 -04:00
|
|
|
} else if ("CLASS".equals(property.getKey())) {
|
|
|
|
if ("PUBLIC".equals(property.getValue())) {
|
|
|
|
properties.put("sensitivity", "0");
|
2010-07-18 17:33:22 -04:00
|
|
|
properties.put("private", "false");
|
2010-07-18 08:26:12 -04:00
|
|
|
} else {
|
|
|
|
properties.put("sensitivity", "2");
|
2010-07-18 17:33:22 -04:00
|
|
|
properties.put("private", "true");
|
2010-07-18 08:26:12 -04:00
|
|
|
}
|
2010-07-20 16:18:01 -04:00
|
|
|
} else if ("SEX".equals(property.getKey())) {
|
|
|
|
String propertyValue = property.getValue();
|
|
|
|
if ("1".equals(propertyValue)) {
|
|
|
|
properties.put("gender", "2");
|
|
|
|
} else if ("2".equals(propertyValue)) {
|
|
|
|
properties.put("gender", "1");
|
|
|
|
}
|
2010-07-26 09:22:28 -04:00
|
|
|
} else if ("FBURL".equals(property.getKey())) {
|
|
|
|
properties.put("fburl", property.getValue());
|
2010-07-06 12:49:57 -04:00
|
|
|
} else if ("X-ASSISTANT".equals(property.getKey())) {
|
|
|
|
properties.put("secretarycn", property.getValue());
|
|
|
|
} else if ("X-MANAGER".equals(property.getKey())) {
|
|
|
|
properties.put("manager", property.getValue());
|
|
|
|
} else if ("X-SPOUSE".equals(property.getKey())) {
|
|
|
|
properties.put("spousecn", property.getValue());
|
2010-07-17 06:45:21 -04:00
|
|
|
} else if ("PHOTO".equals(property.getKey())) {
|
|
|
|
properties.put("photo", property.getValue());
|
2010-07-18 18:54:54 -04:00
|
|
|
properties.put("haspicture", "true");
|
2010-07-06 09:42:24 -04:00
|
|
|
}
|
2010-07-06 05:46:55 -04:00
|
|
|
}
|
2010-07-20 10:09:40 -04:00
|
|
|
LOGGER.debug("Create or update contact " + itemName + ": " + properties);
|
2010-07-18 15:59:13 -04:00
|
|
|
// reset missing properties to null
|
|
|
|
for (String key : CONTACT_ATTRIBUTES) {
|
2010-07-18 18:54:54 -04:00
|
|
|
if (!"imapUid".equals(key) && !"etag".equals(key) && !"urlcompname".equals(key)
|
2010-07-24 09:11:22 -04:00
|
|
|
&& !"lastmodified".equals(key) && !"sensitivity".equals(key) &&
|
2010-07-18 15:59:13 -04:00
|
|
|
!properties.containsKey(key)) {
|
2010-07-18 18:54:54 -04:00
|
|
|
properties.put(key, null);
|
2010-07-13 05:56:48 -04:00
|
|
|
}
|
|
|
|
}
|
2010-07-06 05:46:55 -04:00
|
|
|
return internalCreateOrUpdateContact(folderPath, itemName, properties, etag, noneMatch);
|
|
|
|
}
|
|
|
|
|
2010-07-20 12:33:58 -04:00
|
|
|
protected String convertBDayToZulu(String value) {
|
|
|
|
String result = null;
|
|
|
|
if (value != null && value.length() > 0) {
|
|
|
|
try {
|
|
|
|
SimpleDateFormat parser;
|
|
|
|
if (value.length() == 10) {
|
|
|
|
parser = ExchangeSession.getVcardBdayFormat();
|
2010-07-20 13:19:01 -04:00
|
|
|
} else if (value.length() == 15) {
|
|
|
|
parser = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH);
|
|
|
|
parser.setTimeZone(GMT_TIMEZONE);
|
2010-07-20 12:33:58 -04:00
|
|
|
} else {
|
|
|
|
parser = ExchangeSession.getExchangeZuluDateFormat();
|
|
|
|
}
|
|
|
|
result = ExchangeSession.getExchangeZuluDateFormatMillisecond().format(parser.parse(value));
|
|
|
|
} catch (ParseException e) {
|
|
|
|
LOGGER.warn("Invalid date: " + value);
|
|
|
|
}
|
|
|
|
}
|
2010-07-20 16:18:01 -04:00
|
|
|
|
2010-07-20 12:33:58 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-07-06 05:46:55 -04:00
|
|
|
|
|
|
|
protected abstract ItemResult internalCreateOrUpdateContact(String folderPath, String itemName, Map<String, String> properties, String etag, String noneMatch) throws IOException;
|
2009-02-09 05:12:09 -05:00
|
|
|
|
2010-07-06 05:46:55 -04:00
|
|
|
protected abstract ItemResult internalCreateOrUpdateEvent(String folderPath, String itemName, String contentClass, String icsBody, String etag, String noneMatch) throws IOException;
|
2008-11-26 19:56:28 -05:00
|
|
|
|
|
|
|
/**
|
2009-01-08 04:58:31 -05:00
|
|
|
* Get current Exchange alias name from login name
|
|
|
|
*
|
|
|
|
* @return user name
|
|
|
|
*/
|
2009-02-24 06:53:02 -05:00
|
|
|
protected String getAliasFromLogin() {
|
2009-01-08 04:58:31 -05:00
|
|
|
// Exchange 2007 : userName is login without domain
|
2009-09-30 17:54:53 -04:00
|
|
|
String result = this.userName;
|
|
|
|
int index = result.indexOf('\\');
|
2009-01-08 04:58:31 -05:00
|
|
|
if (index >= 0) {
|
2009-09-30 17:54:53 -04:00
|
|
|
result = result.substring(index + 1);
|
2009-01-08 04:58:31 -05:00
|
|
|
}
|
2009-09-30 17:54:53 -04:00
|
|
|
return result;
|
2009-01-08 04:58:31 -05:00
|
|
|
}
|
|
|
|
|
2010-03-29 04:50:39 -04:00
|
|
|
/**
|
|
|
|
* Test if folderPath is inside user mailbox.
|
|
|
|
*
|
|
|
|
* @param folderPath absolute folder path
|
|
|
|
* @return true if folderPath is a public or shared folder
|
|
|
|
*/
|
2010-06-21 09:52:09 -04:00
|
|
|
public abstract boolean isSharedFolder(String folderPath);
|
2010-03-29 04:50:39 -04:00
|
|
|
|
2009-04-23 17:34:04 -04:00
|
|
|
/**
|
|
|
|
* Build base path for cmd commands (galfind, gallookup).
|
|
|
|
* This does not work with freebusy, which requires /public/
|
2009-04-28 17:01:40 -04:00
|
|
|
*
|
2009-04-23 17:34:04 -04:00
|
|
|
* @return cmd base path
|
|
|
|
*/
|
|
|
|
public String getCmdBasePath() {
|
|
|
|
if (mailPath == null) {
|
2009-12-09 16:13:43 -05:00
|
|
|
if (publicFolderUrl == null) {
|
|
|
|
return "/public/";
|
|
|
|
} else {
|
|
|
|
return publicFolderUrl + '/';
|
|
|
|
}
|
2009-04-23 17:34:04 -04:00
|
|
|
} else {
|
|
|
|
return mailPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-30 18:13:39 -04:00
|
|
|
static final String MAILBOX_BASE = "/cn=";
|
2009-09-21 17:34:13 -04:00
|
|
|
|
2010-04-20 11:01:22 -04:00
|
|
|
protected String getAliasFromOptions() {
|
2009-03-09 11:21:03 -04:00
|
|
|
String result = null;
|
|
|
|
// get user mail URL from html body
|
|
|
|
BufferedReader optionsPageReader = null;
|
2010-04-20 11:01:22 -04:00
|
|
|
GetMethod optionsMethod = new GetMethod("/owa/?ae=Options&t=About");
|
2009-03-09 11:21:03 -04:00
|
|
|
try {
|
2009-10-16 04:58:04 -04:00
|
|
|
DavGatewayHttpClientFacade.executeGetMethod(httpClient, optionsMethod, false);
|
2009-03-09 11:21:03 -04:00
|
|
|
optionsPageReader = new BufferedReader(new InputStreamReader(optionsMethod.getResponseBodyAsStream()));
|
|
|
|
String line;
|
|
|
|
// find mailbox full name
|
|
|
|
//noinspection StatementWithEmptyBody
|
|
|
|
while ((line = optionsPageReader.readLine()) != null && line.toLowerCase().indexOf(MAILBOX_BASE) == -1) {
|
|
|
|
}
|
|
|
|
if (line != null) {
|
2010-03-30 18:13:39 -04:00
|
|
|
int start = line.toLowerCase().lastIndexOf(MAILBOX_BASE) + MAILBOX_BASE.length();
|
2009-04-16 17:16:40 -04:00
|
|
|
int end = line.indexOf('<', start);
|
2009-03-09 11:21:03 -04:00
|
|
|
result = line.substring(start, end);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing options page at " + optionsMethod.getPath());
|
|
|
|
} finally {
|
|
|
|
if (optionsPageReader != null) {
|
|
|
|
try {
|
|
|
|
optionsPageReader.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing options page at " + optionsMethod.getPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
optionsMethod.releaseConnection();
|
2009-01-08 04:58:31 -05:00
|
|
|
}
|
2009-03-09 11:21:03 -04:00
|
|
|
|
|
|
|
return result;
|
2008-12-25 17:16:24 -05:00
|
|
|
}
|
|
|
|
|
2010-04-20 11:01:22 -04:00
|
|
|
protected String getEmailFromOptions() {
|
2009-05-19 06:42:56 -04:00
|
|
|
String result = null;
|
|
|
|
// get user mail URL from html body
|
|
|
|
BufferedReader optionsPageReader = null;
|
2010-04-20 11:01:22 -04:00
|
|
|
GetMethod optionsMethod = new GetMethod("/owa/?ae=Options&t=About");
|
2009-05-19 06:42:56 -04:00
|
|
|
try {
|
2009-10-16 04:58:04 -04:00
|
|
|
DavGatewayHttpClientFacade.executeGetMethod(httpClient, optionsMethod, false);
|
2009-05-19 06:42:56 -04:00
|
|
|
optionsPageReader = new BufferedReader(new InputStreamReader(optionsMethod.getResponseBodyAsStream()));
|
|
|
|
String line;
|
|
|
|
// find email
|
|
|
|
//noinspection StatementWithEmptyBody
|
|
|
|
while ((line = optionsPageReader.readLine()) != null
|
|
|
|
&& (line.indexOf('[') == -1
|
|
|
|
|| line.indexOf('@') == -1
|
|
|
|
|| line.indexOf(']') == -1)) {
|
|
|
|
}
|
|
|
|
if (line != null) {
|
2010-08-17 09:39:14 -04:00
|
|
|
int start = line.indexOf('[') + 1;
|
2009-05-19 06:42:56 -04:00
|
|
|
int end = line.indexOf(']', start);
|
|
|
|
result = line.substring(start, end);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing options page at " + optionsMethod.getPath());
|
|
|
|
} finally {
|
|
|
|
if (optionsPageReader != null) {
|
|
|
|
try {
|
|
|
|
optionsPageReader.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOGGER.error("Error parsing options page at " + optionsMethod.getPath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
optionsMethod.releaseConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-12-25 17:16:24 -05:00
|
|
|
/**
|
|
|
|
* Get current user email
|
|
|
|
*
|
|
|
|
* @return user email
|
|
|
|
*/
|
2009-02-24 06:53:02 -05:00
|
|
|
public String getEmail() {
|
2008-11-26 19:56:28 -05:00
|
|
|
return email;
|
|
|
|
}
|
|
|
|
|
2009-03-18 13:26:33 -04:00
|
|
|
/**
|
2009-03-13 07:11:40 -04:00
|
|
|
* Get current user alias
|
|
|
|
*
|
|
|
|
* @return user email
|
|
|
|
*/
|
|
|
|
public String getAlias() {
|
|
|
|
return alias;
|
|
|
|
}
|
|
|
|
|
2008-12-03 06:38:35 -05:00
|
|
|
/**
|
2010-08-16 17:50:18 -04:00
|
|
|
* Search global address list
|
2008-12-03 06:38:35 -05:00
|
|
|
*
|
2010-08-16 17:50:18 -04:00
|
|
|
* @param condition search filter
|
|
|
|
* @param returningAttributes returning attributes
|
|
|
|
* @param sizeLimit size limit
|
|
|
|
* @return matching contacts from gal
|
2009-04-16 18:20:30 -04:00
|
|
|
* @throws IOException on error
|
2008-12-03 06:38:35 -05:00
|
|
|
*/
|
2010-08-09 18:19:15 -04:00
|
|
|
public abstract Map<String, Contact> galFind(Condition condition, Set<String> returningAttributes, int sizeLimit) throws IOException;
|
2010-08-09 16:15:28 -04:00
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Full Contact attribute list
|
|
|
|
*/
|
2010-07-01 08:47:57 -04:00
|
|
|
public static final Set<String> CONTACT_ATTRIBUTES = new HashSet<String>();
|
2010-06-18 17:12:46 -04:00
|
|
|
|
|
|
|
static {
|
2010-07-06 05:46:55 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("imapUid");
|
2010-07-06 19:40:48 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("etag");
|
2010-07-08 19:26:41 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("urlcompname");
|
2010-07-06 19:40:48 -04:00
|
|
|
|
2010-06-18 17:12:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("extensionattribute1");
|
|
|
|
CONTACT_ATTRIBUTES.add("extensionattribute2");
|
|
|
|
CONTACT_ATTRIBUTES.add("extensionattribute3");
|
|
|
|
CONTACT_ATTRIBUTES.add("extensionattribute4");
|
|
|
|
CONTACT_ATTRIBUTES.add("bday");
|
2010-07-20 12:33:58 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("anniversary");
|
2010-06-18 17:12:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("businesshomepage");
|
2010-07-20 05:36:21 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("personalHomePage");
|
2010-06-18 17:12:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("cn");
|
|
|
|
CONTACT_ATTRIBUTES.add("co");
|
|
|
|
CONTACT_ATTRIBUTES.add("department");
|
2010-07-23 06:13:08 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("smtpemail1");
|
|
|
|
CONTACT_ATTRIBUTES.add("smtpemail2");
|
|
|
|
CONTACT_ATTRIBUTES.add("smtpemail3");
|
2010-06-18 17:12:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("facsimiletelephonenumber");
|
|
|
|
CONTACT_ATTRIBUTES.add("givenName");
|
|
|
|
CONTACT_ATTRIBUTES.add("homeCity");
|
|
|
|
CONTACT_ATTRIBUTES.add("homeCountry");
|
|
|
|
CONTACT_ATTRIBUTES.add("homePhone");
|
|
|
|
CONTACT_ATTRIBUTES.add("homePostalCode");
|
|
|
|
CONTACT_ATTRIBUTES.add("homeState");
|
|
|
|
CONTACT_ATTRIBUTES.add("homeStreet");
|
2010-07-06 12:26:55 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("homepostofficebox");
|
2010-06-18 17:12:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("l");
|
|
|
|
CONTACT_ATTRIBUTES.add("manager");
|
|
|
|
CONTACT_ATTRIBUTES.add("mobile");
|
|
|
|
CONTACT_ATTRIBUTES.add("namesuffix");
|
|
|
|
CONTACT_ATTRIBUTES.add("nickname");
|
|
|
|
CONTACT_ATTRIBUTES.add("o");
|
|
|
|
CONTACT_ATTRIBUTES.add("pager");
|
|
|
|
CONTACT_ATTRIBUTES.add("personaltitle");
|
|
|
|
CONTACT_ATTRIBUTES.add("postalcode");
|
|
|
|
CONTACT_ATTRIBUTES.add("postofficebox");
|
|
|
|
CONTACT_ATTRIBUTES.add("profession");
|
|
|
|
CONTACT_ATTRIBUTES.add("roomnumber");
|
|
|
|
CONTACT_ATTRIBUTES.add("secretarycn");
|
|
|
|
CONTACT_ATTRIBUTES.add("sn");
|
|
|
|
CONTACT_ATTRIBUTES.add("spousecn");
|
|
|
|
CONTACT_ATTRIBUTES.add("st");
|
|
|
|
CONTACT_ATTRIBUTES.add("street");
|
|
|
|
CONTACT_ATTRIBUTES.add("telephoneNumber");
|
|
|
|
CONTACT_ATTRIBUTES.add("title");
|
2010-07-06 05:46:55 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("description");
|
2010-06-22 04:37:34 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("im");
|
2010-06-30 17:38:01 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("middlename");
|
|
|
|
CONTACT_ATTRIBUTES.add("lastmodified");
|
2010-07-06 12:26:55 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("otherstreet");
|
|
|
|
CONTACT_ATTRIBUTES.add("otherstate");
|
|
|
|
CONTACT_ATTRIBUTES.add("otherpostofficebox");
|
|
|
|
CONTACT_ATTRIBUTES.add("otherpostalcode");
|
|
|
|
CONTACT_ATTRIBUTES.add("othercountry");
|
|
|
|
CONTACT_ATTRIBUTES.add("othercity");
|
2010-07-06 19:40:48 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("haspicture");
|
2010-07-13 05:56:48 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("keywords");
|
2010-07-19 10:07:46 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("othermobile");
|
2010-07-28 13:11:18 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("otherTelephone");
|
2010-07-20 16:18:01 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("gender");
|
2010-07-18 08:26:12 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("private");
|
|
|
|
CONTACT_ATTRIBUTES.add("sensitivity");
|
2010-07-28 13:11:18 -04:00
|
|
|
CONTACT_ATTRIBUTES.add("fburl");
|
2010-06-18 17:12:46 -04:00
|
|
|
}
|
|
|
|
|
2010-07-28 13:11:18 -04:00
|
|
|
/**
|
|
|
|
* Get freebusy data string from Exchange.
|
|
|
|
*
|
|
|
|
* @param attendee attendee email address
|
|
|
|
* @param start start date in Exchange zulu format
|
|
|
|
* @param end end date in Exchange zulu format
|
|
|
|
* @param interval freebusy interval in minutes
|
|
|
|
* @return freebusy data or null
|
2010-07-29 12:17:46 -04:00
|
|
|
* @throws IOException on error
|
2010-07-28 13:11:18 -04:00
|
|
|
*/
|
|
|
|
protected abstract String getFreeBusyData(String attendee, String start, String end, int interval) throws IOException;
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Get freebusy info for attendee between start and end date.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
|
|
|
* @param attendee attendee email
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param startDateValue start date
|
2009-08-21 05:58:19 -04:00
|
|
|
* @param endDateValue end date
|
2009-08-13 04:34:51 -04:00
|
|
|
* @return FreeBusy info
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
public FreeBusy getFreebusy(String attendee, String startDateValue, String endDateValue) throws IOException {
|
2009-09-14 17:16:15 -04:00
|
|
|
attendee = replaceIcal4Principal(attendee);
|
2009-11-16 17:49:52 -05:00
|
|
|
if (attendee.startsWith("mailto:") || attendee.startsWith("MAILTO:")) {
|
2008-11-26 19:56:28 -05:00
|
|
|
attendee = attendee.substring("mailto:".length());
|
|
|
|
}
|
|
|
|
|
2009-04-11 08:27:10 -04:00
|
|
|
SimpleDateFormat exchangeZuluDateFormat = getExchangeZuluDateFormat();
|
2009-04-07 08:37:28 -04:00
|
|
|
SimpleDateFormat icalDateFormat = getZuluDateFormat();
|
2008-11-26 19:56:28 -05:00
|
|
|
|
2008-12-01 12:56:18 -05:00
|
|
|
Date startDate;
|
|
|
|
Date endDate;
|
2008-11-26 19:56:28 -05:00
|
|
|
try {
|
|
|
|
if (startDateValue.length() == 8) {
|
2009-04-07 08:37:28 -04:00
|
|
|
startDate = parseDate(startDateValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
} else {
|
2009-04-07 08:37:28 -04:00
|
|
|
startDate = icalDateFormat.parse(startDateValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
if (endDateValue.length() == 8) {
|
2009-04-07 08:37:28 -04:00
|
|
|
endDate = parseDate(endDateValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
} else {
|
2009-04-07 08:37:28 -04:00
|
|
|
endDate = icalDateFormat.parse(endDateValue);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
} catch (ParseException e) {
|
2009-04-27 19:03:58 -04:00
|
|
|
throw new DavMailException("EXCEPTION_INVALID_DATES", e.getMessage());
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-03-20 12:05:45 -04:00
|
|
|
FreeBusy freeBusy = null;
|
2010-07-31 18:52:45 -04:00
|
|
|
String fbdata = getFreeBusyData(attendee, exchangeZuluDateFormat.format(startDate), exchangeZuluDateFormat.format(endDate), FREE_BUSY_INTERVAL);
|
2010-07-28 13:11:18 -04:00
|
|
|
if (fbdata != null) {
|
|
|
|
freeBusy = new FreeBusy(icalDateFormat, startDate, fbdata);
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
|
|
|
|
2009-06-10 12:46:58 -04:00
|
|
|
if (freeBusy != null && freeBusy.knownAttendee) {
|
2009-07-08 18:51:34 -04:00
|
|
|
return freeBusy;
|
2009-06-10 12:46:58 -04:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2009-03-20 12:05:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchange to iCalendar Free/Busy parser.
|
|
|
|
* Free time returns 0, Tentative returns 1, Busy returns 2, and Out of Office (OOF) returns 3
|
|
|
|
*/
|
|
|
|
public static final class FreeBusy {
|
2009-03-25 17:59:27 -04:00
|
|
|
final SimpleDateFormat icalParser;
|
2009-03-20 12:05:45 -04:00
|
|
|
boolean knownAttendee = true;
|
|
|
|
static final HashMap<Character, String> FBTYPES = new HashMap<Character, String>();
|
|
|
|
|
|
|
|
static {
|
|
|
|
FBTYPES.put('1', "BUSY-TENTATIVE");
|
|
|
|
FBTYPES.put('2', "BUSY");
|
|
|
|
FBTYPES.put('3', "BUSY-UNAVAILABLE");
|
|
|
|
}
|
|
|
|
|
2009-03-25 17:59:27 -04:00
|
|
|
final HashMap<String, StringBuilder> busyMap = new HashMap<String, StringBuilder>();
|
2009-03-20 12:05:45 -04:00
|
|
|
|
2009-04-23 18:10:10 -04:00
|
|
|
StringBuilder getBusyBuffer(char type) {
|
2009-03-20 12:05:45 -04:00
|
|
|
String fbType = FBTYPES.get(Character.valueOf(type));
|
|
|
|
StringBuilder buffer = busyMap.get(fbType);
|
|
|
|
if (buffer == null) {
|
|
|
|
buffer = new StringBuilder();
|
|
|
|
busyMap.put(fbType, buffer);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2009-04-23 18:10:10 -04:00
|
|
|
void startBusy(char type, Calendar currentCal) {
|
2009-03-20 12:05:45 -04:00
|
|
|
if (type == '4') {
|
|
|
|
knownAttendee = false;
|
|
|
|
} else if (type != '0') {
|
|
|
|
StringBuilder busyBuffer = getBusyBuffer(type);
|
|
|
|
if (busyBuffer.length() > 0) {
|
|
|
|
busyBuffer.append(',');
|
|
|
|
}
|
|
|
|
busyBuffer.append(icalParser.format(currentCal.getTime()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-23 18:10:10 -04:00
|
|
|
void endBusy(char type, Calendar currentCal) {
|
2009-03-20 12:05:45 -04:00
|
|
|
if (type != '0' && type != '4') {
|
|
|
|
getBusyBuffer(type).append('/').append(icalParser.format(currentCal.getTime()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-23 18:10:10 -04:00
|
|
|
FreeBusy(SimpleDateFormat icalParser, Date startDate, String fbdata) {
|
2009-03-20 12:05:45 -04:00
|
|
|
this.icalParser = icalParser;
|
|
|
|
if (fbdata.length() > 0) {
|
|
|
|
Calendar currentCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
|
|
|
currentCal.setTime(startDate);
|
|
|
|
|
|
|
|
startBusy(fbdata.charAt(0), currentCal);
|
|
|
|
for (int i = 1; i < fbdata.length() && knownAttendee; i++) {
|
|
|
|
currentCal.add(Calendar.MINUTE, FREE_BUSY_INTERVAL);
|
|
|
|
char previousState = fbdata.charAt(i - 1);
|
|
|
|
char currentState = fbdata.charAt(i);
|
|
|
|
if (previousState != currentState) {
|
|
|
|
endBusy(previousState, currentCal);
|
|
|
|
startBusy(currentState, currentCal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentCal.add(Calendar.MINUTE, FREE_BUSY_INTERVAL);
|
|
|
|
endBusy(fbdata.charAt(fbdata.length() - 1), currentCal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-13 04:34:51 -04:00
|
|
|
/**
|
|
|
|
* Append freebusy information to buffer.
|
2009-08-21 05:58:19 -04:00
|
|
|
*
|
2009-08-13 04:34:51 -04:00
|
|
|
* @param buffer String buffer
|
|
|
|
*/
|
2009-03-20 12:05:45 -04:00
|
|
|
public void appendTo(StringBuilder buffer) {
|
|
|
|
for (Map.Entry<String, StringBuilder> entry : busyMap.entrySet()) {
|
|
|
|
buffer.append("FREEBUSY;FBTYPE=").append(entry.getKey())
|
2009-04-23 16:53:22 -04:00
|
|
|
.append(':').append(entry.getValue()).append((char) 13).append((char) 10);
|
2009-03-20 12:05:45 -04:00
|
|
|
}
|
|
|
|
}
|
2008-11-26 19:56:28 -05:00
|
|
|
}
|
2009-08-21 05:58:19 -04:00
|
|
|
|
2010-07-30 11:09:44 -04:00
|
|
|
protected VObject vTimezone;
|
2009-11-02 17:56:08 -05:00
|
|
|
|
2010-07-18 15:59:13 -04:00
|
|
|
/**
|
|
|
|
* Load and return current user OWA timezone.
|
|
|
|
*
|
|
|
|
* @return current timezone
|
|
|
|
*/
|
2010-07-30 11:09:44 -04:00
|
|
|
public VObject getVTimezone() {
|
2009-11-02 17:56:08 -05:00
|
|
|
if (vTimezone == null) {
|
|
|
|
// need to load Timezone info from OWA
|
2010-06-16 17:43:11 -04:00
|
|
|
loadVtimezone();
|
2009-11-02 17:56:08 -05:00
|
|
|
}
|
|
|
|
return vTimezone;
|
|
|
|
}
|
|
|
|
|
2010-06-16 17:43:11 -04:00
|
|
|
protected abstract void loadVtimezone();
|
|
|
|
|
2009-08-21 05:58:19 -04:00
|
|
|
/**
|
|
|
|
* Return internal HttpClient instance
|
2009-08-29 16:20:44 -04:00
|
|
|
*
|
2009-08-21 05:58:19 -04:00
|
|
|
* @return http client
|
|
|
|
*/
|
|
|
|
public HttpClient getHttpClient() {
|
|
|
|
return httpClient;
|
|
|
|
}
|
2006-12-12 18:57:24 -05:00
|
|
|
}
|