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 ;
2009-11-27 18:27:50 -05:00
import davmail.exception.HttpNotFoundException ;
2009-12-07 05:44:17 -05:00
import davmail.exception.HttpServerErrorException ;
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 ;
2009-10-06 17:12:26 -04:00
import org.apache.commons.codec.DecoderException ;
import org.apache.commons.codec.binary.Base64 ;
import org.apache.commons.codec.binary.Hex ;
2008-11-30 13:05:36 -05:00
import org.apache.commons.httpclient.* ;
2009-04-01 19:23:14 -04:00
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity ;
import org.apache.commons.httpclient.methods.GetMethod ;
import org.apache.commons.httpclient.methods.PostMethod ;
import org.apache.commons.httpclient.methods.PutMethod ;
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 ;
2009-11-27 18:27:50 -05:00
import org.apache.jackrabbit.webdav.DavException ;
import org.apache.jackrabbit.webdav.MultiStatus ;
2009-04-01 11:51:12 -04:00
import org.apache.jackrabbit.webdav.MultiStatusResponse ;
import org.apache.jackrabbit.webdav.client.methods.CopyMethod ;
import org.apache.jackrabbit.webdav.client.methods.MoveMethod ;
2009-11-27 18:27:50 -05:00
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod ;
2009-04-01 11:51:12 -04:00
import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod ;
2009-04-01 17:31:44 -04:00
import org.apache.jackrabbit.webdav.property.* ;
2009-04-01 11:51:12 -04:00
import org.apache.jackrabbit.webdav.xml.Namespace ;
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
2009-02-25 05:23:07 -05:00
import javax.mail.MessagingException ;
import javax.mail.internet.MimeMessage ;
import javax.mail.internet.MimeMultipart ;
2009-03-04 01:53:24 -05:00
import javax.mail.internet.MimePart ;
2010-04-23 09:42:37 -04:00
import javax.mail.util.SharedByteArrayInputStream ;
2008-11-26 19:56:28 -05:00
import java.io.* ;
2007-05-09 18:32:01 -04:00
import java.net.HttpURLConnection ;
2009-10-06 17:12:26 -04:00
import java.net.NoRouteToHostException ;
2006-12-12 18:57:24 -05:00
import java.net.URL ;
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.* ;
2010-04-23 06:17:20 -04:00
import java.util.zip.GZIPInputStream ;
2006-12-12 18:57:24 -05:00
/ * *
* Exchange session through Outlook Web Access ( DAV )
2009-09-28 19:28:23 -04:00
* /
2006-12-12 18:57:24 -05:00
public class ExchangeSession {
2007-05-09 18:32:01 -04:00
protected static final Logger LOGGER = Logger . getLogger ( " davmail.exchange.ExchangeSession " ) ;
2006-12-12 18:57:24 -05:00
2009-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-05-07 04:30:12 -04:00
protected static final Namespace DAV = Namespace . getNamespace ( " DAV: " ) ;
2009-04-01 17:31:44 -04:00
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace . getNamespace ( " urn:schemas:httpmail: " ) ;
2009-11-12 16:32:20 -05:00
protected static final Namespace SCHEMAS_EXCHANGE = Namespace . getNamespace ( " http://schemas.microsoft.com/exchange/ " ) ;
2009-04-01 17:31:44 -04:00
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace . getNamespace ( " http://schemas.microsoft.com/mapi/proptag/ " ) ;
2010-05-06 15:26:44 -04:00
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace . getNamespace ( " urn:schemas:contacts: " ) ;
2009-04-01 17:31:44 -04:00
2009-04-01 11:51:12 -04:00
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES = new DavPropertyNameSet ( ) ;
2008-12-09 08:24:17 -05:00
static {
2009-11-13 16:41:43 -05:00
EVENT_REQUEST_PROPERTIES . add ( DavPropertyName . create ( " permanenturl " , SCHEMAS_EXCHANGE ) ) ;
2009-04-01 11:51:12 -04:00
EVENT_REQUEST_PROPERTIES . add ( DavPropertyName . GETETAG ) ;
2010-05-07 04:30:12 -04:00
EVENT_REQUEST_PROPERTIES . add ( DavPropertyName . create ( " contentclass " , DAV ) ) ;
2008-12-09 08:24:17 -05:00
}
2009-04-01 11:51:12 -04:00
protected static final DavPropertyNameSet WELL_KNOWN_FOLDERS = new DavPropertyNameSet ( ) ;
2008-12-09 03:54:08 -05:00
static {
2009-04-01 17:31:44 -04:00
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " inbox " , URN_SCHEMAS_HTTPMAIL ) ) ;
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " deleteditems " , URN_SCHEMAS_HTTPMAIL ) ) ;
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " sentitems " , URN_SCHEMAS_HTTPMAIL ) ) ;
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " sendmsg " , URN_SCHEMAS_HTTPMAIL ) ) ;
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " drafts " , URN_SCHEMAS_HTTPMAIL ) ) ;
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " calendar " , URN_SCHEMAS_HTTPMAIL ) ) ;
2009-08-29 19:49:45 -04:00
WELL_KNOWN_FOLDERS . add ( DavPropertyName . create ( " contacts " , URN_SCHEMAS_HTTPMAIL ) ) ;
2008-12-09 03:54:08 -05:00
}
2009-04-01 11:51:12 -04:00
protected static final DavPropertyNameSet DISPLAY_NAME = new DavPropertyNameSet ( ) ;
2009-03-11 08:39:14 -04:00
static {
2009-04-01 11:51:12 -04:00
DISPLAY_NAME . add ( DavPropertyName . DISPLAYNAME ) ;
2009-03-11 08:39:14 -04:00
}
2009-04-01 11:51:12 -04:00
protected static final DavPropertyNameSet FOLDER_PROPERTIES = new DavPropertyNameSet ( ) ;
2009-03-18 13:26:33 -04:00
static {
2010-05-07 04:30:12 -04:00
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " contentclass " , DAV ) ) ;
2009-04-01 11:51:12 -04:00
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " hassubs " ) ) ;
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " nosubs " ) ) ;
2009-04-01 17:31:44 -04:00
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " unreadcount " , URN_SCHEMAS_HTTPMAIL ) ) ;
2009-04-01 11:51:12 -04:00
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " contenttag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ) ;
2010-04-01 05:12:44 -04:00
FOLDER_PROPERTIES . add ( DavPropertyName . create ( " resourcetag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ) ;
2009-03-18 13:26:33 -04:00
}
2009-04-01 11:51:12 -04:00
protected static final DavPropertyNameSet CONTENT_TAG = new DavPropertyNameSet ( ) ;
2009-03-18 08:09:03 -04:00
static {
2009-04-01 11:51:12 -04:00
CONTENT_TAG . add ( DavPropertyName . create ( " contenttag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ) ;
2009-03-18 08:09:03 -04:00
}
2009-04-02 18:10:07 -04:00
protected static final DavPropertyNameSet RESOURCE_TAG = new DavPropertyNameSet ( ) ;
static {
RESOURCE_TAG . add ( DavPropertyName . create ( " resourcetag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ) ;
}
2009-03-11 08:39:14 -04:00
2009-08-29 16:20:44 -04:00
protected static final DavPropertyName DEFAULT_SCHEDULE_STATE_PROPERTY = DavPropertyName . create ( " schedule-state " , Namespace . getNamespace ( " CALDAV: " ) ) ;
protected DavPropertyName scheduleStateProperty = DEFAULT_SCHEDULE_STATE_PROPERTY ;
2009-12-07 05:44:17 -05:00
protected static final DavPropertyName PR_INTERNET_CONTENT = DavPropertyName . create ( " x66590102 " , SCHEMAS_MAPI_PROPTAG ) ;
2009-08-29 16:20:44 -04:00
2006-12-12 18:57:24 -05:00
/ * *
* Various standard mail boxes Urls
* /
2007-05-09 18:32:01 -04:00
private String inboxUrl ;
private String deleteditemsUrl ;
2008-12-05 05:01:24 -05:00
private String sentitemsUrl ;
2007-05-09 18:32:01 -04:00
private String sendmsgUrl ;
private String draftsUrl ;
2008-11-26 19:56:28 -05:00
private String calendarUrl ;
2009-08-29 19:49:45 -04:00
private String contactsUrl ;
2009-12-09 16:13:43 -05:00
private String publicFolderUrl ;
2006-12-12 18:57:24 -05:00
/ * *
* Base user mailboxes path ( used to select folder )
* /
2007-05-09 18:32:01 -04:00
private String mailPath ;
2008-12-25 17:16:24 -05:00
private String email ;
2009-03-13 07:11:40 -04:00
private String alias ;
2009-04-16 17:52:17 -04:00
private 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
2009-04-16 18:20:30 -04:00
private boolean disableGalLookup ;
2009-04-07 08:37:28 -04:00
private 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' " ;
private static final String YYYY_MM_DD_T_HHMMSS_Z = " yyyy-MM-dd'T'HH:mm:ss'Z' " ;
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
* /
2009-09-30 17:54:53 -04:00
ExchangeSession ( String url , String userName , String password ) throws IOException {
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
2009-03-26 19:29:09 -04:00
buildMailPath ( method ) ;
2009-12-09 16:13:43 -05:00
// get base http mailbox http urls
2009-03-26 19:29:09 -04:00
getWellKnownFolders ( ) ;
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
}
2009-04-07 08:37:28 -04:00
protected String formatSearchDate ( Date date ) {
SimpleDateFormat dateFormatter = new SimpleDateFormat ( YYYY_MM_DD_HH_MM_SS , Locale . ENGLISH ) ;
2009-03-19 04:58:55 -04:00
dateFormatter . setTimeZone ( GMT_TIMEZONE ) ;
return dateFormatter . format ( date ) ;
}
2009-04-07 08:37:28 -04:00
protected 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 ;
}
2009-04-11 08:27:10 -04:00
protected SimpleDateFormat getExchangeZuluDateFormat ( ) {
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
}
2009-09-03 19:08:35 -04:00
protected SimpleDateFormat getExchangeZuluDateFormatMillisecond ( ) {
SimpleDateFormat dateFormat = new SimpleDateFormat ( YYYY_MM_DD_T_HHMMSS_SSS_Z , Locale . ENGLISH ) ;
dateFormat . setTimeZone ( GMT_TIMEZONE ) ;
return dateFormat ;
}
2009-04-07 08:37:28 -04:00
protected Date parseDate ( String dateString ) throws ParseException {
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
* /
2009-05-15 08:38:24 -04:00
public boolean isExpired ( ) throws NoRouteToHostException , UnknownHostException {
2008-12-04 06:50:31 -05:00
boolean isExpired = false ;
try {
2009-03-18 13:26:33 -04:00
DavGatewayHttpClientFacade . executePropFindMethod (
httpClient , URIUtil . encodePath ( inboxUrl ) , 0 , DISPLAY_NAME ) ;
2009-05-15 08:38:24 -04:00
} catch ( UnknownHostException exc ) {
throw exc ;
} catch ( NoRouteToHostException exc ) {
throw exc ;
2008-12-04 06:50:31 -05:00
} catch ( IOException e ) {
isExpired = true ;
}
return isExpired ;
}
2008-01-09 11:33:57 -05:00
/ * *
* Test authentication mode : form based or basic .
*
* @param url exchange base URL
* @return true if basic authentication detected
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 " ) ;
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 ( ) ;
2009-04-25 06:31:33 -04:00
if ( queryString ! = null & & queryString . contains ( " reason=2 " ) ) {
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
}
}
2009-09-21 17:34:13 -04:00
static final String BASE_HREF = " <base href= \" " ;
2009-04-27 19:03:58 -04:00
protected void buildMailPath ( HttpMethod method ) throws DavMailAuthenticationException {
2009-05-07 04:45:00 -04:00
// find base url
2009-05-19 06:42:56 -04:00
String line ;
2009-05-07 04:45:00 -04:00
2008-11-03 20:47:10 -05:00
// get user mail URL from html body (multi frame)
BufferedReader mainPageReader = null ;
try {
mainPageReader = new BufferedReader ( new InputStreamReader ( method . getResponseBodyAsStream ( ) ) ) ;
//noinspection StatementWithEmptyBody
while ( ( line = mainPageReader . readLine ( ) ) ! = null & & line . toLowerCase ( ) . indexOf ( BASE_HREF ) = = - 1 ) {
}
if ( line ! = null ) {
int start = line . toLowerCase ( ) . indexOf ( BASE_HREF ) + BASE_HREF . length ( ) ;
2009-04-16 17:16:40 -04:00
int end = line . indexOf ( '\"' , start ) ;
2008-11-03 20:47:10 -05:00
String mailBoxBaseHref = line . substring ( start , end ) ;
URL baseURL = new URL ( mailBoxBaseHref ) ;
2008-12-25 17:16:24 -05:00
mailPath = baseURL . getPath ( ) ;
2008-12-26 16:00:33 -05:00
LOGGER . debug ( " Base href found in body, mailPath is " + mailPath ) ;
2010-04-20 11:01:22 -04:00
buildEmail ( method . getURI ( ) . getHost ( ) ) ;
2008-12-26 16:00:33 -05:00
LOGGER . debug ( " Current user email is " + email ) ;
2008-12-22 19:14:41 -05:00
} else {
2008-12-25 17:16:24 -05:00
// failover for Exchange 2007 : build standard mailbox link with email
2010-04-20 11:01:22 -04:00
buildEmail ( method . getURI ( ) . getHost ( ) ) ;
2009-04-23 16:53:22 -04:00
mailPath = " /exchange/ " + email + '/' ;
2008-12-26 16:00:33 -05:00
LOGGER . debug ( " Current user email is " + email + " , mailPath is " + mailPath ) ;
2008-11-03 20:47:10 -05:00
}
} catch ( IOException e ) {
2009-01-08 04:58:31 -05:00
LOGGER . error ( " Error parsing main page at " + method . getPath ( ) , e ) ;
2008-11-03 20:47:10 -05:00
} finally {
if ( mainPageReader ! = null ) {
try {
mainPageReader . close ( ) ;
} catch ( IOException e ) {
LOGGER . error ( " Error parsing main page at " + method . getPath ( ) ) ;
}
}
method . releaseConnection ( ) ;
}
2009-05-07 04:45:00 -04:00
2009-04-27 19:03:58 -04:00
if ( mailPath = = null | | email = = null ) {
throw new DavMailAuthenticationException ( " EXCEPTION_AUTHENTICATION_FAILED_PASSWORD_EXPIRED " ) ;
2009-01-08 04:58:31 -05:00
}
2008-12-22 19:14:41 -05:00
}
2009-04-01 17:31:44 -04:00
protected String getPropertyIfExists ( DavPropertySet properties , String name , Namespace namespace ) {
DavProperty property = properties . get ( name , namespace ) ;
if ( property = = null ) {
return null ;
} else {
return ( String ) property . getValue ( ) ;
}
}
2010-05-06 15:26:44 -04:00
protected String getPropertyIfExists ( DavPropertySet properties , DavPropertyName davPropertyName , String defaultValue ) {
String value = getPropertyIfExists ( properties , davPropertyName ) ;
if ( value = = null ) {
return defaultValue ;
} else {
return value ;
}
}
2009-04-15 18:05:11 -04:00
protected String getPropertyIfExists ( DavPropertySet properties , DavPropertyName davPropertyName ) {
DavProperty property = properties . get ( davPropertyName ) ;
if ( property = = null ) {
return null ;
} else {
return ( String ) property . getValue ( ) ;
}
}
2009-04-01 17:31:44 -04:00
protected int getIntPropertyIfExists ( DavPropertySet properties , String name , Namespace namespace ) {
DavProperty property = properties . get ( name , namespace ) ;
if ( property = = null ) {
return 0 ;
} else {
return Integer . parseInt ( ( String ) property . getValue ( ) ) ;
}
}
protected long getLongPropertyIfExists ( DavPropertySet properties , String name , Namespace namespace ) {
DavProperty property = properties . get ( name , namespace ) ;
if ( property = = null ) {
return 0 ;
} else {
return Long . parseLong ( ( String ) property . getValue ( ) ) ;
}
}
protected String getURIPropertyIfExists ( DavPropertySet properties , String name , Namespace namespace ) throws URIException {
DavProperty property = properties . get ( name , namespace ) ;
if ( property = = null ) {
return null ;
} else {
return URIUtil . decode ( ( String ) property . getValue ( ) ) ;
}
}
2009-09-20 10:10:54 -04:00
protected void getWellKnownFolders ( ) throws DavMailException {
2008-12-09 03:54:08 -05:00
// Retrieve well known URLs
2009-09-20 10:10:54 -04:00
MultiStatusResponse [ ] responses ;
try {
responses = DavGatewayHttpClientFacade . executePropFindMethod (
httpClient , URIUtil . encodePath ( mailPath ) , 0 , WELL_KNOWN_FOLDERS ) ;
if ( responses . length = = 0 ) {
throw new DavMailException ( " EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER " , mailPath ) ;
}
DavPropertySet properties = responses [ 0 ] . getProperties ( HttpStatus . SC_OK ) ;
inboxUrl = getURIPropertyIfExists ( properties , " inbox " , URN_SCHEMAS_HTTPMAIL ) ;
deleteditemsUrl = getURIPropertyIfExists ( properties , " deleteditems " , URN_SCHEMAS_HTTPMAIL ) ;
sentitemsUrl = getURIPropertyIfExists ( properties , " sentitems " , URN_SCHEMAS_HTTPMAIL ) ;
sendmsgUrl = getURIPropertyIfExists ( properties , " sendmsg " , URN_SCHEMAS_HTTPMAIL ) ;
draftsUrl = getURIPropertyIfExists ( properties , " drafts " , URN_SCHEMAS_HTTPMAIL ) ;
calendarUrl = getURIPropertyIfExists ( properties , " calendar " , URN_SCHEMAS_HTTPMAIL ) ;
contactsUrl = getURIPropertyIfExists ( properties , " contacts " , URN_SCHEMAS_HTTPMAIL ) ;
2009-12-09 16:13:43 -05:00
2010-03-18 05:21:23 -04:00
// default public folder path
publicFolderUrl = " /public " ;
// check public folder access
2009-12-09 16:13:43 -05:00
try {
2010-03-18 05:21:23 -04:00
if ( inboxUrl ! = null ) {
// try to build full public URI from inboxUrl
URI publicUri = new URI ( inboxUrl , false ) ;
publicUri . setPath ( " /public " ) ;
publicFolderUrl = publicUri . getURI ( ) ;
}
PropFindMethod propFindMethod = new PropFindMethod ( publicFolderUrl , CONTENT_TAG , 0 ) ;
2010-03-30 16:46:57 -04:00
try {
DavGatewayHttpClientFacade . executeMethod ( httpClient , propFindMethod ) ;
} catch ( IOException e ) {
// workaround for NTLM authentication only on /public
if ( ! DavGatewayHttpClientFacade . hasNTLM ( httpClient ) ) {
DavGatewayHttpClientFacade . addNTLM ( httpClient ) ;
DavGatewayHttpClientFacade . executeMethod ( httpClient , propFindMethod ) ;
}
}
2010-03-18 05:21:23 -04:00
// update public folder URI
2009-12-09 16:13:43 -05:00
publicFolderUrl = propFindMethod . getURI ( ) . getURI ( ) ;
} catch ( IOException e ) {
LOGGER . warn ( " Public folders not available: " + ( e . getMessage ( ) = = null ? e : e . getMessage ( ) ) ) ;
publicFolderUrl = " /public " ;
}
2010-03-18 05:21:23 -04:00
2009-09-20 10:10:54 -04:00
LOGGER . debug ( " Inbox URL : " + inboxUrl +
" Trash URL : " + deleteditemsUrl +
" Sent URL : " + sentitemsUrl +
" Send URL : " + sendmsgUrl +
" Drafts URL : " + draftsUrl +
" Calendar URL : " + calendarUrl +
2009-12-09 16:13:43 -05:00
" Contacts URL : " + contactsUrl +
" Public folder URL : " + publicFolderUrl
2009-09-20 10:10:54 -04:00
) ;
} catch ( IOException e ) {
LOGGER . error ( e . getMessage ( ) ) ;
2009-11-01 17:27:23 -05:00
throw new DavMailAuthenticationException ( " EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER " , mailPath ) ;
2008-12-09 03:54:08 -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
* /
2009-07-28 02:53:46 -04:00
public void createMessage ( String folderPath , String messageName , HashMap < String , String > properties , String messageBody ) throws IOException {
String messageUrl = URIUtil . encodePathQuery ( getFolderPath ( folderPath ) + '/' + messageName + " .EML " ) ;
2009-02-03 18:54:48 -05:00
PropPatchMethod patchMethod ;
2009-02-03 11:13:00 -05:00
// create the message first as draft
2009-02-03 18:54:48 -05:00
if ( properties . containsKey ( " draft " ) ) {
2009-04-01 11:51:12 -04:00
patchMethod = new PropPatchMethod ( messageUrl , buildProperties ( properties ) ) ;
2009-02-03 18:54:48 -05:00
try {
// update message with blind carbon copy and other flags
2009-03-18 13:26:33 -04:00
int statusCode = httpClient . executeMethod ( patchMethod ) ;
2009-02-03 18:54:48 -05:00
if ( statusCode ! = HttpStatus . SC_MULTI_STATUS ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_CREATE_MESSAGE " , messageUrl , statusCode , ' ' , patchMethod . getStatusLine ( ) ) ;
2009-02-03 11:13:00 -05:00
}
2009-02-03 18:54:48 -05:00
} finally {
patchMethod . releaseConnection ( ) ;
}
2009-02-03 11:13:00 -05:00
}
2006-12-12 18:57:24 -05:00
PutMethod putmethod = new PutMethod ( messageUrl ) ;
2008-11-26 19:56:28 -05:00
putmethod . setRequestHeader ( " Translate " , " f " ) ;
try {
2008-11-29 09:24:12 -05:00
// use same encoding as client socket reader
2009-04-01 18:06:53 -04:00
putmethod . setRequestEntity ( new ByteArrayRequestEntity ( messageBody . getBytes ( ) ) ) ;
2009-03-18 13:26:33 -04:00
int code = httpClient . executeMethod ( putmethod ) ;
2006-12-12 18:57:24 -05:00
2009-02-03 18:54:48 -05:00
if ( code ! = HttpStatus . SC_OK & & code ! = HttpStatus . SC_CREATED ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_CREATE_MESSAGE " , messageUrl , code , ' ' , putmethod . getStatusLine ( ) ) ;
2008-11-26 19:56:28 -05:00
}
} finally {
putmethod . releaseConnection ( ) ;
2006-12-12 18:57:24 -05:00
}
2008-12-09 05:10:38 -05:00
2009-02-03 18:54:48 -05:00
// add bcc and other properties
2009-04-16 17:16:40 -04:00
if ( ! properties . isEmpty ( ) ) {
2009-04-01 11:51:12 -04:00
patchMethod = new PropPatchMethod ( messageUrl , buildProperties ( properties ) ) ;
2009-02-04 15:38:07 -05:00
try {
// update message with blind carbon copy and other flags
2009-03-18 13:26:33 -04:00
int statusCode = httpClient . executeMethod ( patchMethod ) ;
2009-02-04 15:38:07 -05:00
if ( statusCode ! = HttpStatus . SC_MULTI_STATUS ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_PATCH_MESSAGE " , messageUrl , statusCode , ' ' , patchMethod . getStatusLine ( ) ) ;
2009-02-04 15:38:07 -05:00
}
2009-02-03 18:54:48 -05:00
2009-02-04 15:38:07 -05:00
} finally {
patchMethod . releaseConnection ( ) ;
}
2009-02-03 18:54:48 -05:00
}
2006-12-12 18:57:24 -05:00
}
2009-04-01 11:51:12 -04:00
protected Message buildMessage ( MultiStatusResponse responseEntity ) throws URIException {
2006-12-12 18:57:24 -05:00
Message message = new Message ( ) ;
message . messageUrl = URIUtil . decode ( responseEntity . getHref ( ) ) ;
2009-04-01 17:31:44 -04:00
DavPropertySet properties = responseEntity . getProperties ( HttpStatus . SC_OK ) ;
2009-11-13 13:07:37 -05:00
message . permanentUrl = getPropertyIfExists ( properties , " permanenturl " , SCHEMAS_EXCHANGE ) ;
2009-04-01 17:31:44 -04:00
message . size = getIntPropertyIfExists ( properties , " x0e080003 " , SCHEMAS_MAPI_PROPTAG ) ;
2010-05-07 04:30:12 -04:00
message . uid = getPropertyIfExists ( properties , " uid " , DAV ) ;
2009-04-01 17:31:44 -04:00
message . imapUid = getLongPropertyIfExists ( properties , " x0e230003 " , SCHEMAS_MAPI_PROPTAG ) ;
message . read = " 1 " . equals ( getPropertyIfExists ( properties , " read " , URN_SCHEMAS_HTTPMAIL ) ) ;
message . junk = " 1 " . equals ( getPropertyIfExists ( properties , " x10830003 " , SCHEMAS_MAPI_PROPTAG ) ) ;
message . flagged = " 2 " . equals ( getPropertyIfExists ( properties , " x10900003 " , SCHEMAS_MAPI_PROPTAG ) ) ;
message . draft = " 9 " . equals ( getPropertyIfExists ( properties , " x0E070003 " , SCHEMAS_MAPI_PROPTAG ) ) ;
String x10810003 = getPropertyIfExists ( properties , " x10810003 " , SCHEMAS_MAPI_PROPTAG ) ;
message . answered = " 102 " . equals ( x10810003 ) | | " 103 " . equals ( x10810003 ) ;
message . forwarded = " 104 " . equals ( x10810003 ) ;
message . date = getPropertyIfExists ( properties , " date " , Namespace . getNamespace ( " urn:schemas:mailheader: " ) ) ;
2009-10-09 18:46:10 -04:00
message . deleted = " 1 " . equals ( getPropertyIfExists ( properties , " deleted " , Namespace . getNamespace ( " " ) ) ) ;
2006-12-12 18:57:24 -05:00
2009-12-16 17:12:54 -05:00
if ( LOGGER . isDebugEnabled ( ) ) {
StringBuilder buffer = new StringBuilder ( ) ;
buffer . append ( " Message " ) ;
if ( message . imapUid ! = 0 ) {
buffer . append ( " IMAP uid: " ) . append ( message . imapUid ) ;
}
if ( message . uid ! = null ) {
buffer . append ( " uid: " ) . append ( message . uid ) ;
}
buffer . append ( " href: " ) . append ( responseEntity . getHref ( ) ) . append ( " permanenturl: " ) . append ( message . permanentUrl ) ;
LOGGER . debug ( buffer . toString ( ) ) ;
}
2006-12-12 18:57:24 -05:00
return message ;
}
2009-04-01 11:51:12 -04:00
protected List < DavProperty > buildProperties ( Map < String , String > properties ) {
ArrayList < DavProperty > list = new ArrayList < DavProperty > ( ) ;
2009-02-03 18:54:48 -05:00
for ( Map . Entry < String , String > entry : properties . entrySet ( ) ) {
if ( " read " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " read " , URN_SCHEMAS_HTTPMAIL ) , entry . getValue ( ) ) ) ;
2009-02-03 18:54:48 -05:00
} else if ( " junk " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10830003 " , SCHEMAS_MAPI_PROPTAG ) , entry . getValue ( ) ) ) ;
2009-02-03 18:54:48 -05:00
} else if ( " flagged " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10900003 " , SCHEMAS_MAPI_PROPTAG ) , entry . getValue ( ) ) ) ;
2009-02-03 18:54:48 -05:00
} else if ( " answered " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10810003 " , SCHEMAS_MAPI_PROPTAG ) , entry . getValue ( ) ) ) ;
2009-02-04 19:18:29 -05:00
if ( " 102 " . equals ( entry . getValue ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10800003 " , SCHEMAS_MAPI_PROPTAG ) , " 261 " ) ) ;
2009-02-04 19:18:29 -05:00
}
} else if ( " forwarded " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10810003 " , SCHEMAS_MAPI_PROPTAG ) , entry . getValue ( ) ) ) ;
2009-02-04 19:18:29 -05:00
if ( " 104 " . equals ( entry . getValue ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x10800003 " , SCHEMAS_MAPI_PROPTAG ) , " 262 " ) ) ;
2009-02-04 19:18:29 -05:00
}
2009-02-03 18:54:48 -05:00
} else if ( " bcc " . equals ( entry . getKey ( ) ) ) {
2009-04-01 11:51:12 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " bcc " , Namespace . getNamespace ( " urn:schemas:mailheader: " ) ) , entry . getValue ( ) ) ) ;
2009-02-03 18:54:48 -05:00
} else if ( " draft " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " x0E070003 " , SCHEMAS_MAPI_PROPTAG ) , entry . getValue ( ) ) ) ;
2009-02-04 18:06:30 -05:00
} else if ( " deleted " . equals ( entry . getKey ( ) ) ) {
2009-10-09 18:46:10 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " _x0030_x8570 " , Namespace . getNamespace ( " http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/ " ) ) , entry . getValue ( ) ) ) ;
2009-02-05 12:15:30 -05:00
} else if ( " datereceived " . equals ( entry . getKey ( ) ) ) {
2009-04-01 17:31:44 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " datereceived " , URN_SCHEMAS_HTTPMAIL ) , entry . getValue ( ) ) ) ;
2009-02-03 18:54:48 -05:00
}
}
2009-04-01 11:51:12 -04:00
return list ;
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
* /
2009-01-26 18:51:08 -05:00
public void updateMessage ( Message message , Map < String , String > properties ) throws IOException {
2009-11-13 13:07:37 -05:00
PropPatchMethod patchMethod = new PropPatchMethod ( message . permanentUrl , buildProperties ( properties ) ) {
2009-10-16 04:58:04 -04:00
@Override
2009-10-09 18:46:10 -04:00
protected void processResponseBody ( HttpState httpState , HttpConnection httpConnection ) {
// ignore response body, sometimes invalid with exchange mapi properties
}
} ;
2009-02-02 02:19:57 -05:00
try {
2009-03-18 13:26:33 -04:00
int statusCode = httpClient . executeMethod ( patchMethod ) ;
2009-02-02 02:19:57 -05:00
if ( statusCode ! = HttpStatus . SC_MULTI_STATUS ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_UPDATE_MESSAGE " ) ;
2009-02-02 02:19:57 -05:00
}
} finally {
patchMethod . releaseConnection ( ) ;
}
2009-01-26 18:51:08 -05:00
}
2009-02-02 02:19:57 -05:00
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 {
2009-07-08 18:51:34 -04:00
return searchMessages ( folderName , " \" DAV:uid \" , \" http://schemas.microsoft.com/mapi/proptag/x0e080003 \" " , " " ) ;
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
* @param conditions conditions string in Exchange SQL syntax
* @return message list
* @throws IOException on error
* /
2009-02-17 17:59:32 -05:00
public MessageList searchMessages ( String folderName , String conditions ) throws IOException {
2009-05-10 10:52:09 -04:00
return searchMessages ( folderName , " \" DAV:uid \" , \" http://schemas.microsoft.com/mapi/proptag/x0e080003 \" " +
2009-03-20 12:05:45 -04:00
" , \" http://schemas.microsoft.com/mapi/proptag/x0e230003 \" " +
2009-02-03 09:38:05 -05:00
" , \" http://schemas.microsoft.com/mapi/proptag/x10830003 \" , \" http://schemas.microsoft.com/mapi/proptag/x10900003 \" " +
2009-02-03 18:54:48 -05:00
" , \" http://schemas.microsoft.com/mapi/proptag/x0E070003 \" , \" http://schemas.microsoft.com/mapi/proptag/x10810003 \" " +
2010-04-23 11:00:10 -04:00
" , \" urn:schemas:httpmail:read \" " +
2009-10-09 18:46:10 -04:00
" , \" http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/0x8570 \" as deleted, \" urn:schemas:mailheader:date \" " , conditions ) ;
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
* @param conditions conditions string in Exchange SQL syntax
* @return message list
* @throws IOException on error
* /
2009-05-10 10:52:09 -04:00
public MessageList searchMessages ( String folderName , String attributes , String conditions ) throws IOException {
String folderUrl = getFolderPath ( folderName ) ;
MessageList messages = new MessageList ( ) ;
2009-11-12 16:32:20 -05:00
StringBuilder searchRequest = new StringBuilder ( ) ;
2009-11-13 13:07:37 -05:00
searchRequest . append ( " Select \" http://schemas.microsoft.com/exchange/permanenturl \" " ) ;
2009-11-12 16:32:20 -05:00
if ( attributes ! = null & & attributes . length ( ) > 0 ) {
2009-11-13 13:07:37 -05:00
searchRequest . append ( ',' ) . append ( attributes ) ;
2009-11-12 16:32:20 -05:00
}
searchRequest . append ( " FROM Scope('SHALLOW TRAVERSAL OF \" " ) . append ( folderUrl ) . append ( " \" ') \ n " )
. append ( " WHERE \" DAV:ishidden \" = False AND \" DAV:isfolder \" = False \ n " ) ;
2009-03-09 19:12:08 -04:00
if ( conditions ! = null ) {
2009-11-12 16:32:20 -05:00
searchRequest . append ( conditions ) ;
2009-03-09 19:12:08 -04:00
}
2009-11-12 16:32:20 -05:00
searchRequest . append ( " ORDER BY \" urn:schemas:httpmail:date \" ASC " ) ;
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod (
2009-11-12 16:32:20 -05:00
httpClient , URIUtil . encodePath ( folderUrl ) , searchRequest . toString ( ) ) ;
2008-12-01 12:56:18 -05:00
2009-04-01 11:51:12 -04:00
for ( MultiStatusResponse response : responses ) {
Message message = buildMessage ( response ) ;
2010-04-06 05:20:09 -04:00
message . messageList = messages ;
2008-12-17 10:27:56 -05:00
messages . add ( message ) ;
2006-12-12 18:57:24 -05:00
}
2009-02-02 02:19:57 -05:00
Collections . sort ( messages ) ;
2006-12-12 18:57:24 -05:00
return messages ;
}
2009-07-28 02:39:27 -04:00
/ * *
2009-09-09 17:50:11 -04:00
* Search folders under given folder .
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 {
2009-09-09 17:50:11 -04:00
return getSubFolders ( folderName , " ( \" DAV:contentclass \" ='urn:content-classes:mailfolder' OR \" DAV:contentclass \" ='urn:content-classes:folder') " , recursive ) ;
}
/ * *
* 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 {
return getSubFolders ( folderName , " \" DAV:contentclass \" ='urn:content-classes:calendarfolder' " , recursive ) ;
}
/ * *
* Search folders under given folder matching filter .
*
* @param folderName Exchange folder name
2009-09-10 04:06:15 -04:00
* @param filter search filter
2009-09-09 17:50:11 -04:00
* @param recursive deep search if true
* @return list of folders
* @throws IOException on error
* /
public List < Folder > getSubFolders ( String folderName , String filter , boolean recursive ) throws IOException {
2009-01-23 08:01:46 -05:00
String mode = recursive ? " DEEP " : " SHALLOW " ;
2009-01-22 19:59:41 -05:00
List < Folder > folders = new ArrayList < Folder > ( ) ;
2009-09-09 17:50:11 -04:00
StringBuilder searchRequest = new StringBuilder ( ) ;
searchRequest . append ( " Select \" DAV:nosubs \" , \" DAV:hassubs \" , \" DAV:hassubs \" , " +
" \" urn:schemas:httpmail:unreadcount \" FROM Scope(' " ) . append ( mode ) . append ( " TRAVERSAL OF \" " ) . append ( getFolderPath ( folderName ) ) . append ( " \" ') \ n " +
" WHERE \" DAV:ishidden \" = False AND \" DAV:isfolder \" = True \ n " ) ;
if ( filter ! = null & & filter . length ( ) > 0 ) {
searchRequest . append ( " AND " ) . append ( filter ) ;
}
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod (
2009-09-09 17:50:11 -04:00
httpClient , URIUtil . encodePath ( getFolderPath ( folderName ) ) , searchRequest . toString ( ) ) ;
2009-01-22 19:59:41 -05:00
2009-04-01 11:51:12 -04:00
for ( MultiStatusResponse response : responses ) {
folders . add ( buildFolder ( response ) ) ;
2009-01-22 19:59:41 -05:00
}
return folders ;
}
2009-04-27 19:03:58 -04:00
protected Folder buildFolder ( MultiStatusResponse entity ) throws IOException {
2009-01-22 19:59:41 -05:00
String href = URIUtil . decode ( entity . getHref ( ) ) ;
Folder folder = new Folder ( ) ;
2009-04-01 19:23:14 -04:00
DavPropertySet properties = entity . getProperties ( HttpStatus . SC_OK ) ;
2010-05-07 04:30:12 -04:00
folder . contentClass = getPropertyIfExists ( properties , " contentclass " , DAV ) ;
folder . hasChildren = " 1 " . equals ( getPropertyIfExists ( properties , " hassubs " , DAV ) ) ;
folder . noInferiors = " 1 " . equals ( getPropertyIfExists ( properties , " nosubs " , DAV ) ) ;
2009-04-01 19:23:14 -04:00
folder . unreadCount = getIntPropertyIfExists ( properties , " unreadcount " , URN_SCHEMAS_HTTPMAIL ) ;
2010-04-01 05:12:44 -04:00
folder . ctag = getPropertyIfExists ( properties , " contenttag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ;
folder . etag = getPropertyIfExists ( properties , " resourcetag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ;
2009-04-01 19:23:14 -04:00
2009-01-22 19:59:41 -05:00
// replace well known folder names
if ( href . startsWith ( inboxUrl ) ) {
2009-07-31 02:21:46 -04:00
folder . folderPath = href . replaceFirst ( inboxUrl , " INBOX " ) ;
2009-01-22 19:59:41 -05:00
} else if ( href . startsWith ( sentitemsUrl ) ) {
2009-07-31 02:21:46 -04:00
folder . folderPath = href . replaceFirst ( sentitemsUrl , " Sent " ) ;
2009-01-22 19:59:41 -05:00
} else if ( href . startsWith ( draftsUrl ) ) {
2009-07-31 02:21:46 -04:00
folder . folderPath = href . replaceFirst ( draftsUrl , " Drafts " ) ;
2009-01-22 19:59:41 -05:00
} else if ( href . startsWith ( deleteditemsUrl ) ) {
2009-07-31 02:21:46 -04:00
folder . folderPath = href . replaceFirst ( deleteditemsUrl , " Trash " ) ;
2009-01-22 19:59:41 -05:00
} else {
2009-01-23 08:01:46 -05:00
int index = href . indexOf ( mailPath . substring ( 0 , mailPath . length ( ) - 1 ) ) ;
2009-01-22 19:59:41 -05:00
if ( index > = 0 ) {
2009-02-19 17:00:36 -05:00
if ( index + mailPath . length ( ) > href . length ( ) ) {
2009-07-31 02:21:46 -04:00
folder . folderPath = " " ;
2009-02-17 20:16:31 -05:00
} else {
2009-07-31 02:21:46 -04:00
folder . folderPath = href . substring ( index + mailPath . length ( ) ) ;
2009-02-17 20:16:31 -05:00
}
2009-01-22 19:59:41 -05:00
} else {
2009-09-15 05:48:52 -04:00
try {
URI folderURI = new URI ( href , false ) ;
folder . folderPath = folderURI . getPath ( ) ;
} catch ( URIException e ) {
throw new DavMailException ( " EXCEPTION_INVALID_FOLDER_URL " , href ) ;
}
2009-01-22 19:59:41 -05:00
}
}
2009-09-25 07:49:04 -04:00
if ( folder . folderPath . endsWith ( " / " ) ) {
folder . folderPath = folder . folderPath . substring ( 0 , folder . folderPath . length ( ) - 1 ) ;
}
2009-01-22 19:59:41 -05:00
return folder ;
}
2007-02-07 06:32:44 -05:00
/ * *
* Delete oldest messages in trash .
2007-02-07 06:51:08 -05:00
* keepDelay is the number of days to keep messages in trash before delete
2007-03-14 07:55:37 -04:00
*
* @throws IOException when unable to purge messages
2007-02-07 06:32:44 -05:00
* /
2008-12-05 05:01:24 -05:00
public void purgeOldestTrashAndSentMessages ( ) throws IOException {
2007-02-07 06:51:08 -05:00
int keepDelay = Settings . getIntProperty ( " davmail.keepDelay " ) ;
2008-12-05 05:01:24 -05:00
if ( keepDelay ! = 0 ) {
purgeOldestFolderMessages ( deleteditemsUrl , keepDelay ) ;
}
// this is a new feature, default is : do nothing
int sentKeepDelay = Settings . getIntProperty ( " davmail.sentKeepDelay " ) ;
if ( sentKeepDelay ! = 0 ) {
purgeOldestFolderMessages ( sentitemsUrl , sentKeepDelay ) ;
2007-02-07 06:51:08 -05:00
}
2008-12-05 05:01:24 -05:00
}
2009-07-28 02:39:27 -04:00
protected void purgeOldestFolderMessages ( String folderUrl , int keepDelay ) throws IOException {
2007-02-07 06:32:44 -05:00
Calendar cal = Calendar . getInstance ( ) ;
cal . add ( Calendar . DAY_OF_MONTH , - keepDelay ) ;
2008-12-05 05:01:24 -05:00
LOGGER . debug ( " Delete messages in " + folderUrl + " since " + cal . getTime ( ) ) ;
2008-12-05 04:29:44 -05:00
2008-12-17 10:27:56 -05:00
String searchRequest = " Select \" DAV:uid \" " +
2008-12-05 05:01:24 -05:00
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderUrl + " \" ') \ n " +
2008-12-05 04:29:44 -05:00
" WHERE \" DAV:isfolder \" = False \ n " +
2009-04-07 08:37:28 -04:00
" AND \" DAV:getlastmodified \" < ' " + formatSearchDate ( cal . getTime ( ) ) + " ' \ n " ;
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod (
2009-03-18 13:26:33 -04:00
httpClient , URIUtil . encodePath ( folderUrl ) , searchRequest ) ;
2008-12-05 04:29:44 -05:00
2009-04-01 11:51:12 -04:00
for ( MultiStatusResponse response : responses ) {
2009-04-15 12:15:00 -04:00
String messageUrl = URIUtil . decode ( response . getHref ( ) ) ;
2008-12-05 04:29:44 -05:00
2008-12-17 10:27:56 -05:00
LOGGER . debug ( " Delete " + messageUrl ) ;
2009-04-15 12:15:00 -04:00
DavGatewayHttpClientFacade . executeDeleteMethod ( httpClient , URIUtil . encodePath ( messageUrl ) ) ;
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
*
2009-07-28 02:39:27 -04:00
* @param recipients recipients list
2009-08-04 16:48:35 -04:00
* @param reader message stream
2009-07-28 02:39:27 -04:00
* @throws IOException on error
* /
2008-12-09 05:10:38 -05:00
public void sendMessage ( List < String > recipients , BufferedReader reader ) throws IOException {
2006-12-12 18:57:24 -05:00
String line = reader . readLine ( ) ;
2008-12-09 05:10:38 -05:00
StringBuilder mailBuffer = new StringBuilder ( ) ;
StringBuilder recipientBuffer = new StringBuilder ( ) ;
boolean inHeader = true ;
boolean inRecipientHeader = false ;
while ( ! " . " . equals ( line ) ) {
2009-02-12 06:33:19 -05:00
mailBuffer . append ( line ) . append ( ( char ) 13 ) . append ( ( char ) 10 ) ;
2006-12-12 18:57:24 -05:00
line = reader . readLine ( ) ;
2009-04-11 09:38:11 -04:00
// Exchange 2007 : skip From: header
if ( ( inHeader & & line . length ( ) > = 5 ) ) {
String prefix = line . substring ( 0 , 5 ) . toLowerCase ( ) ;
if ( " from: " . equals ( prefix ) ) {
line = reader . readLine ( ) ;
}
}
2006-12-12 18:57:24 -05:00
2008-12-09 05:10:38 -05:00
if ( inHeader & & line . length ( ) = = 0 ) {
inHeader = false ;
}
inRecipientHeader = inRecipientHeader & & line . startsWith ( " " ) ;
if ( ( inHeader & & line . length ( ) > = 3 ) | | inRecipientHeader ) {
String prefix = line . substring ( 0 , 3 ) . toLowerCase ( ) ;
if ( " to: " . equals ( prefix ) | | " cc: " . equals ( prefix ) | | inRecipientHeader ) {
inRecipientHeader = true ;
recipientBuffer . append ( line ) ;
2008-12-02 05:20:46 -05:00
}
2006-12-12 18:57:24 -05:00
}
2008-12-09 05:10:38 -05:00
}
// remove visible recipients from list
List < String > visibleRecipients = new ArrayList < String > ( ) ;
for ( String recipient : recipients ) {
if ( recipientBuffer . indexOf ( recipient ) > = 0 ) {
visibleRecipients . add ( recipient ) ;
}
}
recipients . removeAll ( visibleRecipients ) ;
2009-04-16 17:16:40 -04:00
StringBuilder bccBuffer = new StringBuilder ( ) ;
2008-12-09 05:10:38 -05:00
for ( String recipient : recipients ) {
if ( bccBuffer . length ( ) > 0 ) {
bccBuffer . append ( ',' ) ;
}
2009-04-17 06:49:04 -04:00
bccBuffer . append ( '<' ) ;
2008-12-09 05:10:38 -05:00
bccBuffer . append ( recipient ) ;
2009-04-17 06:49:04 -04:00
bccBuffer . append ( '>' ) ;
2006-12-12 18:57:24 -05:00
}
2009-02-03 18:54:48 -05:00
String bcc = bccBuffer . toString ( ) ;
HashMap < String , String > properties = new HashMap < String , String > ( ) ;
if ( bcc . length ( ) > 0 ) {
properties . put ( " bcc " , bcc ) ;
}
2008-12-19 07:27:33 -05:00
String messageName = UUID . randomUUID ( ) . toString ( ) ;
2009-07-28 02:53:46 -04:00
createMessage ( " Drafts " , messageName , properties , mailBuffer . toString ( ) ) ;
2006-12-12 18:57:24 -05:00
2009-04-23 16:53:22 -04:00
String tempUrl = draftsUrl + '/' + messageName + " .EML " ;
2009-04-01 11:51:12 -04:00
MoveMethod method = new MoveMethod ( URIUtil . encodePath ( tempUrl ) , URIUtil . encodePath ( sendmsgUrl ) , true ) ;
2009-03-18 13:26:33 -04:00
int status = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , method ) ;
2009-03-18 18:19:28 -04:00
if ( status ! = HttpStatus . SC_OK ) {
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
}
2006-12-12 18:57:24 -05:00
}
2009-07-28 02:39:27 -04:00
/ * *
2009-07-28 02:53:46 -04:00
* Convert logical or relative folder path to absolute folder path .
2009-08-04 16:48:35 -04:00
*
2009-07-28 02:53:46 -04:00
* @param folderName folder name
* @return folder path
2009-07-28 02:39:27 -04:00
* /
2009-01-22 19:59:41 -05:00
public String getFolderPath ( String folderName ) {
String folderPath ;
if ( folderName . startsWith ( " INBOX " ) ) {
folderPath = folderName . replaceFirst ( " INBOX " , inboxUrl ) ;
} else if ( folderName . startsWith ( " Trash " ) ) {
folderPath = folderName . replaceFirst ( " Trash " , deleteditemsUrl ) ;
} else if ( folderName . startsWith ( " Drafts " ) ) {
folderPath = folderName . replaceFirst ( " Drafts " , draftsUrl ) ;
} else if ( folderName . startsWith ( " Sent " ) ) {
folderPath = folderName . replaceFirst ( " Sent " , sentitemsUrl ) ;
2009-02-25 05:23:07 -05:00
} else if ( folderName . startsWith ( " calendar " ) ) {
folderPath = folderName . replaceFirst ( " calendar " , calendarUrl ) ;
2009-09-15 05:48:52 -04:00
} else if ( folderName . startsWith ( " public " ) ) {
2009-12-09 16:13:43 -05:00
folderPath = publicFolderUrl + folderName . substring ( " public " . length ( ) ) ;
2009-01-23 08:01:46 -05:00
// absolute folder path
2009-01-23 08:03:32 -05:00
} else if ( folderName . startsWith ( " / " ) ) {
2009-01-22 19:59:41 -05:00
folderPath = folderName ;
} else {
folderPath = mailPath + folderName ;
}
return folderPath ;
}
2007-11-08 12:12:36 -05:00
/ * *
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
* /
2009-01-22 19:59:41 -05:00
public Folder getFolder ( String folderName ) throws IOException {
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executePropFindMethod (
2009-03-18 13:26:33 -04:00
httpClient , URIUtil . encodePath ( getFolderPath ( folderName ) ) , 0 , FOLDER_PROPERTIES ) ;
2009-01-23 06:20:20 -05:00
Folder folder = null ;
2009-04-01 11:51:12 -04:00
if ( responses . length > 0 ) {
folder = buildFolder ( responses [ 0 ] ) ;
2009-01-23 06:20:20 -05:00
folder . folderName = folderName ;
2009-01-23 08:01:46 -05:00
}
2006-12-12 18:57:24 -05:00
return folder ;
}
2009-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 {
2009-03-27 06:31:52 -04:00
Folder newFolder = getFolder ( currentFolder . folderName ) ;
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 ( ) ) {
2009-04-23 16:53:22 -04:00
LOGGER . debug ( " Contenttag changed on " + currentFolder . folderName + ' '
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 ;
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
* @throws IOException on error
* /
2009-10-27 19:32:00 -04:00
public void createMessageFolder ( String folderName ) throws IOException {
createFolder ( folderName , " IPF.Note " ) ;
}
/ * *
* Create Exchange calendar folder .
*
* @param folderName logical folder name
* @throws IOException on error
* /
public void createCalendarFolder ( String folderName ) throws IOException {
createFolder ( folderName , " IPF.Appointment " ) ;
}
/ * *
* Create Exchange folder with given folder class .
*
2009-10-29 13:19:00 -04:00
* @param folderName logical folder name
* @param folderClass folder class
2009-10-27 19:32:00 -04:00
* @throws IOException on error
* /
public void createFolder ( String folderName , String folderClass ) throws IOException {
2009-01-23 08:01:46 -05:00
String folderPath = getFolderPath ( folderName ) ;
2009-04-01 11:51:12 -04:00
ArrayList < DavProperty > list = new ArrayList < DavProperty > ( ) ;
2009-10-27 05:26:12 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " outlookfolderclass " , Namespace . getNamespace ( " http://schemas.microsoft.com/exchange/ " ) ) , folderClass ) ) ;
2009-07-31 02:21:46 -04:00
// standard MkColMethod does not take properties, override PropPatchMethod instead
2009-04-01 11:51:12 -04:00
PropPatchMethod method = new PropPatchMethod ( URIUtil . encodePath ( folderPath ) , list ) {
2009-04-28 17:01:40 -04:00
@Override
public String getName ( ) {
2009-01-23 08:01:46 -05:00
return " MKCOL " ;
}
} ;
2009-03-18 18:19:28 -04:00
int status = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , method ) ;
2010-01-21 05:49:23 -05:00
// ok or already exists
2009-03-18 18:19:28 -04:00
if ( status ! = HttpStatus . SC_MULTI_STATUS & & status ! = HttpStatus . SC_METHOD_NOT_ALLOWED ) {
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
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
* /
public void deleteFolder ( String folderName ) throws IOException {
DavGatewayHttpClientFacade . executeDeleteMethod ( httpClient , URIUtil . encodePath ( getFolderPath ( folderName ) ) ) ;
}
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
* /
public void copyMessage ( Message message , String targetFolder ) throws IOException {
2009-11-13 13:07:37 -05:00
String targetPath = URIUtil . encodePath ( getFolderPath ( targetFolder ) ) + '/' + UUID . randomUUID ( ) . toString ( ) ;
CopyMethod method = new CopyMethod ( message . permanentUrl , targetPath , false ) ;
2009-07-31 02:21:46 -04:00
// allow rename if a message with the same name exists
2009-02-02 18:29:44 -05:00
method . addRequestHeader ( " Allow-Rename " , " t " ) ;
try {
2009-03-18 13:26:33 -04:00
int statusCode = httpClient . executeMethod ( method ) ;
2009-02-02 18:29:44 -05:00
if ( statusCode = = HttpStatus . SC_PRECONDITION_FAILED ) {
2009-07-31 02:21:46 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_COPY_MESSAGE " ) ;
2009-02-02 18:29:44 -05:00
} else if ( statusCode ! = HttpStatus . SC_CREATED ) {
2009-03-18 13:26:33 -04:00
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
2009-02-02 18:29:44 -05:00
}
} finally {
method . releaseConnection ( ) ;
}
}
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
* /
2009-01-26 18:51:08 -05:00
public void moveFolder ( String folderName , String targetName ) throws IOException {
String folderPath = getFolderPath ( folderName ) ;
String targetPath = getFolderPath ( targetName ) ;
2009-07-31 02:21:46 -04:00
MoveMethod method = new MoveMethod ( URIUtil . encodePath ( folderPath ) , URIUtil . encodePath ( targetPath ) , false ) ;
2009-02-02 02:19:57 -05:00
try {
2009-03-18 13:26:33 -04:00
int statusCode = httpClient . executeMethod ( method ) ;
2009-02-02 02:19:57 -05:00
if ( statusCode = = HttpStatus . SC_PRECONDITION_FAILED ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_MOVE_FOLDER " ) ;
2009-02-02 02:19:57 -05:00
} else if ( statusCode ! = HttpStatus . SC_CREATED ) {
2009-03-18 13:26:33 -04:00
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
2009-02-02 02:19:57 -05:00
}
2009-01-26 18:51:08 -05:00
} finally {
method . releaseConnection ( ) ;
}
2009-01-23 08:01:46 -05:00
}
2009-11-12 16:32:20 -05:00
protected void moveToTrash ( String encodedMessageUrl ) throws IOException {
2009-11-13 13:07:37 -05:00
String destination = URIUtil . encodePath ( deleteditemsUrl ) + '/' + UUID . randomUUID ( ) . toString ( ) ;
LOGGER . debug ( " Deleting : " + encodedMessageUrl + " to " + destination ) ;
MoveMethod method = new MoveMethod ( encodedMessageUrl , destination , false ) ;
2009-03-05 05:26:20 -05:00
method . addRequestHeader ( " Allow-rename " , " t " ) ;
2009-03-18 13:26:33 -04:00
int status = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , method ) ;
2009-03-13 17:18:11 -04:00
// do not throw error if already deleted
if ( status ! = HttpStatus . SC_CREATED & & status ! = HttpStatus . SC_NOT_FOUND ) {
2009-03-18 13:26:33 -04:00
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
2009-03-05 05:26:20 -05:00
}
if ( method . getResponseHeader ( " Location " ) ! = null ) {
destination = method . getResponseHeader ( " Location " ) . getValue ( ) ;
}
2009-03-18 13:26:33 -04:00
LOGGER . debug ( " Deleted to : " + destination ) ;
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
/ * *
* Folder content class .
* /
public String contentClass ;
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
/ * *
* Requested folder name
* /
2009-01-23 06:20:20 -05:00
public String folderName ;
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 ;
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-01-21 05:49:23 -05:00
* PermanentURL to UID map .
2010-01-05 05:06:47 -05:00
* /
2010-01-21 05:49:23 -05:00
private final HashMap < String , Long > uidUrlHashMap = new HashMap < String , Long > ( ) ;
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-01-21 05:49:23 -05:00
messages = ExchangeSession . this . searchMessages ( folderPath , " " ) ;
fixUids ( messages ) ;
}
/ * *
* Search messages in folder matching query .
*
* @param query search query
* @return message list
* @throws IOException on error
* /
public MessageList searchMessages ( String query ) throws IOException {
MessageList localMessages = ExchangeSession . this . searchMessages ( folderName , query ) ;
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 ) {
if ( uidUrlHashMap . containsKey ( message . getPermanentUrl ( ) ) ) {
long previousUid = uidUrlHashMap . get ( message . getPermanentUrl ( ) ) ;
if ( message . getImapUid ( ) ! = previousUid ) {
LOGGER . debug ( " Restoring IMAP uid " + message . getImapUid ( ) + " -> " + previousUid + " for message " + message . getPermanentUrl ( ) + " ( " + message . messageUrl + ')' ) ;
message . setImapUid ( previousUid ) ;
sortNeeded = true ;
}
} else {
// add message to uid map
uidUrlHashMap . put ( message . getPermanentUrl ( ) , message . getImapUid ( ) ) ;
}
}
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-04-06 05:20:09 -04:00
return " urn:content-classes:calendarfolder " . equals ( contentClass ) ;
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-04-06 05:20:09 -04:00
return " urn:content-classes:contactfolder " . equals ( contentClass ) ;
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
* /
protected MessageList messageList ;
/ * *
* Message url .
* /
2009-07-31 02:21:46 -04:00
protected String messageUrl ;
2010-04-06 05:20:09 -04:00
/ * *
* Message permanent url ( does not change on message move ) .
* /
2009-11-13 13:07:37 -05:00
protected String permanentUrl ;
2009-07-31 02:21:46 -04:00
/ * *
* Message uid .
* /
protected String uid ;
/ * *
* Message IMAP uid , unique in folder ( x0e230003 ) .
* /
protected long imapUid ;
/ * *
* 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 ;
}
2009-11-12 16:32:20 -05:00
/ * *
2009-11-17 18:11:08 -05:00
* Return permanent message url .
2009-11-18 06:13:06 -05:00
*
2009-11-17 18:11:08 -05:00
* @return permanent message url
2009-11-12 16:32:20 -05:00
* /
2010-01-21 05:49:23 -05:00
public String getPermanentUrl ( ) {
2009-11-17 18:11:08 -05:00
return permanentUrl ;
2009-11-12 16:32:20 -05:00
}
2009-07-31 02:21:46 -04:00
/ * *
* 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-07-31 02:21:46 -04:00
/ * *
* Write MIME message to os
2009-08-04 16:48:35 -04:00
*
2010-04-06 05:20:09 -04:00
* @param os output stream
2010-04-02 11:35:37 -04:00
* @param doubleDot replace '.' lines with ' . . ' ( POP protocol )
2009-07-31 02:21:46 -04:00
* @throws IOException on error
* /
2010-04-02 11:35:37 -04:00
public void write ( OutputStream os , boolean doubleDot ) throws IOException {
2009-12-16 04:16:21 -05:00
try {
2010-04-02 11:35:37 -04:00
write ( os , messageUrl , doubleDot ) ;
2009-12-16 04:16:21 -05:00
} catch ( HttpNotFoundException e ) {
2009-12-16 17:12:54 -05:00
LOGGER . debug ( " Message not found at: " + messageUrl + " , retrying with permanenturl " ) ;
2010-04-02 11:35:37 -04:00
write ( os , permanentUrl , doubleDot ) ;
2009-12-16 04:16:21 -05:00
}
}
2010-04-23 06:17:20 -04:00
protected boolean isGzipEncoded ( HttpMethod method ) {
Header [ ] contentEncodingHeaders = method . getResponseHeaders ( " Content-Encoding " ) ;
if ( contentEncodingHeaders ! = null ) {
for ( Header header : contentEncodingHeaders ) {
if ( " gzip " . equals ( header . getValue ( ) ) ) {
return true ;
}
}
}
return false ;
}
2010-04-02 11:35:37 -04:00
protected void write ( OutputStream os , String url , boolean doubleDot ) throws IOException {
2009-12-16 04:16:21 -05:00
GetMethod method = new GetMethod ( URIUtil . encodePath ( url ) ) ;
2009-09-17 17:40:47 -04:00
method . setRequestHeader ( " Content-Type " , " text/xml; charset=utf-8 " ) ;
method . setRequestHeader ( " Translate " , " f " ) ;
2010-04-23 06:17:20 -04:00
method . setRequestHeader ( " Accept-Encoding " , " gzip " ) ;
2007-04-25 18:04:37 -04:00
BufferedReader reader = null ;
2006-12-12 18:57:24 -05:00
try {
2010-03-24 05:59:23 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , method , true ) ;
2006-12-12 18:57:24 -05:00
2010-04-23 06:17:20 -04:00
if ( isGzipEncoded ( method ) ) {
reader = new BufferedReader ( new InputStreamReader ( new GZIPInputStream ( method . getResponseBodyAsStream ( ) ) ) ) ;
} else {
reader = new BufferedReader ( new InputStreamReader ( method . getResponseBodyAsStream ( ) ) ) ;
}
2008-02-05 18:17:31 -05:00
OutputStreamWriter isoWriter = new OutputStreamWriter ( os ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
2010-04-02 11:35:37 -04:00
if ( doubleDot & & " . " . equals ( line ) ) {
2008-02-05 18:17:31 -05:00
line = " .. " ;
2008-02-14 09:20:37 -05:00
// patch text/calendar to include utf-8 encoding
} else if ( " Content-Type: text/calendar; " . equals ( line ) ) {
2009-04-16 17:16:40 -04:00
StringBuilder headerBuffer = new StringBuilder ( ) ;
2008-02-14 09:20:37 -05:00
headerBuffer . append ( line ) ;
while ( ( line = reader . readLine ( ) ) ! = null & & line . startsWith ( " \ t " ) ) {
headerBuffer . append ( ( char ) 13 ) ;
headerBuffer . append ( ( char ) 10 ) ;
headerBuffer . append ( line ) ;
}
if ( headerBuffer . indexOf ( " charset " ) < 0 ) {
headerBuffer . append ( " ;charset=utf-8 " ) ;
}
headerBuffer . append ( ( char ) 13 ) ;
headerBuffer . append ( ( char ) 10 ) ;
headerBuffer . append ( line ) ;
line = headerBuffer . toString ( ) ;
2008-01-31 17:17:44 -05:00
}
2008-02-05 18:17:31 -05:00
isoWriter . write ( line ) ;
isoWriter . write ( ( char ) 13 ) ;
isoWriter . write ( ( char ) 10 ) ;
2007-04-25 18:04:37 -04:00
}
2008-02-05 18:17:31 -05:00
isoWriter . flush ( ) ;
2009-12-07 05:44:17 -05:00
} catch ( HttpServerErrorException e ) {
2009-12-09 16:13:43 -05:00
LOGGER . warn ( " Unable to retrieve message at: " + messageUrl ) ;
2009-12-07 05:44:17 -05:00
throw e ;
2007-04-25 18:04:37 -04:00
} finally {
if ( reader ! = null ) {
try {
reader . close ( ) ;
} catch ( IOException e ) {
2008-02-05 18:17:31 -05:00
LOGGER . warn ( " Error closing message input stream " , e ) ;
2007-04-25 18:04:37 -04:00
}
}
2009-09-17 17:40:47 -04:00
method . releaseConnection ( ) ;
2006-12-12 18:57:24 -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 {
// load message
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
write ( baos , false ) ;
2010-04-23 09:42:37 -04:00
// load and parse message
mimeBody = new SharedByteArrayInputStream ( baos . toByteArray ( ) ) ;
mimeMessage = new MimeMessage ( null , mimeBody ) ;
LOGGER . debug ( " Downloaded message content for " + imapUid + " ( " + baos . size ( ) + ')' ) ;
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 {
2009-11-13 13:07:37 -05:00
DavGatewayHttpClientFacade . executeDeleteMethod ( httpClient , permanentUrl ) ;
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 ) ;
2009-11-13 13:07:37 -05:00
ExchangeSession . this . moveToTrash ( permanentUrl ) ;
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-05-06 15:26:44 -04:00
public abstract class Item {
2008-11-26 19:56:28 -05:00
protected String href ;
2009-11-13 16:41:43 -05:00
protected String permanentUrl ;
2010-04-12 04:54:53 -04:00
protected String displayName ;
2008-11-26 19:56:28 -05:00
protected String etag ;
2009-11-27 04:34:20 -05:00
protected String contentClass ;
protected String noneMatch ;
2010-05-07 04:30:12 -04:00
/ * *
* ICS content
* /
protected String itemBody ;
2010-05-06 15:26:44 -04:00
/ * *
2010-05-07 04:30:12 -04:00
* Build item instance .
*
* @param messageUrl message url
* @param contentClass content class
* @param itemBody item body
* @param etag item etag
* @param noneMatch none match flag
2010-05-06 15:26:44 -04:00
* /
2010-05-07 04:30:12 -04:00
public Item ( String messageUrl , String contentClass , String itemBody , String etag , String noneMatch ) {
this . href = messageUrl ;
this . contentClass = contentClass ;
this . itemBody = itemBody ;
this . etag = etag ;
this . noneMatch = noneMatch ;
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
* /
public abstract String getBody ( ) throws HttpException ;
/ * *
* Build Item instance from multistatusResponse info
2010-05-07 04:30:12 -04:00
*
2010-05-06 15:26:44 -04:00
* @param multiStatusResponse response
* @throws URIException on error
* /
public Item ( MultiStatusResponse multiStatusResponse ) throws URIException {
href = URIUtil . decode ( multiStatusResponse . getHref ( ) ) ;
permanentUrl = getPropertyIfExists ( multiStatusResponse . getProperties ( HttpStatus . SC_OK ) , " permanenturl " , SCHEMAS_EXCHANGE ) ;
2010-05-07 04:30:12 -04:00
etag = getPropertyIfExists ( multiStatusResponse . getProperties ( HttpStatus . SC_OK ) , " getetag " , DAV ) ;
displayName = getPropertyIfExists ( multiStatusResponse . getProperties ( HttpStatus . SC_OK ) , " displayname " , DAV ) ;
2010-05-06 15:26:44 -04:00
}
/ * *
* Get event name ( file name part in URL ) .
*
* @return event name
* /
public String getName ( ) {
int index = href . lastIndexOf ( '/' ) ;
if ( index > = 0 ) {
return href . substring ( index + 1 ) ;
} else {
return href ;
}
}
/ * *
* 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-05-07 04:30:12 -04:00
2010-05-06 15:26:44 -04:00
/ * *
* Calendar event object
* /
public class Contact extends Item {
/ * *
* Build Contact instance from multistatusResponse info
2010-05-07 04:30:12 -04:00
*
2010-05-06 15:26:44 -04:00
* @param multiStatusResponse response
* @throws URIException on error
* /
public Contact ( MultiStatusResponse multiStatusResponse ) throws URIException {
super ( multiStatusResponse ) ;
}
2010-05-07 04:30:12 -04:00
/ * *
* { @inheritDoc }
* /
public Contact ( String messageUrl , String contentClass , String itemBody , String etag , String noneMatch ) {
super ( messageUrl . endsWith ( " .vcf " ) ? messageUrl . substring ( 0 , messageUrl . length ( ) - 3 ) + " EML " : messageUrl , contentClass , itemBody , etag , noneMatch ) ;
}
/ * *
* 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 ;
}
/ * *
* 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 " ;
}
@Override
public String getBody ( ) throws HttpException {
// first retrieve contact details
String result = null ;
PropFindMethod propFindMethod = null ;
try {
propFindMethod = new PropFindMethod ( URIUtil . encodePath ( permanentUrl ) ) ;
DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , propFindMethod ) ;
MultiStatus responses = propFindMethod . getResponseBodyAsMultiStatus ( ) ;
if ( responses . getResponses ( ) . length > 0 ) {
DavPropertySet properties = responses . getResponses ( ) [ 0 ] . getProperties ( HttpStatus . SC_OK ) ;
ICSBufferedWriter writer = new ICSBufferedWriter ( ) ;
writer . writeLine ( " BEGIN:VCARD " ) ;
writer . writeLine ( " VERSION:3.0 " ) ;
writer . write ( " UID: " ) ;
2010-05-07 04:30:12 -04:00
writer . writeLine ( getUid ( ) ) ;
2010-05-06 15:26:44 -04:00
writer . write ( " FN: " ) ;
writer . writeLine ( getPropertyIfExists ( properties , DavPropertyName . create ( " cn " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
// RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes
writer . write ( " N: " ) ;
writer . write ( getPropertyIfExists ( properties , DavPropertyName . create ( " sn " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
writer . write ( " ; " ) ;
writer . write ( getPropertyIfExists ( properties , DavPropertyName . create ( " givenName " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
writer . write ( " ; " ) ;
writer . writeLine ( getPropertyIfExists ( properties , DavPropertyName . create ( " middlename " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
writer . write ( " TEL;TYPE=cell: " ) ;
writer . writeLine ( getPropertyIfExists ( properties , DavPropertyName . create ( " mobile " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
//writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("initials", URN_SCHEMAS_CONTACTS), ""));
// 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
// ADR;TYPE=dom,home,postal,parcel:;;123 Main Street;Any Town;CA;91921-1234
writer . write ( " ADR;TYPE=home:;; " ) ;
writer . write ( getPropertyIfExists ( properties , DavPropertyName . create ( " homepostaladdress " , URN_SCHEMAS_CONTACTS ) , " " ) ) ;
writer . write ( " ;;; " ) ;
writer . newLine ( ) ;
writer . writeLine ( " END:VCARD " ) ;
result = writer . toString ( ) ;
}
} catch ( DavException e ) {
throw buildHttpException ( e ) ;
} catch ( IOException e ) {
throw buildHttpException ( e ) ;
} finally {
if ( propFindMethod ! = null ) {
2010-05-07 04:30:12 -04:00
propFindMethod . releaseConnection ( ) ;
2010-05-06 15:26:44 -04:00
}
}
return result ;
2010-05-07 04:30:12 -04:00
}
protected List < DavProperty > buildProperties ( ) throws IOException {
ArrayList < DavProperty > list = new ArrayList < DavProperty > ( ) ;
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " contentclass " , DAV ) , contentClass ) ) ;
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " outlookmessageclass " , SCHEMAS_EXCHANGE ) , " IPM.Contact " ) ) ;
ICSBufferedReader reader = new ICSBufferedReader ( new StringReader ( itemBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
int index = line . indexOf ( ':' ) ;
if ( index > = 0 ) {
String key = line . substring ( 0 , index ) ;
String value = line . substring ( index + 1 ) ;
if ( " FN " . equals ( key ) ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " cn " , URN_SCHEMAS_CONTACTS ) , value ) ) ;
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " subject " , URN_SCHEMAS_HTTPMAIL ) , value ) ) ;
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " fileas " , URN_SCHEMAS_CONTACTS ) , value ) ) ;
} else if ( " N " . equals ( key ) ) {
String [ ] values = value . split ( " ; " ) ;
if ( values . length > 0 ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " sn " , URN_SCHEMAS_CONTACTS ) , values [ 0 ] ) ) ;
}
if ( values . length > 1 ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " givenName " , URN_SCHEMAS_CONTACTS ) , values [ 1 ] ) ) ;
}
} else if ( " TEL;TYPE=cell " . equals ( key ) ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " mobile " , URN_SCHEMAS_CONTACTS ) , value ) ) ;
} else if ( " TEL;TYPE=work " . equals ( key ) ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " telephoneNumber " , URN_SCHEMAS_CONTACTS ) , value ) ) ;
} else if ( " TEL;TYPE=home " . equals ( key ) ) {
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " homePhone " , URN_SCHEMAS_CONTACTS ) , value ) ) ;
}
}
}
return list ;
}
protected ItemResult createOrUpdate ( ) throws IOException {
int status = 0 ;
PropPatchMethod propPatchMethod = new PropPatchMethod ( URIUtil . encodePath ( href ) , buildProperties ( ) ) ;
propPatchMethod . setRequestHeader ( " Translate " , " f " ) ;
if ( etag ! = null ) {
propPatchMethod . setRequestHeader ( " If-Match " , etag ) ;
}
if ( noneMatch ! = null ) {
propPatchMethod . setRequestHeader ( " If-None-Match " , noneMatch ) ;
}
try {
status = httpClient . executeMethod ( propPatchMethod ) ;
if ( status = = HttpStatus . SC_MULTI_STATUS ) {
if ( etag ! = null ) {
LOGGER . debug ( " Updated contact " + href ) ;
} else {
LOGGER . warn ( " Overwritten contact " + href ) ;
}
status = HttpStatus . SC_CREATED ;
} else {
LOGGER . warn ( " Unable to create or update contact " + status + ' ' + propPatchMethod . getStatusLine ( ) ) ;
}
} finally {
propPatchMethod . releaseConnection ( ) ;
}
ItemResult itemResult = new ItemResult ( ) ;
// 440 means forbidden on Exchange
if ( status = = 440 ) {
status = HttpStatus . SC_FORBIDDEN ;
}
itemResult . status = status ;
if ( propPatchMethod . getResponseHeader ( " GetETag " ) ! = null ) {
itemResult . etag = propPatchMethod . getResponseHeader ( " GetETag " ) . getValue ( ) ;
}
return itemResult ;
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
* /
public class Event extends Item {
2009-11-27 04:34:20 -05:00
/ * *
2010-05-07 04:30:12 -04:00
* Build Event instance from response info .
*
2010-05-06 15:26:44 -04:00
* @param multiStatusResponse response
* @throws URIException on error
* /
public Event ( MultiStatusResponse multiStatusResponse ) throws URIException {
super ( multiStatusResponse ) ;
}
/ * *
2010-05-07 04:30:12 -04:00
* { @inheritDoc }
2010-05-06 15:26:44 -04:00
* /
2010-05-07 04:30:12 -04:00
public Event ( String messageUrl , String contentClass , String itemBody , String etag , String noneMatch ) {
super ( messageUrl , contentClass , itemBody , etag , noneMatch ) ;
2010-05-06 15:26:44 -04:00
}
@Override
public String getContentType ( ) {
return " text/calendar;charset=UTF-8 " ;
}
2009-11-29 16:10:01 -05:00
protected boolean isCalendarContentType ( String contentType ) {
2009-12-07 05:44:17 -05:00
return contentType . startsWith ( " text/calendar " ) | | contentType . startsWith ( " application/ics " ) ;
2009-11-29 16:10:01 -05:00
}
2008-11-26 19:56:28 -05:00
2009-03-04 13:20:35 -05:00
protected MimePart getCalendarMimePart ( MimeMultipart multiPart ) throws IOException , MessagingException {
MimePart bodyPart = null ;
for ( int i = 0 ; i < multiPart . getCount ( ) ; i + + ) {
String contentType = multiPart . getBodyPart ( i ) . getContentType ( ) ;
2009-11-29 16:10:01 -05:00
if ( isCalendarContentType ( contentType ) ) {
2009-04-16 17:52:17 -04:00
bodyPart = ( MimePart ) multiPart . getBodyPart ( i ) ;
2009-03-04 13:20:35 -05:00
break ;
} else if ( contentType . startsWith ( " multipart " ) ) {
Object content = multiPart . getBodyPart ( i ) . getContent ( ) ;
if ( content instanceof MimeMultipart ) {
bodyPart = getCalendarMimePart ( ( MimeMultipart ) content ) ;
}
}
}
return bodyPart ;
}
2009-11-27 18:27:50 -05:00
/ * *
* Load ICS content from MIME message input stream
*
* @param mimeInputStream mime message input stream
* @return mime message ics attachment body
* @throws IOException on error
* @throws MessagingException on error
* /
protected String getICS ( InputStream mimeInputStream ) throws IOException , MessagingException {
2009-12-07 05:44:17 -05:00
String result ;
2009-11-27 18:27:50 -05:00
MimeMessage mimeMessage = new MimeMessage ( null , mimeInputStream ) ;
Object mimeBody = mimeMessage . getContent ( ) ;
2009-11-29 16:10:01 -05:00
MimePart bodyPart = null ;
2009-11-27 18:27:50 -05:00
if ( mimeBody instanceof MimeMultipart ) {
bodyPart = getCalendarMimePart ( ( MimeMultipart ) mimeBody ) ;
2009-12-07 05:44:17 -05:00
} else if ( isCalendarContentType ( mimeMessage . getContentType ( ) ) ) {
2009-11-27 18:27:50 -05:00
// no multipart, single body
bodyPart = mimeMessage ;
}
if ( bodyPart ! = null ) {
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
bodyPart . getDataHandler ( ) . writeTo ( baos ) ;
baos . close ( ) ;
result = fixICS ( new String ( baos . toByteArray ( ) , " UTF-8 " ) , true ) ;
2009-11-29 16:10:01 -05:00
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
2010-04-15 08:56:55 -04:00
mimeMessage . writeTo ( baos ) ;
2009-11-29 16:10:01 -05:00
baos . close ( ) ;
throw new DavMailException ( " EXCEPTION_INVALID_MESSAGE_CONTENT " , new String ( baos . toByteArray ( ) , " UTF-8 " ) ) ;
2009-11-27 18:27:50 -05:00
}
return result ;
}
protected String getICSFromInternetContentProperty ( ) throws IOException , DavException , MessagingException {
String result = null ;
// PropFind PR_INTERNET_CONTENT
DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet ( ) ;
davPropertyNameSet . add ( PR_INTERNET_CONTENT ) ;
PropFindMethod propFindMethod = new PropFindMethod ( URIUtil . encodePath ( permanentUrl ) , davPropertyNameSet , 0 ) ;
try {
DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , propFindMethod ) ;
MultiStatus responses = propFindMethod . getResponseBodyAsMultiStatus ( ) ;
if ( responses . getResponses ( ) . length > 0 ) {
DavPropertySet properties = responses . getResponses ( ) [ 0 ] . getProperties ( HttpStatus . SC_OK ) ;
DavProperty property = properties . get ( PR_INTERNET_CONTENT ) ;
if ( property ! = null ) {
byte [ ] byteArray = Base64 . decodeBase64 ( ( ( String ) property . getValue ( ) ) . getBytes ( ) ) ;
result = getICS ( new ByteArrayInputStream ( byteArray ) ) ;
}
}
} finally {
propFindMethod . releaseConnection ( ) ;
}
return result ;
}
2009-08-13 04:34:51 -04:00
/ * *
* Load ICS content from Exchange server .
* User Translate : f header to get MIME event content and get ICS attachment from it
2009-08-21 05:58:19 -04:00
*
2009-08-13 04:34:51 -04:00
* @return ICS ( iCalendar ) event
2010-02-19 18:21:16 -05:00
* @throws HttpException on error
2009-08-13 04:34:51 -04:00
* /
2010-05-06 15:26:44 -04:00
@Override
public String getBody ( ) throws HttpException {
2009-12-07 16:57:56 -05:00
String result ;
2009-11-13 16:41:43 -05:00
LOGGER . debug ( " Get event: " + permanentUrl ) ;
2009-11-27 18:27:50 -05:00
// try to get PR_INTERNET_CONTENT
2008-11-26 19:56:28 -05:00
try {
2009-11-27 18:27:50 -05:00
result = getICSFromInternetContentProperty ( ) ;
if ( result = = null ) {
GetMethod method = new GetMethod ( permanentUrl ) ;
method . setRequestHeader ( " Content-Type " , " text/xml; charset=utf-8 " ) ;
method . setRequestHeader ( " Translate " , " f " ) ;
try {
2010-01-27 16:54:30 -05:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , method , true ) ;
2009-11-27 18:27:50 -05:00
result = getICS ( method . getResponseBodyAsStream ( ) ) ;
} finally {
method . releaseConnection ( ) ;
}
2008-11-26 19:56:28 -05:00
}
2009-11-27 18:27:50 -05:00
} catch ( DavException e ) {
2009-12-07 16:57:56 -05:00
throw buildHttpException ( e ) ;
2009-09-17 17:40:47 -04:00
} catch ( IOException e ) {
2009-12-07 16:57:56 -05:00
throw buildHttpException ( e ) ;
2009-02-25 05:23:07 -05:00
} catch ( MessagingException e ) {
2009-12-07 16:57:56 -05:00
throw buildHttpException ( e ) ;
2008-11-26 19:56:28 -05:00
}
2009-02-25 05:23:07 -05:00
return result ;
2008-11-26 19:56:28 -05:00
}
2009-11-27 04:34:20 -05:00
protected String fixTimezoneId ( String line , String validTimezoneId ) {
return StringUtil . replaceToken ( line , " TZID= " , " : " , validTimezoneId ) ;
}
protected void splitExDate ( ICSBufferedWriter result , String line ) {
int cur = line . lastIndexOf ( ':' ) + 1 ;
String start = line . substring ( 0 , cur ) ;
for ( int next = line . indexOf ( ',' , cur ) ; next ! = - 1 ; next = line . indexOf ( ',' , cur ) ) {
String val = line . substring ( cur , next ) ;
result . writeLine ( start + val ) ;
cur = next + 1 ;
}
result . writeLine ( start + line . substring ( cur ) ) ;
}
protected String getAllDayLine ( String line ) throws IOException {
int valueIndex = line . lastIndexOf ( ':' ) ;
int valueEndIndex = line . lastIndexOf ( 'T' ) ;
if ( valueIndex < 0 | | valueEndIndex < 0 ) {
throw new DavMailException ( " EXCEPTION_INVALID_ICS_LINE " , line ) ;
}
2010-04-15 08:56:55 -04:00
int keyIndex = line . indexOf ( ';' ) ;
if ( keyIndex = = - 1 ) {
keyIndex = valueIndex ;
}
2009-11-27 04:34:20 -05:00
String dateValue = line . substring ( valueIndex + 1 , valueEndIndex ) ;
2010-03-19 06:32:29 -04:00
String key = line . substring ( 0 , Math . min ( keyIndex , valueIndex ) ) ;
2009-11-27 04:34:20 -05:00
return key + " ;VALUE=DATE: " + dateValue ;
}
protected String fixICS ( String icsBody , boolean fromServer ) throws IOException {
// first pass : detect
class AllDayState {
boolean isAllDay ;
boolean hasCdoAllDay ;
boolean isCdoAllDay ;
}
dumpIndex + + ;
dumpICS ( icsBody , fromServer , false ) ;
// Convert event class from and to iCal
// See https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-privateevents.txt
boolean isAppleiCal = false ;
boolean hasAttendee = false ;
boolean hasCdoBusyStatus = false ;
// detect ics event with empty timezone (all day from Lightning)
boolean hasTimezone = false ;
String transp = null ;
String validTimezoneId = null ;
String eventClass = null ;
String organizer = null ;
String action = null ;
boolean sound = false ;
List < AllDayState > allDayStates = new ArrayList < AllDayState > ( ) ;
AllDayState currentAllDayState = new AllDayState ( ) ;
BufferedReader reader = null ;
try {
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
int index = line . indexOf ( ':' ) ;
if ( index > = 0 ) {
String key = line . substring ( 0 , index ) ;
String value = line . substring ( index + 1 ) ;
if ( " DTSTART;VALUE=DATE " . equals ( key ) ) {
currentAllDayState . isAllDay = true ;
} else if ( " X-MICROSOFT-CDO-ALLDAYEVENT " . equals ( key ) ) {
currentAllDayState . hasCdoAllDay = true ;
currentAllDayState . isCdoAllDay = " TRUE " . equals ( value ) ;
} else if ( " END:VEVENT " . equals ( line ) ) {
allDayStates . add ( currentAllDayState ) ;
currentAllDayState = new AllDayState ( ) ;
} else if ( " PRODID " . equals ( key ) & & line . contains ( " iCal " ) ) {
// detect iCal created events
isAppleiCal = true ;
} else if ( isAppleiCal & & " X-CALENDARSERVER-ACCESS " . equals ( key ) ) {
eventClass = value ;
} else if ( ! isAppleiCal & & " CLASS " . equals ( key ) ) {
eventClass = value ;
} else if ( " ACTION " . equals ( key ) ) {
action = value ;
} else if ( " ATTACH;VALUES=URI " . equals ( key ) ) {
// This is a marker that this event has an alarm with sound
sound = true ;
} else if ( key . startsWith ( " ORGANIZER " ) ) {
if ( value . startsWith ( " MAILTO: " ) ) {
organizer = value . substring ( 7 ) ;
} else {
organizer = value ;
}
} else if ( key . startsWith ( " ATTENDEE " ) ) {
hasAttendee = true ;
} else if ( " TRANSP " . equals ( key ) ) {
transp = value ;
} else if ( line . startsWith ( " TZID:(GMT " ) | |
// additional test for Outlook created recurring events
line . startsWith ( " TZID:GMT " ) ) {
try {
validTimezoneId = ResourceBundle . getBundle ( " timezones " ) . getString ( value ) ;
} catch ( MissingResourceException mre ) {
LOGGER . warn ( new BundleMessage ( " LOG_INVALID_TIMEZONE " , value ) ) ;
}
} else if ( " X-MICROSOFT-CDO-BUSYSTATUS " . equals ( key ) ) {
hasCdoBusyStatus = true ;
} else if ( " BEGIN:VTIMEZONE " . equals ( line ) ) {
hasTimezone = true ;
}
}
}
} finally {
if ( reader ! = null ) {
reader . close ( ) ;
}
}
// second pass : fix
int count = 0 ;
ICSBufferedWriter result = new ICSBufferedWriter ( ) ;
try {
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
// remove empty properties
if ( " CLASS: " . equals ( line ) | | " LOCATION: " . equals ( line ) ) {
continue ;
}
// fix invalid exchange timezoneid
if ( validTimezoneId ! = null & & line . indexOf ( " ;TZID= " ) > = 0 ) {
line = fixTimezoneId ( line , validTimezoneId ) ;
}
if ( ! fromServer & & " BEGIN:VEVENT " . equals ( line ) & & ! hasTimezone ) {
result . write ( ExchangeSession . this . getVTimezone ( ) . timezoneBody ) ;
hasTimezone = true ;
}
if ( ! fromServer & & currentAllDayState . isAllDay & & " X-MICROSOFT-CDO-ALLDAYEVENT:FALSE " . equals ( line ) ) {
line = " X-MICROSOFT-CDO-ALLDAYEVENT:TRUE " ;
} else if ( ! fromServer & & " END:VEVENT " . equals ( line ) ) {
if ( ! hasCdoBusyStatus ) {
result . writeLine ( " X-MICROSOFT-CDO-BUSYSTATUS: " + ( ! " TRANSPARENT " . equals ( transp ) ? " BUSY " : " FREE " ) ) ;
}
if ( currentAllDayState . isAllDay & & ! currentAllDayState . hasCdoAllDay ) {
result . writeLine ( " X-MICROSOFT-CDO-ALLDAYEVENT:TRUE " ) ;
}
// add organizer line to all events created in Exchange for active sync
if ( organizer = = null ) {
result . writeLine ( " ORGANIZER:MAILTO: " + email ) ;
}
} else if ( ! fromServer & & line . startsWith ( " X-MICROSOFT-CDO-BUSYSTATUS: " ) ) {
line = " X-MICROSOFT-CDO-BUSYSTATUS: " + ( ! " TRANSPARENT " . equals ( transp ) ? " BUSY " : " FREE " ) ;
} else if ( ! fromServer & & ! currentAllDayState . isAllDay & & " X-MICROSOFT-CDO-ALLDAYEVENT:TRUE " . equals ( line ) ) {
line = " X-MICROSOFT-CDO-ALLDAYEVENT:FALSE " ;
} else if ( fromServer & & currentAllDayState . isCdoAllDay & & line . startsWith ( " DTSTART " ) & & ! line . startsWith ( " DTSTART;VALUE=DATE " ) ) {
line = getAllDayLine ( line ) ;
} else if ( fromServer & & currentAllDayState . isCdoAllDay & & line . startsWith ( " DTEND " ) & & ! line . startsWith ( " DTEND;VALUE=DATE " ) ) {
line = getAllDayLine ( line ) ;
} else if ( ! fromServer & & currentAllDayState . isAllDay & & line . startsWith ( " DTSTART " ) & & line . startsWith ( " DTSTART;VALUE=DATE " ) ) {
line = " DTSTART;TZID= \" " + ExchangeSession . this . getVTimezone ( ) . timezoneId + " \" : " + line . substring ( 19 ) + " T000000 " ;
} else if ( ! fromServer & & currentAllDayState . isAllDay & & line . startsWith ( " DTEND " ) & & line . startsWith ( " DTEND;VALUE=DATE " ) ) {
line = " DTEND;TZID= \" " + ExchangeSession . this . getVTimezone ( ) . timezoneId + " \" : " + line . substring ( 17 ) + " T000000 " ;
} else if ( line . startsWith ( " TZID: " ) & & validTimezoneId ! = null ) {
line = " TZID: " + validTimezoneId ;
} else if ( " BEGIN:VEVENT " . equals ( line ) ) {
currentAllDayState = allDayStates . get ( count + + ) ;
} else if ( line . startsWith ( " X-CALENDARSERVER-ACCESS: " ) ) {
if ( ! isAppleiCal ) {
continue ;
} else {
if ( " CONFIDENTIAL " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " CLASS:PRIVATE " ) ;
} else if ( " PRIVATE " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " CLASS:CONFIDENTIAL " ) ;
} else {
result . writeLine ( " CLASS: " + eventClass ) ;
}
}
} else if ( line . startsWith ( " EXDATE;TZID= " ) | | line . startsWith ( " EXDATE: " ) ) {
// Apple iCal doesn't support EXDATE with multiple exceptions
// on one line. Split into multiple EXDATE entries (which is
// also legal according to the caldav standard).
splitExDate ( result , line ) ;
continue ;
} else if ( line . startsWith ( " X-ENTOURAGE_UUID: " ) ) {
// Apple iCal doesn't understand this key, and it's entourage
// specific (i.e. not needed by any caldav client): strip it out
continue ;
} else if ( fromServer & & line . startsWith ( " ATTENDEE; " )
& & ( line . indexOf ( email ) > = 0 ) ) {
// If this is coming from the server, strip out RSVP for this
// user as an attendee where the partstat is something other
// than PARTSTAT=NEEDS-ACTION since the RSVP confuses iCal4 into
// thinking the attendee has not replied
int rsvpSuffix = line . indexOf ( " RSVP=TRUE; " ) ;
int rsvpPrefix = line . indexOf ( " ;RSVP=TRUE " ) ;
if ( ( ( rsvpSuffix > = 0 ) | | ( rsvpPrefix > = 0 ) )
& & ( line . indexOf ( " PARTSTAT= " ) > = 0 )
& & ( line . indexOf ( " PARTSTAT=NEEDS-ACTION " ) < 0 ) ) {
// Strip out the "RSVP" line from the calendar entry
if ( rsvpSuffix > = 0 ) {
line = line . substring ( 0 , rsvpSuffix ) + line . substring ( rsvpSuffix + 10 ) ;
} else {
line = line . substring ( 0 , rsvpPrefix ) + line . substring ( rsvpPrefix + 10 ) ;
}
}
} else if ( line . startsWith ( " ACTION: " ) ) {
2009-12-20 05:52:53 -05:00
if ( fromServer & & " DISPLAY " . equals ( action )
// convert DISPLAY to AUDIO only if user defined an alarm sound
2009-12-21 08:01:49 -05:00
& & Settings . getProperty ( " davmail.caldavAlarmSound " ) ! = null ) {
2009-12-20 05:52:53 -05:00
// Convert alarm to audio for iCal
2009-11-27 04:34:20 -05:00
result . writeLine ( " ACTION:AUDIO " ) ;
if ( ! sound ) {
2009-12-20 05:52:53 -05:00
// Add defined sound into the audio alarm
result . writeLine ( " ATTACH;VALUE=URI: " + Settings . getProperty ( " davmail.caldavAlarmSound " ) ) ;
2009-11-27 04:34:20 -05:00
}
continue ;
} else if ( ! fromServer & & " AUDIO " . equals ( action ) ) {
// Use the alarm action that exchange (and blackberry) understand
// (exchange and blackberry don't understand audio actions)
result . writeLine ( " ACTION:DISPLAY " ) ;
continue ;
}
// Don't recognize this type of action: pass it through
} else if ( line . startsWith ( " CLASS: " ) ) {
if ( isAppleiCal ) {
continue ;
} else {
if ( " PRIVATE " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " X-CALENDARSERVER-ACCESS:CONFIDENTIAL " ) ;
} else if ( " CONFIDENTIAL " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " X-CALENDARSERVER-ACCESS:PRIVATE " ) ;
} else {
result . writeLine ( " X-CALENDARSERVER-ACCESS: " + eventClass ) ;
}
}
// remove organizer line if user is organizer for iPhone
} else if ( fromServer & & line . startsWith ( " ORGANIZER " ) & & ! hasAttendee ) {
continue ;
} else if ( organizer ! = null & & line . startsWith ( " ATTENDEE " ) & & line . contains ( organizer ) ) {
// Ignore organizer as attendee
continue ;
} else if ( ! fromServer & & line . startsWith ( " ATTENDEE " ) ) {
line = replaceIcal4Principal ( line ) ;
}
result . writeLine ( line ) ;
}
} finally {
reader . close ( ) ;
}
String resultString = result . toString ( ) ;
dumpICS ( resultString , fromServer , true ) ;
2010-03-21 16:40:14 -04:00
return resultString ;
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 ) ;
}
}
}
}
}
}
protected String getICSValue ( String icsBody , String prefix , String defval ) throws IOException {
// only return values in VEVENT section, not VALARM
Stack < String > sectionStack = new Stack < String > ( ) ;
BufferedReader reader = null ;
try {
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
if ( line . startsWith ( " BEGIN: " ) ) {
sectionStack . push ( line ) ;
} else if ( line . startsWith ( " END: " ) & & ! sectionStack . isEmpty ( ) ) {
sectionStack . pop ( ) ;
} else if ( ! sectionStack . isEmpty ( ) & & " BEGIN:VEVENT " . equals ( sectionStack . peek ( ) ) & & line . startsWith ( prefix ) ) {
return line . substring ( prefix . length ( ) ) ;
}
}
} finally {
if ( reader ! = null ) {
reader . close ( ) ;
}
}
return defval ;
}
protected String getICSSummary ( String icsBody ) throws IOException {
return getICSValue ( icsBody , " SUMMARY: " , BundleMessage . format ( " MEETING_REQUEST " ) ) ;
}
protected String getICSDescription ( String icsBody ) throws IOException {
return getICSValue ( icsBody , " DESCRIPTION: " , " " ) ;
}
class Participants {
String attendees ;
2009-12-24 10:33:26 -05:00
String optionalAttendees ;
2009-11-27 04:34:20 -05:00
String organizer ;
}
/ * *
* Parse ics event for attendees and organizer .
* For notifications , only include attendees with RSVP = TRUE or PARTSTAT = NEEDS - ACTION
*
* @param isNotification get only notified attendees
* @return participants
* @throws IOException on error
* /
protected Participants getParticipants ( boolean isNotification ) throws IOException {
HashSet < String > attendees = new HashSet < String > ( ) ;
2009-12-24 10:33:26 -05:00
HashSet < String > optionalAttendees = new HashSet < String > ( ) ;
2009-11-27 04:34:20 -05:00
String organizer = null ;
BufferedReader reader = null ;
try {
2010-05-07 04:30:12 -04:00
reader = new ICSBufferedReader ( new StringReader ( itemBody ) ) ;
2009-11-27 04:34:20 -05:00
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
int index = line . indexOf ( ':' ) ;
if ( index > = 0 ) {
String key = line . substring ( 0 , index ) ;
String value = line . substring ( index + 1 ) ;
int semiColon = key . indexOf ( ';' ) ;
if ( semiColon > = 0 ) {
key = key . substring ( 0 , semiColon ) ;
}
if ( " ORGANIZER " . equals ( key ) | | " ATTENDEE " . equals ( key ) ) {
int colonIndex = value . indexOf ( ':' ) ;
if ( colonIndex > = 0 ) {
value = value . substring ( colonIndex + 1 ) ;
}
value = replaceIcal4Principal ( value ) ;
if ( " ORGANIZER " . equals ( key ) ) {
organizer = value ;
// exclude current user and invalid values from recipients
// also exclude no action attendees
} else if ( ! email . equalsIgnoreCase ( value ) & & value . indexOf ( '@' ) > = 0
& & ( ! isNotification
| | line . indexOf ( " RSVP=TRUE " ) > = 0
2010-01-19 08:45:35 -05:00
| | line . indexOf ( " PARTSTAT=NEEDS-ACTION " ) > = 0
// need to include other PARTSTATs participants for CANCEL notifications
2010-01-21 05:49:23 -05:00
| | line . indexOf ( " PARTSTAT=ACCEPTED " ) > = 0
| | line . indexOf ( " PARTSTAT=DECLINED " ) > = 0
| | line . indexOf ( " PARTSTAT=TENTATIVE " ) > = 0
) ) {
2009-12-24 10:33:26 -05:00
if ( line . indexOf ( " ROLE=OPT-PARTICIPANT " ) > = 0 ) {
optionalAttendees . add ( value ) ;
} else {
attendees . add ( value ) ;
}
2009-11-27 04:34:20 -05:00
}
}
}
}
} finally {
if ( reader ! = null ) {
reader . close ( ) ;
}
}
Participants participants = new Participants ( ) ;
2009-12-24 10:33:26 -05:00
participants . attendees = StringUtil . join ( attendees , " , " ) ;
participants . optionalAttendees = StringUtil . join ( optionalAttendees , " , " ) ;
2009-11-27 04:34:20 -05:00
participants . organizer = organizer ;
return participants ;
}
protected String getICSMethod ( String icsBody ) {
String icsMethod = StringUtil . getToken ( icsBody , " METHOD: " , " \ r " ) ;
if ( icsMethod = = null ) {
// default method is REQUEST
icsMethod = " REQUEST " ;
}
return icsMethod ;
}
2010-05-07 04:30:12 -04:00
protected ItemResult createOrUpdate ( ) 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
int status = 0 ;
PutMethod putmethod = new PutMethod ( URIUtil . encodePath ( href ) ) ;
putmethod . setRequestHeader ( " Translate " , " f " ) ;
putmethod . setRequestHeader ( " Overwrite " , " f " ) ;
if ( etag ! = null ) {
putmethod . setRequestHeader ( " If-Match " , etag ) ;
}
if ( noneMatch ! = null ) {
putmethod . setRequestHeader ( " If-None-Match " , noneMatch ) ;
}
putmethod . setRequestHeader ( " Content-Type " , " message/rfc822 " ) ;
2010-05-07 04:30:12 -04:00
String method = getICSMethod ( itemBody ) ;
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-05-07 04:30:12 -04:00
writer . writeHeader ( " Subject " , getICSSummary ( itemBody ) ) ;
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
Participants participants = getParticipants ( true ) ;
if ( email . equalsIgnoreCase ( participants . organizer ) ) {
// current user is organizer => notify all
2009-12-24 10:33:26 -05:00
writer . writeHeader ( " To " , participants . attendees ) ;
writer . writeHeader ( " Cc " , participants . optionalAttendees ) ;
// do not send notification if no recipients found
2010-01-05 05:06:47 -05:00
if ( participants . attendees = = null & & participants . optionalAttendees = = null ) {
status = HttpStatus . SC_NO_CONTENT ;
}
2009-11-27 04:34:20 -05:00
} else {
// notify only organizer
2009-12-24 10:33:26 -05:00
writer . writeHeader ( " To " , participants . organizer ) ;
// do not send notification if no recipients found
2010-01-05 05:06:47 -05:00
if ( participants . organizer = = null ) {
status = HttpStatus . SC_NO_CONTENT ;
}
2009-11-27 04:34:20 -05:00
}
} else {
// need to parse attendees and organizer to build recipients
Participants participants = getParticipants ( false ) ;
// storing appointment, full recipients header
if ( participants . attendees ! = null ) {
2009-12-24 10:33:26 -05:00
writer . writeHeader ( " To " , participants . 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
}
2009-12-24 10:33:26 -05:00
writer . writeHeader ( " Cc " , participants . optionalAttendees ) ;
2009-11-27 04:34:20 -05:00
if ( participants . organizer ! = null ) {
2009-12-24 10:33:26 -05:00
writer . writeHeader ( " From " , participants . 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
}
// if not organizer, set REPLYTIME to force Outlook in attendee mode
if ( participants . organizer ! = null & & ! email . equalsIgnoreCase ( participants . organizer ) ) {
2010-05-07 04:30:12 -04:00
if ( itemBody . indexOf ( " METHOD: " ) < 0 ) {
itemBody = itemBody . replaceAll ( " BEGIN:VCALENDAR " , " BEGIN:VCALENDAR \ r \ nMETHOD:REQUEST " ) ;
2009-11-27 04:34:20 -05:00
}
2010-05-07 04:30:12 -04:00
if ( itemBody . indexOf ( " X-MICROSOFT-CDO-REPLYTIME " ) < 0 ) {
itemBody = itemBody . replaceAll ( " END:VEVENT " , " X-MICROSOFT-CDO-REPLYTIME: " +
2009-11-27 04:34:20 -05:00
getZuluDateFormat ( ) . format ( new Date ( ) ) + " \ r \ nEND:VEVENT " ) ;
}
}
}
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
// Write a part of the message that contains the
// ICS description so that invites contain the description text
2010-05-07 04:30:12 -04:00
String description = getICSDescription ( itemBody ) . replaceAll ( " \\ \\ [Nn] " , " \ r \ n " ) ;
2009-11-27 04:34:20 -05:00
if ( 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 " +
" \ tmethod= " + method + " ; \ 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-05-07 04:30:12 -04:00
baos . write ( fixICS ( itemBody , false ) . 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 ( ) ;
putmethod . setRequestEntity ( new ByteArrayRequestEntity ( baos . toByteArray ( ) , " message/rfc822 " ) ) ;
try {
if ( status = = 0 ) {
status = httpClient . executeMethod ( putmethod ) ;
if ( status = = HttpURLConnection . HTTP_OK ) {
if ( etag ! = null ) {
LOGGER . debug ( " Updated event " + href ) ;
} else {
LOGGER . warn ( " Overwritten event " + href ) ;
}
} else if ( status ! = HttpURLConnection . HTTP_CREATED ) {
LOGGER . warn ( " Unable to create or update message " + status + ' ' + putmethod . getStatusLine ( ) ) ;
}
}
} finally {
putmethod . releaseConnection ( ) ;
}
2010-05-07 04:30:12 -04:00
ItemResult itemResult = new ItemResult ( ) ;
2009-11-27 04:34:20 -05:00
// 440 means forbidden on Exchange
if ( status = = 440 ) {
status = HttpStatus . SC_FORBIDDEN ;
}
2010-05-07 04:30:12 -04:00
itemResult . status = status ;
2009-11-27 04:34:20 -05:00
if ( putmethod . getResponseHeader ( " GetETag " ) ! = null ) {
2010-05-07 04:30:12 -04:00
itemResult . etag = putmethod . getResponseHeader ( " GetETag " ) . getValue ( ) ;
2009-11-27 04:34:20 -05:00
}
// trigger activeSync push event, only if davmail.forceActiveSyncUpdate setting is true
if ( ( status = = HttpStatus . SC_OK | | status = = HttpStatus . SC_CREATED ) & &
( Settings . getBooleanProperty ( " davmail.forceActiveSyncUpdate " ) ) ) {
ArrayList < DavProperty > propertyList = new ArrayList < DavProperty > ( ) ;
2009-11-27 18:27:50 -05:00
// Set contentclass to make ActiveSync happy
2010-05-07 04:30:12 -04:00
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " contentclass " , DAV ) , contentClass ) ) ;
2009-11-27 18:27:50 -05:00
// ... but also set PR_INTERNET_CONTENT to preserve custom properties
propertyList . add ( new DefaultDavProperty ( PR_INTERNET_CONTENT , new String ( Base64 . encodeBase64 ( baos . toByteArray ( ) ) ) ) ) ;
2009-11-27 04:34:20 -05:00
PropPatchMethod propPatchMethod = new PropPatchMethod ( URIUtil . encodePath ( href ) , propertyList ) ;
int patchStatus = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , propPatchMethod ) ;
if ( patchStatus ! = HttpStatus . SC_MULTI_STATUS ) {
LOGGER . warn ( " Unable to patch event to trigger activeSync push " ) ;
2009-11-27 18:27:50 -05:00
} else {
// need to retrieve new etag
2010-05-06 15:26:44 -04:00
Item newItem = getItem ( href ) ;
2010-05-07 04:30:12 -04:00
itemResult . etag = newItem . etag ;
2009-11-27 04:34:20 -05:00
}
}
2010-05-07 04:30:12 -04:00
return itemResult ;
2009-11-27 04:34:20 -05:00
}
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
* /
public List < Contact > getAllContacts ( String folderPath ) throws IOException {
String searchQuery = " Select \" DAV:getetag \" , \" http://schemas.microsoft.com/exchange/permanenturl \" , \" DAV:displayname \" " +
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderPath + " \" ') \ n " +
" WHERE \" DAV:contentclass \" = 'urn:content-classes:person' \ n " ;
return getContacts ( folderPath , searchQuery ) ;
}
/ * *
* Search contacts in provided folder matching the search query .
*
* @param folderPath Exchange folder path
* @param searchQuery Exchange search query
* @return list of contacts
* @throws IOException on error
* /
protected List < Contact > getContacts ( String folderPath , String searchQuery ) throws IOException {
List < Contact > contacts = new ArrayList < Contact > ( ) ;
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod ( httpClient , URIUtil . encodePath ( folderPath ) , searchQuery ) ;
for ( MultiStatusResponse response : responses ) {
contacts . add ( new Contact ( response ) ) ;
}
return contacts ;
}
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 {
2009-08-29 16:20:44 -04:00
List < Event > result ;
try {
2010-03-29 06:45:10 -04:00
String scheduleStatePropertyName = scheduleStateProperty . getNamespace ( ) . getURI ( ) + scheduleStateProperty . getName ( ) ;
2010-04-12 04:54:53 -04:00
String searchQuery = " Select \" DAV:getetag \" , \" http://schemas.microsoft.com/exchange/permanenturl \" , \" urn:schemas:calendar:instancetype \" , \" DAV:displayname \" " +
2009-08-29 16:20:44 -04:00
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderPath + " \" ') \ n " +
" WHERE \" DAV:contentclass \" = 'urn:content-classes:calendarmessage' \ n " +
2010-03-30 16:46:57 -04:00
" AND ( \" " + scheduleStatePropertyName + " \" IS NULL OR NOT \" " + scheduleStatePropertyName + " \" = 'CALDAV:schedule-processed') \ n " +
2009-08-29 16:20:44 -04:00
" ORDER BY \" urn:schemas:calendar:dtstart \" DESC \ n " ;
result = getEvents ( folderPath , searchQuery ) ;
} catch ( HttpException e ) {
2009-10-16 04:58:04 -04:00
// failover to DAV:comment property on some Exchange servers
2009-08-29 16:20:44 -04:00
if ( DEFAULT_SCHEDULE_STATE_PROPERTY . equals ( scheduleStateProperty ) ) {
2010-05-07 04:30:12 -04:00
scheduleStateProperty = DavPropertyName . create ( " comment " , DAV ) ;
2009-08-29 16:20:44 -04:00
result = getEventMessages ( folderPath ) ;
} else {
throw e ;
}
}
return result ;
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 " ) ;
2008-12-05 05:45:23 -05:00
String dateCondition = " " ;
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 ) ;
2009-04-07 08:37:28 -04:00
dateCondition = " AND \" urn:schemas:calendar:dtstart \" > ' " + formatSearchDate ( cal . getTime ( ) ) + " ' \ n " ;
2008-12-05 05:45:23 -05:00
}
2010-03-29 04:50:39 -04:00
String privateCondition = " " ;
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 " ) ;
privateCondition = " AND \" http://schemas.microsoft.com/exchange/sensitivity \" = 0 \ n " ;
}
2010-04-12 04:54:53 -04:00
String searchQuery = " Select \" DAV:getetag \" , \" http://schemas.microsoft.com/exchange/permanenturl \" , \" urn:schemas:calendar:instancetype \" , \" DAV:displayname \" " +
2009-04-14 19:46:37 -04:00
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderPath + " \" ') \ n " +
2009-03-22 15:22:27 -04:00
" WHERE ( " +
2009-11-22 16:52:53 -05:00
// VTODO events have a null instancetype
" \" urn:schemas:calendar:instancetype \" is null OR " +
2009-03-22 15:22:27 -04:00
" \" urn:schemas:calendar:instancetype \" = 1 \ n " +
2009-01-08 05:35:06 -05:00
" OR ( \" urn:schemas:calendar:instancetype \" = 0 \ n " +
2008-12-05 05:45:23 -05:00
dateCondition +
2009-01-08 05:35:06 -05:00
" )) AND \" DAV:contentclass \" = 'urn:content-classes:appointment' \ n " +
2010-03-29 04:50:39 -04:00
privateCondition +
2009-02-20 12:44:30 -05:00
" ORDER BY \" urn:schemas:calendar:dtstart \" DESC \ n " ;
2009-04-14 19:46:37 -04:00
return getEvents ( folderPath , searchQuery ) ;
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
*
* @param folderPath Exchange folder path
2009-08-13 04:34:51 -04:00
* @param searchQuery Exchange search query
* @return list of calendar messages as Event objects
* @throws IOException on error
* /
protected List < Event > getEvents ( String folderPath , String searchQuery ) throws IOException {
2009-02-20 12:44:30 -05:00
List < Event > events = new ArrayList < Event > ( ) ;
2009-08-13 04:34:51 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod ( httpClient , URIUtil . encodePath ( folderPath ) , searchQuery ) ;
2009-04-01 11:51:12 -04:00
for ( MultiStatusResponse response : responses ) {
2009-12-21 08:01:49 -05:00
String instancetype = getPropertyIfExists ( response . getProperties ( HttpStatus . SC_OK ) , " instancetype " , Namespace . getNamespace ( " urn:schemas:calendar: " ) ) ;
2010-05-06 15:26:44 -04:00
Event event = new Event ( response ) ;
2010-03-29 04:50:39 -04:00
//noinspection VariableNotUsedInsideIf
2010-03-21 16:40:14 -04:00
if ( instancetype = = null ) {
2010-01-14 04:46:48 -05:00
// check ics content
try {
2010-05-06 15:26:44 -04:00
event . getBody ( ) ;
// getBody success => add event or task
2010-04-23 11:16:05 -04:00
events . add ( event ) ;
2010-02-19 18:21:16 -05:00
} catch ( HttpException e ) {
2010-01-14 04:46:48 -05:00
// invalid event: exclude from list
2010-04-23 09:42:37 -04:00
LOGGER . warn ( " Invalid event " + event . displayName + " found at " + response . getHref ( ) , e ) ;
2010-01-14 04:46:48 -05:00
}
2009-12-21 08:01:49 -05:00
} else {
events . add ( event ) ;
}
2008-11-26 19:56:28 -05:00
}
return events ;
}
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-05-06 15:26:44 -04:00
public Item getItem ( String folderPath , String itemName ) throws IOException {
2010-05-07 04:30:12 -04:00
String itemPath ;
// convert vcf extension to EML
if ( itemName . endsWith ( " .vcf " ) ) {
itemPath = folderPath + '/' + itemName . substring ( 0 , itemName . length ( ) - 3 ) + " EML " ;
} else {
itemPath = folderPath + '/' + itemName ;
}
2010-05-06 15:26:44 -04:00
Item item ;
2009-11-27 18:27:50 -05:00
try {
2010-05-06 15:26:44 -04:00
item = getItem ( itemPath ) ;
2009-11-27 18:27:50 -05:00
} catch ( HttpNotFoundException hnfe ) {
// failover for Exchange 2007 plus encoding issue
2010-05-06 15:26:44 -04:00
String decodedEventName = itemName . replaceAll ( " _xF8FF_ " , " / " ) . replaceAll ( " _x003F_ " , " ? " ) . replaceAll ( " ' " , " '' " ) ;
2009-11-27 18:27:50 -05:00
ExchangeSession . MessageList messages = searchMessages ( folderPath , " AND \" DAV:displayname \" =' " + decodedEventName + '\'' ) ;
if ( ! messages . isEmpty ( ) ) {
2010-05-06 15:26:44 -04:00
item = getItem ( messages . get ( 0 ) . getPermanentUrl ( ) ) ;
2009-11-27 18:27:50 -05:00
} else {
throw hnfe ;
}
}
2010-05-06 15:26:44 -04:00
return item ;
2009-09-29 07:03:15 -04:00
}
2009-11-17 18:11:08 -05:00
/ * *
2010-05-06 15:26:44 -04:00
* Get item by url
2009-11-17 18:11:08 -05:00
*
2010-05-06 15:26:44 -04:00
* @param itemPath Event path
2009-11-17 18:11:08 -05:00
* @return event object
* @throws IOException on error
* /
2010-05-06 15:26:44 -04:00
public Item getItem ( String itemPath ) throws IOException {
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executePropFindMethod ( httpClient , URIUtil . encodePath ( itemPath ) , 0 , EVENT_REQUEST_PROPERTIES ) ;
2009-04-01 11:51:12 -04:00
if ( responses . length = = 0 ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_EVENT_NOT_FOUND " ) ;
2008-11-26 19:56:28 -05:00
}
2010-05-07 04:30:12 -04:00
String contentClass = getPropertyIfExists ( responses [ 0 ] . getProperties ( HttpStatus . SC_OK ) ,
" contentclass " , DAV ) ;
2010-05-06 15:26:44 -04:00
if ( " urn:content-classes:person " . equals ( contentClass ) ) {
return new Contact ( responses [ 0 ] ) ;
2010-05-07 04:30:12 -04:00
} else if ( " urn:content-classes:appointment " . equals ( contentClass ) ) {
2010-05-06 15:26:44 -04:00
return new Event ( responses [ 0 ] ) ;
} else {
throw new DavMailException ( " EXCEPTION_EVENT_NOT_FOUND " ) ;
}
2008-12-17 10:27:56 -05:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Delete event 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
2009-08-21 05:58:19 -04:00
* @param eventName event name
2009-08-13 04:34:51 -04:00
* @return HTTP status
* @throws IOException on error
* /
public int deleteEvent ( String folderPath , String eventName ) throws IOException {
String eventPath = URIUtil . encodePath ( folderPath + '/' + eventName ) ;
int status ;
if ( inboxUrl . endsWith ( folderPath ) ) {
// do not delete calendar messages, mark read and processed
ArrayList < DavProperty > list = new ArrayList < DavProperty > ( ) ;
2009-08-29 16:20:44 -04:00
list . add ( new DefaultDavProperty ( scheduleStateProperty , " CALDAV:schedule-processed " ) ) ;
2009-08-13 04:34:51 -04:00
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " read " , URN_SCHEMAS_HTTPMAIL ) , " 1 " ) ) ;
PropPatchMethod patchMethod = new PropPatchMethod ( eventPath , list ) ;
DavGatewayHttpClientFacade . executeMethod ( httpClient , patchMethod ) ;
status = HttpStatus . SC_OK ;
} else {
status = DavGatewayHttpClientFacade . executeDeleteMethod ( httpClient , eventPath ) ;
}
return status ;
2008-11-26 19:56:28 -05: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
* /
2009-02-09 05:12:09 -05:00
public int sendEvent ( String icsBody ) throws IOException {
2009-11-16 10:24:54 -05:00
String messageUrl = draftsUrl + '/' + UUID . randomUUID ( ) . toString ( ) + " .EML " ;
2009-02-12 06:33:19 -05:00
int status = internalCreateOrUpdateEvent ( messageUrl , " urn:content-classes:calendarmessage " , icsBody , null , null ) . status ;
2009-02-09 05:12:09 -05:00
if ( status ! = HttpStatus . SC_CREATED ) {
return status ;
} else {
2009-04-01 11:51:12 -04:00
MoveMethod method = new MoveMethod ( URIUtil . encodePath ( messageUrl ) , URIUtil . encodePath ( sendmsgUrl ) , true ) ;
2009-03-18 13:26:33 -04:00
status = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , method ) ;
if ( status ! = HttpStatus . SC_OK ) {
throw DavGatewayHttpClientFacade . buildHttpException ( method ) ;
2009-02-09 05:12:09 -05:00
}
2009-03-18 13:26:33 -04:00
return status ;
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 {
String messageUrl = folderPath + '/' + itemName ;
if ( itemBody . startsWith ( " BEGIN:VCALENDAR " ) ) {
return internalCreateOrUpdateEvent ( messageUrl , " urn:content-classes:appointment " , itemBody , etag , noneMatch ) ;
} else if ( itemBody . startsWith ( " BEGIN:VCARD " ) ) {
return internalCreateOrUpdateContact ( messageUrl , " urn:content-classes:person " , itemBody , etag , noneMatch ) ;
} else {
throw new IOException ( BundleMessage . format ( " EXCEPTION_INVALID_MESSAGE_CONTENT " , itemBody ) ) ;
}
}
protected ItemResult internalCreateOrUpdateContact ( String messageUrl , String contentClass , String icsBody , String etag , String noneMatch ) throws IOException {
Contact contact = new Contact ( messageUrl , contentClass , icsBody , etag , noneMatch ) ;
return contact . createOrUpdate ( ) ;
2009-02-09 05:12:09 -05:00
}
2010-05-07 04:30:12 -04:00
protected ItemResult internalCreateOrUpdateEvent ( String messageUrl , String contentClass , String icsBody , String etag , String noneMatch ) throws IOException {
Event event = new Event ( messageUrl , contentClass , icsBody , etag , noneMatch ) ;
2009-11-27 04:34:20 -05:00
return event . createOrUpdate ( ) ;
2008-11-26 19:56:28 -05:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Get folder ctag ( change tag ) .
* This flag changes whenever folder or folder content changes
*
2009-08-21 05:58:19 -04:00
* @param folderPath Exchange folder path
2009-08-13 04:34:51 -04:00
* @return folder ctag
* @throws IOException on error
* /
2009-04-15 18:05:11 -04:00
public String getFolderCtag ( String folderPath ) throws IOException {
return getFolderProperty ( folderPath , CONTENT_TAG ) ;
2009-03-03 06:09:07 -05:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Get folder resource tag .
* Same as etag for folders , changes when folder ( not content ) changes
*
* @param folderPath Exchange folder path
* @return folder resource tag
* @throws IOException on error
* /
2009-04-15 18:05:11 -04:00
public String getFolderResourceTag ( String folderPath ) throws IOException {
return getFolderProperty ( folderPath , RESOURCE_TAG ) ;
2009-03-03 06:09:07 -05:00
}
2009-08-13 04:34:51 -04:00
protected String getFolderProperty ( String folderPath , DavPropertyNameSet davPropertyNameSet ) throws IOException {
2009-04-15 18:05:11 -04:00
String result ;
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executePropFindMethod (
2009-04-15 18:05:11 -04:00
httpClient , URIUtil . encodePath ( folderPath ) , 0 , davPropertyNameSet ) ;
2009-04-01 11:51:12 -04:00
if ( responses . length = = 0 ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_GET_FOLDER " , folderPath ) ;
2008-11-26 19:56:28 -05:00
}
2009-04-01 19:23:14 -04:00
DavPropertySet properties = responses [ 0 ] . getProperties ( HttpStatus . SC_OK ) ;
2009-04-15 18:05:11 -04:00
DavPropertyName davPropertyName = davPropertyNameSet . iterator ( ) . nextPropertyName ( ) ;
result = getPropertyIfExists ( properties , davPropertyName ) ;
if ( result = = null ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_UNABLE_TO_GET_PROPERTY " , davPropertyName ) ;
2008-11-26 19:56:28 -05:00
}
2009-04-15 18:05:11 -04:00
return result ;
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
}
/ * *
* Get current Exchange alias name from mailbox name
2008-11-26 19:56:28 -05:00
*
* @return user name
* /
2009-05-07 04:45:00 -04:00
protected String getAliasFromMailPath ( ) {
2008-12-25 17:16:24 -05:00
if ( mailPath = = null ) {
2009-03-09 11:21:03 -04:00
return null ;
2009-01-08 04:58:31 -05:00
}
2009-04-16 17:16:40 -04:00
int index = mailPath . lastIndexOf ( '/' , mailPath . length ( ) - 2 ) ;
2009-01-08 04:58:31 -05:00
if ( index > = 0 & & mailPath . endsWith ( " / " ) ) {
return mailPath . substring ( index + 1 , mailPath . length ( ) - 1 ) ;
2008-11-26 19:56:28 -05:00
} else {
2009-05-07 04:45:00 -04:00
LOGGER . warn ( new BundleMessage ( " EXCEPTION_INVALID_MAIL_PATH " , mailPath ) ) ;
return null ;
2008-11-26 19:56:28 -05:00
}
}
2009-08-13 04:34:51 -04:00
/ * *
* Get user alias from mailbox display name over Webdav .
2009-08-21 05:58:19 -04:00
*
2009-08-13 04:34:51 -04:00
* @return user alias
* /
2009-05-07 04:45:00 -04:00
public String getAliasFromMailboxDisplayName ( ) {
2009-03-11 08:39:14 -04:00
if ( mailPath = = null ) {
return null ;
}
2009-05-07 04:45:00 -04:00
String displayName = null ;
try {
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executePropFindMethod (
httpClient , URIUtil . encodePath ( mailPath ) , 0 , DISPLAY_NAME ) ;
if ( responses . length = = 0 ) {
2009-09-20 10:10:54 -04:00
LOGGER . warn ( new BundleMessage ( " EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER " , mailPath ) ) ;
2009-05-07 04:45:00 -04:00
} else {
2010-05-07 04:30:12 -04:00
displayName = getPropertyIfExists ( responses [ 0 ] . getProperties ( HttpStatus . SC_OK ) , " displayname " , DAV ) ;
2009-05-07 04:45:00 -04:00
}
} catch ( IOException e ) {
2009-09-20 10:10:54 -04:00
LOGGER . warn ( new BundleMessage ( " EXCEPTION_UNABLE_TO_GET_MAIL_FOLDER " , mailPath ) ) ;
2009-03-11 08:39:14 -04:00
}
2009-04-16 17:16:40 -04:00
return displayName ;
2009-03-25 17:59:27 -04:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Build Caldav calendar path for principal and folder name .
* - prefix is current user mailbox path if principal is current user ,
2009-08-21 05:58:19 -04:00
* else prefix is parent folder of current user mailbox path followed by principal
2009-08-13 04:34:51 -04:00
* - suffix according to well known folder names ( internationalized on Exchange )
*
2009-08-21 05:58:19 -04:00
* @param principal calendar principal
2009-08-13 04:34:51 -04:00
* @param folderName requested folder name
* @return Exchange folder path
* @throws IOException on error
* /
2009-04-14 19:46:37 -04:00
public String buildCalendarPath ( String principal , String folderName ) throws IOException {
StringBuilder buffer = new StringBuilder ( ) ;
2009-09-10 04:06:15 -04:00
// other user calendar => replace principal folder name in mailPath
2010-04-14 16:08:33 -04:00
if ( principal ! = null & & ! alias . equalsIgnoreCase ( principal ) & & ! email . equalsIgnoreCase ( principal ) ) {
2009-10-06 17:12:26 -04:00
LOGGER . debug ( " Detected shared calendar path for principal " + principal + " , user principal is " + email ) ;
2009-04-16 17:16:40 -04:00
int index = mailPath . lastIndexOf ( '/' , mailPath . length ( ) - 2 ) ;
2009-04-14 19:46:37 -04:00
if ( index > = 0 & & mailPath . endsWith ( " / " ) ) {
2009-04-23 16:53:22 -04:00
buffer . append ( mailPath . substring ( 0 , index + 1 ) ) . append ( principal ) . append ( '/' ) ;
2009-04-14 19:46:37 -04:00
} else {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_INVALID_MAIL_PATH " , mailPath ) ;
2009-04-14 19:46:37 -04:00
}
} else if ( principal ! = null ) {
buffer . append ( mailPath ) ;
}
2009-09-10 04:06:15 -04:00
if ( folderName ! = null & & folderName . startsWith ( " calendar " ) ) {
// replace 'calendar' folder name with i18n name
2009-04-16 17:16:40 -04:00
buffer . append ( calendarUrl . substring ( calendarUrl . lastIndexOf ( '/' ) + 1 ) ) ;
2009-09-10 04:06:15 -04:00
// sub calendar folder => append sub folder name
int index = folderName . indexOf ( '/' ) ;
if ( index > = 0 ) {
2009-09-14 17:16:15 -04:00
buffer . append ( folderName . substring ( index ) ) ;
2009-09-10 04:06:15 -04:00
}
// replace 'inbox' folder name with i18n name
2009-04-14 19:46:37 -04:00
} else if ( " inbox " . equals ( folderName ) ) {
2009-04-16 17:16:40 -04:00
buffer . append ( inboxUrl . substring ( inboxUrl . lastIndexOf ( '/' ) + 1 ) ) ;
2009-09-10 04:06:15 -04:00
// append folder name without replace (public folder)
2009-04-14 19:46:37 -04:00
} else if ( folderName ! = null & & folderName . length ( ) > 0 ) {
buffer . append ( folderName ) ;
}
return buffer . toString ( ) ;
}
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
* /
public boolean isSharedFolder ( String folderPath ) {
2010-04-14 16:08:33 -04:00
return ! folderPath . toLowerCase ( ) . startsWith ( mailPath . toLowerCase ( ) ) ;
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 ;
}
}
2009-08-13 04:34:51 -04:00
/ * *
* Get user email from global address list ( galfind ) .
*
* @param alias user alias
* @return user email
* /
2009-05-07 04:45:00 -04:00
public String getEmail ( String alias ) {
2009-01-08 04:58:31 -05:00
String emailResult = null ;
2009-03-09 11:21:03 -04:00
if ( alias ! = null ) {
2009-05-07 04:45:00 -04:00
GetMethod getMethod = null ;
String path = null ;
2009-03-09 11:21:03 -04:00
try {
2009-05-07 04:45:00 -04:00
path = getCmdBasePath ( ) + " ?Cmd=galfind&AN= " + URIUtil . encodeWithinQuery ( alias ) ;
getMethod = new GetMethod ( path ) ;
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , getMethod , true ) ;
2009-03-09 11:21:03 -04:00
Map < String , Map < String , String > > results = XMLStreamUtil . getElementContentsAsMap ( getMethod . getResponseBodyAsStream ( ) , " item " , " AN " ) ;
Map < String , String > result = results . get ( alias . toLowerCase ( ) ) ;
if ( result ! = null ) {
emailResult = result . get ( " EM " ) ;
}
2009-05-07 04:45:00 -04:00
} catch ( IOException e ) {
LOGGER . debug ( " GET " + path + " failed: " + e + ' ' + e . getMessage ( ) ) ;
2009-03-09 11:21:03 -04:00
} finally {
2009-05-07 04:45:00 -04:00
if ( getMethod ! = null ) {
getMethod . releaseConnection ( ) ;
}
2009-03-09 11:21:03 -04:00
}
2008-11-26 19:56:28 -05:00
}
2009-01-08 04:58:31 -05:00
return emailResult ;
}
2009-08-13 04:34:51 -04:00
/ * *
* Determine user email through various means .
*
2010-04-23 09:42:37 -04:00
* @param hostName Exchange server host name for last failover
2009-08-13 04:34:51 -04:00
* /
2010-04-20 11:01:22 -04:00
public void buildEmail ( String hostName ) {
2009-01-08 04:58:31 -05:00
// first try to get email from login name
2009-03-13 07:11:40 -04:00
alias = getAliasFromLogin ( ) ;
email = getEmail ( alias ) ;
2009-01-08 04:58:31 -05:00
// failover: use mailbox name as alias
2008-11-26 19:56:28 -05:00
if ( email = = null ) {
2009-03-13 07:11:40 -04:00
alias = getAliasFromMailPath ( ) ;
email = getEmail ( alias ) ;
2008-11-26 19:56:28 -05:00
}
2009-03-11 08:39:14 -04:00
// another failover : get alias from mailPath display name
if ( email = = null ) {
2009-03-13 07:11:40 -04:00
alias = getAliasFromMailboxDisplayName ( ) ;
email = getEmail ( alias ) ;
2009-03-11 08:39:14 -04:00
}
2009-01-08 04:58:31 -05:00
if ( email = = null ) {
2009-03-09 11:21:03 -04:00
// failover : get email from Exchange 2007 Options page
2010-04-20 11:01:22 -04:00
alias = getAliasFromOptions ( ) ;
2009-03-13 07:11:40 -04:00
email = getEmail ( alias ) ;
2009-05-19 06:42:56 -04:00
// failover: get email from options
if ( alias ! = null & & email = = null ) {
2010-04-20 11:01:22 -04:00
email = getEmailFromOptions ( ) ;
2009-05-19 06:42:56 -04:00
}
2009-03-09 11:21:03 -04:00
}
if ( email = = null ) {
2009-04-08 10:51:15 -04:00
LOGGER . debug ( " Unable to get user email with alias " + getAliasFromLogin ( )
2009-03-09 11:21:03 -04:00
+ " or " + getAliasFromMailPath ( )
2010-04-20 11:01:22 -04:00
+ " or " + getAliasFromOptions ( )
2009-03-09 11:21:03 -04:00
) ;
2009-04-08 10:51:15 -04:00
// last failover: build email from domain name and mailbox display name
StringBuilder buffer = new StringBuilder ( ) ;
// most reliable alias
alias = getAliasFromMailboxDisplayName ( ) ;
if ( alias = = null ) {
alias = getAliasFromLogin ( ) ;
}
if ( alias ! = null ) {
buffer . append ( alias ) ;
2009-12-21 10:02:16 -05:00
if ( alias . indexOf ( '@' ) < 0 ) {
buffer . append ( '@' ) ;
int dotIndex = hostName . indexOf ( '.' ) ;
if ( dotIndex > = 0 ) {
buffer . append ( hostName . substring ( dotIndex + 1 ) ) ;
}
2009-04-08 10:51:15 -04:00
}
}
email = buffer . toString ( ) ;
2009-03-09 11:21:03 -04:00
}
}
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 ) {
int start = line . toLowerCase ( ) . indexOf ( '[' ) + 1 ;
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
/ * *
* Search users in global address book
*
2008-12-05 09:53:38 -05:00
* @param searchAttribute exchange search attribute
* @param searchValue search value
2008-12-03 06:38:35 -05:00
* @return List of users
2009-04-16 18:20:30 -04:00
* @throws IOException on error
2008-12-03 06:38:35 -05:00
* /
public Map < String , Map < String , String > > galFind ( String searchAttribute , String searchValue ) throws IOException {
2008-12-05 09:53:38 -05:00
Map < String , Map < String , String > > results ;
2009-04-28 17:01:40 -04:00
GetMethod getMethod = new GetMethod ( URIUtil . encodePathQuery ( getCmdBasePath ( ) + " ?Cmd=galfind& " + searchAttribute + '=' + searchValue ) ) ;
2008-12-03 06:38:35 -05:00
try {
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , getMethod , true ) ;
2008-12-03 06:38:35 -05:00
results = XMLStreamUtil . getElementContentsAsMap ( getMethod . getResponseBodyAsStream ( ) , " item " , " AN " ) ;
} finally {
getMethod . releaseConnection ( ) ;
}
2009-04-23 16:53:22 -04:00
LOGGER . debug ( " galfind " + searchAttribute + '=' + searchValue + " : " + results . size ( ) + " result(s) " ) ;
2008-12-03 06:38:35 -05:00
return results ;
}
2009-10-06 17:12:26 -04:00
/ * *
* Search users in contacts folder by uid .
*
* @param uid unique id
* @return List of users
* @throws IOException on error
* /
public Map < String , Map < String , String > > contactFindByUid ( String uid ) throws IOException {
return contactFind ( DAV_UID_FILTER + uid + '\'' ) ;
}
static final String DAV_UID_FILTER = " \" DAV:uid \" =' " ;
2009-08-29 19:49:45 -04:00
/ * *
* Search users in contacts folder
*
* @param searchFilter search filter
* @return List of users
* @throws IOException on error
* /
public Map < String , Map < String , String > > contactFind ( String searchFilter ) throws IOException {
2009-10-06 17:12:26 -04:00
// uid value in search filter (hex value)
String filterUid = null ;
// base64 encoded uid value
String actualFilterUid = null ;
// replace hex encoded uid with base64 uid
if ( searchFilter ! = null ) {
int uidStart = searchFilter . indexOf ( DAV_UID_FILTER ) ;
if ( uidStart > = 0 ) {
int uidEnd = searchFilter . indexOf ( '\'' , uidStart + DAV_UID_FILTER . length ( ) ) ;
if ( uidEnd > = 0 ) {
try {
filterUid = searchFilter . substring ( uidStart + DAV_UID_FILTER . length ( ) , uidEnd ) ;
actualFilterUid = new String ( Base64 . encodeBase64 ( Hex . decodeHex ( filterUid . toCharArray ( ) ) ) ) ;
searchFilter = searchFilter . substring ( 0 , uidStart + DAV_UID_FILTER . length ( ) ) + actualFilterUid + searchFilter . substring ( uidEnd ) ;
} catch ( DecoderException e ) {
// ignore, this is not an hex uid
}
}
}
}
2009-08-29 19:49:45 -04:00
StringBuilder searchRequest = new StringBuilder ( ) ;
2009-09-03 19:08:35 -04:00
searchRequest . append ( " Select \" DAV:uid \" , " +
" \" http://schemas.microsoft.com/exchange/extensionattribute1 \" , " +
" \" http://schemas.microsoft.com/exchange/extensionattribute2 \" , " +
" \" http://schemas.microsoft.com/exchange/extensionattribute3 \" , " +
" \" http://schemas.microsoft.com/exchange/extensionattribute4 \" , " +
" \" urn:schemas:contacts:bday \" , " +
" \" urn:schemas:contacts:businesshomepage \" , " +
" \" urn:schemas:contacts:c \" , " +
" \" urn:schemas:contacts:cn \" , " +
" \" urn:schemas:contacts:co \" , " +
" \" urn:schemas:contacts:department \" , " +
" \" urn:schemas:contacts:email1 \" , " +
" \" urn:schemas:contacts:email2 \" , " +
" \" urn:schemas:contacts:facsimiletelephonenumber \" , " +
" \" urn:schemas:contacts:givenName \" , " +
" \" urn:schemas:contacts:homeCity \" , " +
" \" urn:schemas:contacts:homeCountry \" , " +
" \" urn:schemas:contacts:homePhone \" , " +
" \" urn:schemas:contacts:homePostalCode \" , " +
" \" urn:schemas:contacts:homeState \" , " +
" \" urn:schemas:contacts:homeStreet \" , " +
" \" urn:schemas:contacts:l \" , " +
" \" urn:schemas:contacts:manager \" , " +
" \" urn:schemas:contacts:mobile \" , " +
" \" urn:schemas:contacts:namesuffix \" , " +
" \" urn:schemas:contacts:nickname \" , " +
" \" urn:schemas:contacts:o \" , " +
" \" urn:schemas:contacts:pager \" , " +
" \" urn:schemas:contacts:personaltitle \" , " +
" \" urn:schemas:contacts:postalcode \" , " +
" \" urn:schemas:contacts:postofficebox \" , " +
" \" urn:schemas:contacts:profession \" , " +
" \" urn:schemas:contacts:roomnumber \" , " +
" \" urn:schemas:contacts:secretarycn \" , " +
" \" urn:schemas:contacts:sn \" , " +
" \" urn:schemas:contacts:spousecn \" , " +
" \" urn:schemas:contacts:st \" , " +
" \" urn:schemas:contacts:street \" , " +
" \" urn:schemas:contacts:telephoneNumber \" , " +
" \" urn:schemas:contacts:title \" , " +
" \" urn:schemas:httpmail:textdescription \" " )
2009-09-07 08:24:50 -04:00
. append ( " FROM Scope('SHALLOW TRAVERSAL OF \" " ) . append ( contactsUrl ) . append ( " \" ') \ n " )
. append ( " WHERE \" DAV:contentclass \" = 'urn:content-classes:person' \ n " ) ;
2009-09-04 18:30:03 -04:00
if ( searchFilter ! = null & & searchFilter . length ( ) > 0 ) {
2009-09-07 08:24:50 -04:00
searchRequest . append ( " AND " ) . append ( searchFilter ) ;
2009-08-29 19:49:45 -04:00
}
2009-10-06 17:12:26 -04:00
LOGGER . debug ( " contactFind: " + searchRequest ) ;
2009-08-29 19:49:45 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executeSearchMethod (
httpClient , URIUtil . encodePath ( contactsUrl ) , searchRequest . toString ( ) ) ;
Map < String , Map < String , String > > results = new HashMap < String , Map < String , String > > ( ) ;
Map < String , String > item ;
for ( MultiStatusResponse response : responses ) {
2009-09-03 05:48:58 -04:00
item = new HashMap < String , String > ( ) ;
2009-08-29 19:49:45 -04:00
DavPropertySet properties = response . getProperties ( HttpStatus . SC_OK ) ;
2009-09-03 05:48:58 -04:00
DavPropertyIterator propertiesIterator = properties . iterator ( ) ;
while ( propertiesIterator . hasNext ( ) ) {
DavProperty property = propertiesIterator . nextProperty ( ) ;
String propertyName = property . getName ( ) . getName ( ) ;
String propertyValue = ( String ) property . getValue ( ) ;
2009-10-06 17:12:26 -04:00
if ( " uid " . equals ( propertyName ) ) {
// uid is base64, reencode to hex
propertyValue = new String ( Hex . encodeHex ( Base64 . decodeBase64 ( propertyValue . getBytes ( ) ) ) ) ;
// if actualFilterUid is not null, exclude non exact match
if ( actualFilterUid ! = null & & ! filterUid . equals ( propertyValue ) ) {
propertyValue = null ;
}
} else if ( propertyName . startsWith ( " email " ) ) {
2009-09-03 05:48:58 -04:00
if ( propertyValue ! = null & & propertyValue . startsWith ( " \" " ) ) {
int endIndex = propertyValue . indexOf ( '\"' , 1 ) ;
if ( endIndex > 0 ) {
propertyValue = propertyValue . substring ( 1 , endIndex ) ;
}
}
2009-09-03 19:08:35 -04:00
} else if ( " bday " . equals ( propertyName ) ) {
SimpleDateFormat parser = getExchangeZuluDateFormatMillisecond ( ) ;
try {
Calendar calendar = Calendar . getInstance ( ) ;
calendar . setTime ( parser . parse ( propertyValue ) ) ;
item . put ( " birthday " , String . valueOf ( calendar . get ( Calendar . DAY_OF_MONTH ) ) ) ;
2009-09-09 17:50:11 -04:00
item . put ( " birthmonth " , String . valueOf ( calendar . get ( Calendar . MONTH ) + 1 ) ) ;
2009-09-03 19:08:35 -04:00
item . put ( " birthyear " , String . valueOf ( calendar . get ( Calendar . YEAR ) ) ) ;
propertyValue = null ;
} catch ( ParseException e ) {
throw new IOException ( e ) ;
}
2009-09-10 04:47:01 -04:00
} else if ( " textdescription " . equals ( propertyName ) & & " \ n " . equals ( propertyValue ) ) {
propertyValue = null ;
2009-09-03 19:08:35 -04:00
}
if ( propertyValue ! = null & & propertyValue . length ( ) > 0 ) {
item . put ( propertyName , propertyValue ) ;
2009-08-29 19:49:45 -04:00
}
}
2009-10-06 17:12:26 -04:00
if ( item . get ( " uid " ) ! = null ) {
results . put ( item . get ( " uid " ) , item ) ;
}
2009-08-29 19:49:45 -04:00
}
2009-09-09 17:50:11 -04:00
LOGGER . debug ( " contactFind " + ( ( searchFilter = = null ) ? " " : searchFilter ) + " : " + results . size ( ) + " result(s) " ) ;
2009-08-29 19:49:45 -04:00
return results ;
}
2009-08-13 04:34:51 -04:00
/ * *
* Get extended address book information for person with gallookup .
* Does not work with Exchange 2007
*
* @param person person attributes map
* /
2008-12-03 06:38:35 -05:00
public void galLookup ( Map < String , String > person ) {
2008-12-23 10:20:50 -05:00
if ( ! disableGalLookup ) {
GetMethod getMethod = null ;
try {
2009-04-28 17:01:40 -04:00
getMethod = new GetMethod ( URIUtil . encodePathQuery ( getCmdBasePath ( ) + " ?Cmd=gallookup&ADDR= " + person . get ( " EM " ) ) ) ;
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , getMethod , true ) ;
2008-12-23 10:20:50 -05:00
Map < String , Map < String , String > > results = XMLStreamUtil . getElementContentsAsMap ( getMethod . getResponseBodyAsStream ( ) , " person " , " alias " ) ;
// add detailed information
2009-04-16 17:16:40 -04:00
if ( ! results . isEmpty ( ) ) {
2009-02-09 08:57:10 -05:00
Map < String , String > fullperson = results . get ( person . get ( " AN " ) . toLowerCase ( ) ) ;
2009-08-29 19:49:45 -04:00
if ( fullperson ! = null ) {
for ( Map . Entry < String , String > entry : fullperson . entrySet ( ) ) {
person . put ( entry . getKey ( ) , entry . getValue ( ) ) ;
}
2008-12-23 10:20:50 -05:00
}
}
} catch ( IOException e ) {
LOGGER . warn ( " Unable to gallookup person: " + person + " , disable GalLookup " ) ;
disableGalLookup = true ;
} finally {
if ( getMethod ! = null ) {
getMethod . releaseConnection ( ) ;
2008-12-03 06:38:35 -05:00
}
}
}
}
2009-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
2009-09-30 17:54:53 -04:00
String freebusyUrl ;
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
}
2009-12-09 16:13:43 -05:00
freebusyUrl = publicFolderUrl + " /?cmd=freebusy " +
2009-04-11 08:27:10 -04:00
" &start= " + exchangeZuluDateFormat . format ( startDate ) +
" &end= " + exchangeZuluDateFormat . format ( endDate ) +
2009-03-20 12:05:45 -04:00
" &interval= " + FREE_BUSY_INTERVAL +
2008-11-26 19:56:28 -05:00
" &u=SMTP: " + attendee ;
} 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 ;
2009-09-30 17:54:53 -04:00
GetMethod getMethod = new GetMethod ( freebusyUrl ) ;
2008-11-26 19:56:28 -05:00
getMethod . setRequestHeader ( " Content-Type " , " text/xml " ) ;
try {
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , getMethod , true ) ;
2009-11-03 05:01:33 -05:00
String fbdata = StringUtil . getLastToken ( getMethod . getResponseBodyAsString ( ) , " <a:fbdata> " , " </a:fbdata> " ) ;
if ( fbdata ! = null ) {
2009-04-07 08:37:28 -04:00
freeBusy = new FreeBusy ( icalDateFormat , startDate , fbdata ) ;
2008-11-26 19:56:28 -05:00
}
} finally {
getMethod . releaseConnection ( ) ;
}
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
2009-11-02 17:56:08 -05:00
protected final class VTimezone {
private String timezoneBody ;
private String timezoneId ;
/ * *
* create a fake event to get VTIMEZONE body
* /
private void load ( ) {
try {
// create temporary folder
String folderPath = ExchangeSession . this . getFolderPath ( " davmailtemp " ) ;
ExchangeSession . this . createCalendarFolder ( folderPath ) ;
2009-11-09 17:09:15 -05:00
PostMethod postMethod = new PostMethod ( URIUtil . encodePath ( folderPath ) ) ;
2009-11-02 17:56:08 -05:00
postMethod . addParameter ( " Cmd " , " saveappt " ) ;
postMethod . addParameter ( " FORMTYPE " , " appointment " ) ;
String fakeEventUrl = null ;
try {
// create fake event
int statusCode = ExchangeSession . this . httpClient . executeMethod ( postMethod ) ;
if ( statusCode = = HttpStatus . SC_OK ) {
fakeEventUrl = StringUtil . getToken ( postMethod . getResponseBodyAsString ( ) , " <span id= \" itemHREF \" > " , " </span> " ) ;
}
} finally {
postMethod . releaseConnection ( ) ;
}
2009-11-09 17:09:15 -05:00
// failover for Exchange 2007, use PROPPATCH with forced timezone
if ( fakeEventUrl = = null ) {
ArrayList < DavProperty > propertyList = new ArrayList < DavProperty > ( ) ;
2010-05-07 04:30:12 -04:00
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " contentclass " , DAV ) , " urn:content-classes:appointment " ) ) ;
2009-11-09 17:09:15 -05:00
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " outlookmessageclass " , Namespace . getNamespace ( " http://schemas.microsoft.com/exchange/ " ) ) , " IPM.Appointment " ) ) ;
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " instancetype " , Namespace . getNamespace ( " urn:schemas:calendar: " ) ) , " 0 " ) ) ;
// get forced timezone id from settings
timezoneId = Settings . getProperty ( " davmail.timezoneId " ) ;
if ( timezoneId ! = null ) {
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " timezoneid " , Namespace . getNamespace ( " urn:schemas:calendar: " ) ) , timezoneId ) ) ;
}
2009-11-12 16:32:20 -05:00
String patchMethodUrl = URIUtil . encodePath ( folderPath ) + '/' + UUID . randomUUID ( ) . toString ( ) + " .EML " ;
2009-11-09 17:09:15 -05:00
PropPatchMethod patchMethod = new PropPatchMethod ( URIUtil . encodePath ( patchMethodUrl ) , propertyList ) ;
try {
int statusCode = httpClient . executeMethod ( patchMethod ) ;
if ( statusCode = = HttpStatus . SC_MULTI_STATUS ) {
fakeEventUrl = patchMethodUrl ;
}
} finally {
patchMethod . releaseConnection ( ) ;
}
}
2009-11-02 17:56:08 -05:00
if ( fakeEventUrl ! = null ) {
// get fake event body
GetMethod getMethod = new GetMethod ( URIUtil . encodePath ( fakeEventUrl ) ) ;
getMethod . setRequestHeader ( " Translate " , " f " ) ;
try {
ExchangeSession . this . httpClient . executeMethod ( getMethod ) ;
timezoneBody = " BEGIN:VTIMEZONE " +
StringUtil . getToken ( getMethod . getResponseBodyAsString ( ) , " BEGIN:VTIMEZONE " , " END:VTIMEZONE " ) +
" END:VTIMEZONE \ r \ n " ;
timezoneId = StringUtil . getToken ( timezoneBody , " TZID: " , " \ r \ n " ) ;
} finally {
getMethod . releaseConnection ( ) ;
}
}
// delete temporary folder
ExchangeSession . this . deleteFolder ( " davmailtemp " ) ;
} catch ( IOException e ) {
LOGGER . warn ( " Unable to get VTIMEZONE info: " + e , e ) ;
}
}
}
protected VTimezone vTimezone ;
protected VTimezone getVTimezone ( ) {
if ( vTimezone = = null ) {
// need to load Timezone info from OWA
vTimezone = new VTimezone ( ) ;
vTimezone . load ( ) ;
}
return vTimezone ;
}
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
}