2009-07-21 04:39:18 -04:00
/ *
* DavMail POP / IMAP / SMTP / CalDav / LDAP Exchange Gateway
* Copyright ( C ) 2009 Mickael Guessant
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
* /
2006-12-12 18:57:24 -05:00
package davmail.exchange ;
2009-04-27 19:03:58 -04:00
import davmail.BundleMessage ;
2009-10-06 17:12:26 -04:00
import davmail.Settings ;
2009-04-27 19:03:58 -04:00
import davmail.exception.DavMailAuthenticationException ;
import davmail.exception.DavMailException ;
2008-11-03 20:47:10 -05:00
import davmail.http.DavGatewayHttpClientFacade ;
2009-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-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 ;
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 ;
2009-09-02 06:33:54 -04:00
import javax.mail.internet.MimeUtility ;
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.* ;
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-03-20 12:05:45 -04:00
protected static final int FREE_BUSY_INTERVAL = 15 ;
2009-04-01 17:31:44 -04:00
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace . getNamespace ( " urn:schemas:httpmail: " ) ;
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace . getNamespace ( " http://schemas.microsoft.com/mapi/proptag/ " ) ;
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-04-01 11:51:12 -04:00
EVENT_REQUEST_PROPERTIES . add ( DavPropertyName . GETETAG ) ;
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 {
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/ " ) ) ) ;
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 ;
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 ;
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-05-21 06:47:10 -04:00
// avoid 401 roundtrips
httpClient . getParams ( ) . setParameter ( HttpClientParams . PREEMPTIVE_AUTHENTICATION , true ) ;
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
}
buildMailPath ( method ) ;
// got base http mailbox http url
getWellKnownFolders ( ) ;
2009-04-27 19:03:58 -04:00
} catch ( DavMailAuthenticationException exc ) {
LOGGER . error ( exc . getLogMessage ( ) ) ;
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 ) {
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-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 ) ;
2008-12-01 07:38:49 -05:00
if ( forms . size ( ) = = 1 ) {
2008-12-17 10:27:56 -05:00
TagNode form = ( TagNode ) forms . get ( 0 ) ;
2008-12-01 07:38:49 -05:00
String logonMethodPath = form . getAttributeByName ( " action " ) ;
2009-09-02 12:05:03 -04:00
logonMethod = new PostMethod ( getAbsoluteUri ( initmethod , logonMethodPath ) ) ;
2008-12-01 07:38:49 -05:00
2008-12-17 10:27:56 -05:00
List inputList = form . getElementListByName ( " input " , true ) ;
for ( Object input : inputList ) {
String type = ( ( TagNode ) input ) . getAttributeByName ( " type " ) ;
String name = ( ( TagNode ) input ) . getAttributeByName ( " name " ) ;
String value = ( ( TagNode ) input ) . getAttributeByName ( " value " ) ;
2008-12-01 07:38:49 -05:00
if ( " hidden " . equalsIgnoreCase ( type ) & & name ! = null & & value ! = null ) {
logonMethod . addParameter ( name , value ) ;
}
2009-09-08 04:05:05 -04:00
// custom login form
if ( " txtUserName " . equals ( name ) ) {
userNameInput = " txtUserName " ;
} else if ( " txtUserPass " . equals ( name ) ) {
passwordInput = " txtUserPass " ;
}
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-09-21 17:34:13 -04:00
int sUrlIndex = scriptValue . indexOf ( " var a_sUrl = \" " ) ;
int sLgnIndex = scriptValue . indexOf ( " var a_sLgn = \" " ) ;
if ( sUrlIndex > = 0 & & sLgnIndex > = 0 ) {
sUrlIndex + = " var a_sUrl = \" " . length ( ) ;
sLgnIndex + = " var a_sLgn = \" " . length ( ) ;
int sUrlEndIndex = scriptValue . indexOf ( '\"' , sUrlIndex ) ;
int sLgnEndIndex = scriptValue . indexOf ( '\"' , sLgnIndex ) ;
if ( sUrlEndIndex > = 0 & & sLgnEndIndex > = 0 ) {
String pathQuery = scriptValue . substring ( sLgnIndex , sLgnEndIndex ) +
scriptValue . substring ( sUrlIndex , sUrlEndIndex ) ;
2009-09-17 10:42:45 -04:00
String src = getScriptBasedFormURL ( initmethod , pathQuery ) ;
2008-12-22 19:14:41 -05:00
LOGGER . debug ( " Detected script based logon, redirect to form at " + src ) ;
2008-12-19 07:27:33 -05:00
HttpMethod newInitMethod = DavGatewayHttpClientFacade . executeFollowRedirects ( httpClient , src ) ;
logonMethod = buildLogonMethod ( httpClient , newInitMethod ) ;
}
2008-12-22 19:14:41 -05:00
} else {
2009-09-21 17:34:13 -04:00
sLgnIndex = scriptValue . indexOf ( " var a_sLgnQS = \" " ) ;
if ( sUrlIndex > = 0 & & sLgnIndex > = 0 ) {
sUrlIndex + = " var a_sUrl = \" " . length ( ) ;
sLgnIndex + = " var a_sLgnQS = \" " . length ( ) ;
int sUrlEndIndex = scriptValue . indexOf ( '\"' , sUrlIndex ) ;
int sLgnEndIndex = scriptValue . indexOf ( '\"' , sLgnIndex ) ;
if ( sUrlEndIndex > = 0 & & sLgnEndIndex > = 0 ) {
String pathQuery = scriptValue . substring ( sLgnIndex , sLgnEndIndex ) +
scriptValue . substring ( sUrlIndex , sUrlEndIndex ) ;
2009-09-17 10:42:45 -04:00
String src = getScriptBasedFormURL ( initmethod , pathQuery ) ;
2008-12-22 19:14:41 -05:00
LOGGER . debug ( " Detected script based logon, redirect to form at " + src ) ;
HttpMethod newInitMethod = DavGatewayHttpClientFacade . executeFollowRedirects ( httpClient , src ) ;
logonMethod = buildLogonMethod ( httpClient , newInitMethod ) ;
}
}
2008-12-19 07:27:33 -05:00
}
}
}
2008-12-01 07:38:49 -05:00
}
2008-11-03 20:47:10 -05:00
}
}
} catch ( IOException e ) {
2008-12-01 07:38:49 -05:00
LOGGER . error ( " Error parsing login form at " + initmethod . getURI ( ) ) ;
2008-11-03 20:47:10 -05:00
} finally {
initmethod . releaseConnection ( ) ;
}
2008-12-01 07:38:49 -05:00
if ( logonMethod = = null ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_AUTHENTICATION_FORM_NOT_FOUND " , initmethod . getURI ( ) ) ;
2008-11-03 20:47:10 -05:00
}
2008-12-01 07:38:49 -05:00
return logonMethod ;
}
2008-11-03 20:47:10 -05:00
2008-12-01 07:38:49 -05:00
protected HttpMethod formLogin ( HttpClient httpClient , HttpMethod initmethod , String userName , String password ) throws IOException {
LOGGER . debug ( " Form based authentication detected " ) ;
2008-12-02 05:20:46 -05:00
2008-12-01 07:38:49 -05:00
HttpMethod logonMethod = buildLogonMethod ( httpClient , initmethod ) ;
2009-09-08 04:05:05 -04:00
( ( PostMethod ) logonMethod ) . addParameter ( userNameInput , userName ) ;
( ( PostMethod ) logonMethod ) . addParameter ( passwordInput , password ) ;
2009-09-02 12:05:03 -04:00
( ( PostMethod ) logonMethod ) . addParameter ( " trusted " , " 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
if ( httpClient . getState ( ) . getCookies ( ) . length = = 0 ) {
logonMethod = buildLogonMethod ( httpClient , logonMethod ) ;
logonMethod = DavGatewayHttpClientFacade . executeFollowRedirects ( httpClient , logonMethod ) ;
checkFormLoginQueryString ( logonMethod ) ;
}
2008-12-01 07:38:49 -05:00
return logonMethod ;
2008-11-03 20:47:10 -05:00
}
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-09-30 17:54:53 -04:00
if ( this . userName ! = null & & this . userName . contains ( " \\ " ) ) {
2009-04-27 19:03:58 -04:00
throw new DavMailAuthenticationException ( " EXCEPTION_AUTHENTICATION_FAILED " ) ;
2009-04-25 06:31:33 -04:00
} else {
2009-04-27 19:03:58 -04:00
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 ) ;
2009-05-07 04:45:00 -04:00
buildEmail ( method . getURI ( ) . getHost ( ) , method . getPath ( ) ) ;
2008-12-26 16:00:33 -05:00
LOGGER . debug ( " Current user email is " + email ) ;
2008-12-22 19:14:41 -05:00
} else {
2008-12-25 17:16:24 -05:00
// failover for Exchange 2007 : build standard mailbox link with email
2009-05-07 04:45:00 -04:00
buildEmail ( method . getURI ( ) . getHost ( ) , method . getPath ( ) ) ;
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 ( ) ;
}
}
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 ) ;
LOGGER . debug ( " Inbox URL : " + inboxUrl +
" Trash URL : " + deleteditemsUrl +
" Sent URL : " + sentitemsUrl +
" Send URL : " + sendmsgUrl +
" Drafts URL : " + draftsUrl +
" Calendar URL : " + calendarUrl +
" Contacts URL : " + contactsUrl
) ;
} catch ( IOException e ) {
LOGGER . error ( e . getMessage ( ) ) ;
throw new DavMailException ( " 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 ) ;
message . size = getIntPropertyIfExists ( properties , " x0e080003 " , SCHEMAS_MAPI_PROPTAG ) ;
message . uid = getPropertyIfExists ( properties , " uid " , Namespace . getNamespace ( " DAV: " ) ) ;
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 ( " " ) ) ) ;
2009-04-01 17:31:44 -04:00
message . messageId = getPropertyIfExists ( properties , " message-id " , Namespace . getNamespace ( " urn:schemas:mailheader: " ) ) ;
if ( message . messageId ! = null & & message . messageId . startsWith ( " < " ) & & message . messageId . endsWith ( " > " ) ) {
message . messageId = message . messageId . substring ( 1 , message . messageId . length ( ) - 1 ) ;
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-10-09 18:46:10 -04:00
PropPatchMethod patchMethod = new PropPatchMethod ( URIUtil . encodePathQuery ( message . messageUrl ) , 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 \" " +
2009-10-09 18:46:10 -04:00
" , \" urn:schemas:mailheader:message-id \" , \" urn:schemas:httpmail:read \" " +
" , \" http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/0x8570 \" as deleted, \" urn:schemas:mailheader:date \" " , conditions ) ;
}
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-07-08 18:51:34 -04:00
String searchRequest = " Select " + attributes +
2009-01-22 19:59:41 -05:00
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderUrl + " \" ') \ n " +
2009-03-09 19:12:08 -04:00
" WHERE \" DAV:ishidden \" = False AND \" DAV:isfolder \" = False \ n " ;
if ( conditions ! = null ) {
2009-03-18 13:26:33 -04:00
searchRequest + = conditions ;
2009-03-09 19:12:08 -04:00
}
searchRequest + = " ORDER BY \" urn:schemas:httpmail:date \" ASC " ;
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-01 12:56:18 -05:00
2009-04-01 11:51:12 -04:00
for ( MultiStatusResponse response : responses ) {
Message message = buildMessage ( response ) ;
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 ) ;
folder . hasChildren = " 1 " . equals ( getPropertyIfExists ( properties , " hassubs " , Namespace . getNamespace ( " DAV: " ) ) ) ;
folder . noInferiors = " 1 " . equals ( getPropertyIfExists ( properties , " nosubs " , Namespace . getNamespace ( " DAV: " ) ) ) ;
folder . unreadCount = getIntPropertyIfExists ( properties , " unreadcount " , URN_SCHEMAS_HTTPMAIL ) ;
folder . contenttag = getPropertyIfExists ( properties , " contenttag " , Namespace . getNamespace ( " http://schemas.microsoft.com/repl/ " ) ) ;
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 " ) ) {
folderPath = '/' + folderName ;
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
* @return current folder or new refreshed folder
* @throws IOException on error
2009-07-31 02:21:46 -04:00
* @deprecated no longer used : breaks Outlook IMAP
2009-03-27 06:31:52 -04:00
* /
public Folder refreshFolder ( Folder currentFolder ) throws IOException {
Folder newFolder = getFolder ( currentFolder . folderName ) ;
if ( currentFolder . contenttag = = null | | ! currentFolder . contenttag . equals ( newFolder . contenttag ) ) {
if ( LOGGER . isDebugEnabled ( ) ) {
2009-04-23 16:53:22 -04:00
LOGGER . debug ( " Contenttag changed on " + currentFolder . folderName + ' '
2009-03-27 06:31:52 -04:00
+ currentFolder . contenttag + " => " + newFolder . contenttag + " , reloading messages " ) ;
}
newFolder . loadMessages ( ) ;
return newFolder ;
} else {
return currentFolder ;
}
}
2009-07-31 02:21:46 -04:00
/ * *
2009-08-13 04:34:51 -04:00
* Create Exchange 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-01-23 08:01:46 -05:00
public void createFolder ( String folderName ) throws IOException {
String folderPath = getFolderPath ( folderName ) ;
2009-04-01 11:51:12 -04:00
ArrayList < DavProperty > list = new ArrayList < DavProperty > ( ) ;
list . add ( new DefaultDavProperty ( DavPropertyName . create ( " outlookfolderclass " , Namespace . getNamespace ( " http://schemas.microsoft.com/exchange/ " ) ) , " IPF.Note " ) ) ;
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 ) ;
// ok or alredy exists
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 {
String messageUrl = message . messageUrl ;
String targetPath = getFolderPath ( targetFolder ) + messageUrl . substring ( messageUrl . lastIndexOf ( '/' ) ) ;
CopyMethod method = new CopyMethod ( URIUtil . encodePath ( messageUrl ) , URIUtil . encodePath ( targetPath ) , false ) ;
// 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-07-31 02:21:46 -04:00
protected void moveToTrash ( String encodedPath , String encodedMessageName ) throws IOException {
2009-04-23 16:53:22 -04:00
String source = encodedPath + '/' + encodedMessageName ;
String destination = URIUtil . encodePath ( deleteditemsUrl ) + '/' + encodedMessageName ;
2009-03-13 17:18:11 -04:00
LOGGER . debug ( " Deleting : " + source + " to " + destination ) ;
2009-04-01 11:51:12 -04:00
MoveMethod method = new MoveMethod ( source , 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 ;
/ * *
* 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 ) .
* /
2009-03-27 06:31:52 -04:00
public String contenttag ;
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 ;
2009-01-22 19:59:41 -05:00
2009-07-31 02:21:46 -04:00
/ * *
* Get IMAP folder flags .
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @return folder flags in IMAP format
* /
2009-01-22 19:59:41 -05:00
public String getFlags ( ) {
if ( noInferiors ) {
return " \\ NoInferiors " ;
} else if ( hasChildren ) {
return " \\ HasChildren " ;
} else {
return " \\ HasNoChildren " ;
}
}
2009-03-27 06:31:52 -04:00
2009-07-31 02:21:46 -04:00
/ * *
* Load folder messages .
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @throws IOException on error
* /
2009-03-27 06:31:52 -04:00
public void loadMessages ( ) throws IOException {
2009-07-31 02:21:46 -04:00
messages = searchMessages ( folderPath , " " ) ;
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 uid at index .
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @param index message index
* @return message uid
* /
2009-03-27 06:31:52 -04:00
public long getImapUid ( int index ) {
return messages . get ( index ) . getImapUid ( ) ;
}
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 ) ;
}
2006-12-12 18:57:24 -05:00
}
2009-07-31 02:21:46 -04:00
/ * *
* Exchange message .
* /
2009-02-02 02:19:57 -05:00
public class Message implements Comparable {
2009-07-31 02:21:46 -04:00
protected String messageUrl ;
/ * *
* 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
/ * *
* Mail header message - id .
* /
protected String messageId ;
/ * *
* 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
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
}
2009-07-31 02:21:46 -04:00
/ * *
* Exchange uid .
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @return uid
* /
public String getUid ( ) {
return uid ;
}
/ * *
* Return message flags in IMAP format .
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @return IMAP flags
* /
2009-02-02 18:29:44 -05:00
public String getImapFlags ( ) {
StringBuilder buffer = new StringBuilder ( ) ;
if ( read ) {
buffer . append ( " \\ Seen " ) ;
}
if ( deleted ) {
2009-02-03 09:38:05 -05:00
buffer . append ( " \\ Deleted " ) ;
}
if ( flagged ) {
buffer . append ( " \\ Flagged " ) ;
}
if ( junk ) {
buffer . append ( " Junk " ) ;
2009-02-02 18:29:44 -05:00
}
2009-02-03 11:13:00 -05:00
if ( draft ) {
buffer . append ( " \\ Draft " ) ;
}
2009-02-03 18:54:48 -05:00
if ( answered ) {
buffer . append ( " \\ Answered " ) ;
}
2009-02-04 19:18:29 -05:00
if ( forwarded ) {
buffer . append ( " $Forwarded " ) ;
}
2009-02-03 09:38:05 -05:00
return buffer . toString ( ) . trim ( ) ;
2009-02-02 18:29:44 -05:00
}
2009-07-31 02:21:46 -04:00
/ * *
* Write MIME message to os
2009-08-04 16:48:35 -04:00
*
2009-07-31 02:21:46 -04:00
* @param os output stream
* @throws IOException on error
* /
2008-02-05 18:17:31 -05:00
public void write ( OutputStream os ) throws IOException {
2009-09-17 17:40:47 -04:00
GetMethod method = new GetMethod ( URIUtil . encodePath ( messageUrl ) ) ;
method . setRequestHeader ( " Content-Type " , " text/xml; charset=utf-8 " ) ;
method . setRequestHeader ( " Translate " , " f " ) ;
2007-04-25 18:04:37 -04:00
BufferedReader reader = null ;
2006-12-12 18:57:24 -05:00
try {
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , method , false ) ;
2006-12-12 18:57:24 -05:00
2008-02-05 18:17:31 -05:00
reader = new BufferedReader ( new InputStreamReader ( method . getResponseBodyAsStream ( ) ) ) ;
OutputStreamWriter isoWriter = new OutputStreamWriter ( os ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
if ( " . " . equals ( line ) ) {
line = " .. " ;
2008-02-14 09:20:37 -05:00
// patch text/calendar to include utf-8 encoding
} else if ( " Content-Type: text/calendar; " . equals ( line ) ) {
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 ( ) ;
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-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-03-18 13:26:33 -04:00
DavGatewayHttpClientFacade . executeDeleteMethod ( httpClient , URIUtil . encodePath ( messageUrl ) ) ;
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-03-13 17:18:11 -04:00
int index = messageUrl . lastIndexOf ( '/' ) ;
if ( index < 0 ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_INVALID_MESSAGE_URL " , messageUrl ) ;
2009-03-13 17:18:11 -04:00
}
String encodedPath = URIUtil . encodePath ( messageUrl . substring ( 0 , index ) ) ;
2009-03-18 13:26:33 -04:00
String encodedMessageName = URIUtil . encodePath ( messageUrl . substring ( index + 1 ) ) ;
2009-03-13 17:18:11 -04:00
ExchangeSession . this . moveToTrash ( encodedPath , encodedMessageName ) ;
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-02-02 02:19:57 -05:00
public int compareTo ( Object message ) {
2009-04-16 17:16:40 -04:00
long compareValue = ( imapUid - ( ( Message ) 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
/ * *
* Message list
* /
2009-08-04 16:48:35 -04:00
public static class MessageList extends ArrayList < Message > {
}
2008-11-26 19:56:28 -05:00
2009-08-13 04:34:51 -04:00
/ * *
* Calendar event object
* /
2008-11-26 19:56:28 -05:00
public class Event {
protected String href ;
protected String etag ;
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 ( ) ;
if ( contentType . startsWith ( " text/calendar " ) | | contentType . startsWith ( " application/ics " ) ) {
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-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
* @throws IOException on error
* /
2008-11-26 19:56:28 -05:00
public String getICS ( ) throws IOException {
2009-02-25 05:23:07 -05:00
String result = null ;
2008-12-17 10:27:56 -05:00
LOGGER . debug ( " Get event: " + href ) ;
2008-11-26 19:56:28 -05:00
GetMethod method = new GetMethod ( URIUtil . encodePath ( href ) ) ;
method . setRequestHeader ( " Content-Type " , " text/xml; charset=utf-8 " ) ;
method . setRequestHeader ( " Translate " , " f " ) ;
try {
2009-10-16 04:58:04 -04:00
DavGatewayHttpClientFacade . executeGetMethod ( httpClient , method , false ) ;
2009-09-17 17:40:47 -04:00
2009-02-25 05:23:07 -05:00
MimeMessage mimeMessage = new MimeMessage ( null , method . getResponseBodyAsStream ( ) ) ;
2009-03-04 01:53:24 -05:00
Object mimeBody = mimeMessage . getContent ( ) ;
2009-03-04 13:20:35 -05:00
MimePart bodyPart ;
2009-03-04 01:53:24 -05:00
if ( mimeBody instanceof MimeMultipart ) {
2009-03-04 13:20:35 -05:00
bodyPart = getCalendarMimePart ( ( MimeMultipart ) mimeBody ) ;
2009-03-04 01:53:24 -05:00
} else {
// no multipart, single body
bodyPart = mimeMessage ;
2008-11-26 19:56:28 -05:00
}
2009-02-25 05:23:07 -05:00
if ( bodyPart = = null ) {
2009-03-04 01:53:24 -05:00
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
mimeMessage . getDataHandler ( ) . writeTo ( baos ) ;
baos . close ( ) ;
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_INVALID_MESSAGE_CONTENT " , new String ( baos . toByteArray ( ) , " UTF-8 " ) ) ;
2008-11-26 19:56:28 -05:00
}
2009-02-25 05:23:07 -05:00
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
bodyPart . getDataHandler ( ) . writeTo ( baos ) ;
baos . close ( ) ;
result = fixICS ( new String ( baos . toByteArray ( ) , " UTF-8 " ) , true ) ;
2009-09-17 17:40:47 -04:00
} catch ( IOException e ) {
2009-09-20 10:10:54 -04:00
LOGGER . warn ( " Unable to get event at " + href + " : " + e . getMessage ( ) ) ;
2009-02-25 05:23:07 -05:00
} catch ( MessagingException e ) {
2009-09-20 10:10:54 -04:00
LOGGER . warn ( " Unable to get event at " + href + " : " + e . getMessage ( ) ) ;
2009-02-25 05:23:07 -05:00
} finally {
2008-11-26 19:56:28 -05:00
method . releaseConnection ( ) ;
}
2009-02-25 05:23:07 -05:00
return result ;
2008-11-26 19:56:28 -05:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Get event name ( file name part in URL ) .
2009-08-21 05:58:19 -04:00
*
2009-08-13 04:34:51 -04:00
* @return event name
* /
public String getName ( ) {
2009-02-25 05:23:07 -05:00
int index = href . lastIndexOf ( '/' ) ;
if ( index > = 0 ) {
return href . substring ( index + 1 ) ;
} else {
return href ;
}
2008-11-26 19:56:28 -05:00
}
2009-08-13 04:34:51 -04:00
/ * *
* Get event etag ( last change tag ) .
2009-08-21 05:58:19 -04:00
*
2009-08-13 04:34:51 -04:00
* @return event etag
* /
2008-11-26 19:56:28 -05:00
public String getEtag ( ) {
return etag ;
}
}
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 {
String searchQuery = " Select \" DAV:getetag \" " +
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderPath + " \" ') \ n " +
" WHERE \" DAV:contentclass \" = 'urn:content-classes:calendarmessage' \ n " +
" AND (NOT \" " + scheduleStateProperty . getNamespace ( ) . getURI ( ) + scheduleStateProperty . getName ( ) + " \" = 'CALDAV:schedule-processed') \ n " +
" 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 ) ) {
scheduleStateProperty = DavPropertyName . create ( " comment " , Namespace . getNamespace ( " DAV: " ) ) ;
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 {
2008-12-05 11:33:40 -05:00
int caldavPastDelay = Settings . getIntProperty ( " davmail.caldavPastDelay " , Integer . MAX_VALUE ) ;
2008-12-05 05:45:23 -05:00
String dateCondition = " " ;
2008-12-05 11:33:40 -05:00
if ( caldavPastDelay ! = Integer . MAX_VALUE ) {
2008-12-05 05:45:23 -05:00
Calendar cal = Calendar . getInstance ( ) ;
cal . add ( Calendar . DAY_OF_MONTH , - caldavPastDelay ) ;
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
}
2009-02-20 12:44:30 -05:00
String searchQuery = " Select \" DAV:getetag \" " +
2009-04-14 19:46:37 -04:00
" FROM Scope('SHALLOW TRAVERSAL OF \" " + folderPath + " \" ') \ n " +
2009-03-22 15:22:27 -04:00
" WHERE ( " +
" \" 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 " +
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 ) {
events . add ( buildEvent ( response ) ) ;
2008-11-26 19:56:28 -05:00
}
return events ;
}
2009-08-13 04:34:51 -04:00
/ * *
* Get 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 event object
* @throws IOException on error
* /
public Event getEvent ( String folderPath , String eventName ) throws IOException {
String eventPath = URIUtil . encodePath ( folderPath + '/' + eventName ) ;
2009-09-29 07:03:15 -04:00
return getEvent ( eventPath ) ;
}
protected Event getEvent ( String eventPath ) throws IOException {
2009-04-01 11:51:12 -04:00
MultiStatusResponse [ ] responses = DavGatewayHttpClientFacade . executePropFindMethod ( httpClient , eventPath , 0 , EVENT_REQUEST_PROPERTIES ) ;
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
}
2009-04-01 11:51:12 -04:00
return buildEvent ( responses [ 0 ] ) ;
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 ;
}
2009-04-01 11:51:12 -04:00
protected Event buildEvent ( MultiStatusResponse calendarResponse ) throws URIException {
2008-12-17 10:27:56 -05:00
Event event = new Event ( ) ;
2008-11-26 19:56:28 -05:00
String href = calendarResponse . getHref ( ) ;
event . href = URIUtil . decode ( href ) ;
2009-04-01 19:23:14 -04:00
event . etag = getPropertyIfExists ( calendarResponse . getProperties ( HttpStatus . SC_OK ) , " getetag " , Namespace . getNamespace ( " DAV: " ) ) ;
2008-11-26 19:56:28 -05:00
return event ;
}
2009-10-07 16:53:21 -04:00
private static int dumpIndex ;
2009-09-18 08:17:57 -04:00
private String defaultSound = " Basso " ;
2009-09-02 06:33:54 -04:00
protected void dumpICS ( String icsBody , boolean fromServer , boolean after ) {
2009-10-07 16:53:21 -04:00
String logFileDirectory = Settings . getLogFileDirectory ( ) ;
2009-09-02 06:33:54 -04:00
// additional setting to activate ICS dump (not available in GUI)
2009-10-07 16:53:21 -04:00
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 ) {
2009-10-19 05:00:22 -04:00
if ( ! file . delete ( ) ) {
LOGGER . warn ( " Unable to delete " + file . getAbsolutePath ( ) ) ;
}
2009-10-07 16:53:21 -04:00
}
} catch ( Exception ex ) {
LOGGER . warn ( " Error deleting ics dump: " + ex . getMessage ( ) ) ;
}
}
2009-09-02 06:33:54 -04:00
StringBuilder filePath = new StringBuilder ( ) ;
2009-10-07 16:53:21 -04:00
filePath . append ( logFileDirectory ) . append ( '/' )
2009-09-02 06:33:54 -04:00
. 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 ) ;
}
}
}
}
}
}
2008-12-09 10:49:20 -05:00
protected String fixICS ( String icsBody , boolean fromServer ) throws IOException {
2008-12-09 08:24:17 -05:00
// first pass : detect
2008-12-09 10:49:20 -05:00
class AllDayState {
2009-04-16 18:20:30 -04:00
boolean isAllDay ;
boolean hasCdoAllDay ;
boolean isCdoAllDay ;
2008-12-09 10:49:20 -05:00
}
2009-09-02 06:33:54 -04:00
dumpIndex + + ;
dumpICS ( icsBody , fromServer , false ) ;
2009-02-16 18:39:54 -05:00
// Convert event class from and to iCal
// See https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-privateevents.txt
boolean isAppleiCal = false ;
2009-08-10 12:39:28 -04:00
boolean hasAttendee = false ;
boolean hasCdoBusyStatus = false ;
2009-08-10 17:22:23 -04:00
String transp = null ;
2009-08-10 12:39:28 -04:00
String validTimezoneId = null ;
2009-02-16 18:39:54 -05:00
String eventClass = null ;
2009-09-02 06:33:54 -04:00
String organizer = null ;
String action = null ;
boolean sound = false ;
2009-02-16 18:39:54 -05:00
2008-12-09 10:49:20 -05:00
List < AllDayState > allDayStates = new ArrayList < AllDayState > ( ) ;
AllDayState currentAllDayState = new AllDayState ( ) ;
2008-12-09 08:24:17 -05:00
BufferedReader reader = null ;
try {
2009-01-04 09:21:50 -05:00
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
2008-12-09 08:24:17 -05:00
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
int index = line . indexOf ( ':' ) ;
if ( index > = 0 ) {
String key = line . substring ( 0 , index ) ;
String value = line . substring ( index + 1 ) ;
if ( " DTSTART;VALUE=DATE " . equals ( key ) ) {
2008-12-09 10:49:20 -05:00
currentAllDayState . isAllDay = true ;
2008-12-09 08:24:17 -05:00
} else if ( " X-MICROSOFT-CDO-ALLDAYEVENT " . equals ( key ) ) {
2008-12-09 10:49:20 -05:00
currentAllDayState . hasCdoAllDay = true ;
currentAllDayState . isCdoAllDay = " TRUE " . equals ( value ) ;
} else if ( " END:VEVENT " . equals ( line ) ) {
allDayStates . add ( currentAllDayState ) ;
currentAllDayState = new AllDayState ( ) ;
2009-02-16 18:39:54 -05:00
} else if ( " PRODID " . equals ( key ) & & line . contains ( " iCal " ) ) {
2009-09-02 06:33:54 -04:00
// detect iCal created events
2009-02-16 18:39:54 -05:00
isAppleiCal = true ;
} else if ( isAppleiCal & & " X-CALENDARSERVER-ACCESS " . equals ( key ) ) {
eventClass = value ;
} else if ( ! isAppleiCal & & " CLASS " . equals ( key ) ) {
eventClass = value ;
2009-09-02 06:33:54 -04:00
} 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 ;
// Set the default sound to whatever this event contains
// (under assumption that the user has the same sound set
// for all events)
defaultSound = value ;
2009-07-08 18:51:34 -04:00
} else if ( key . startsWith ( " ORGANIZER " ) ) {
2009-09-02 06:33:54 -04:00
if ( value . startsWith ( " MAILTO: " ) ) {
organizer = value . substring ( 7 ) ;
} else {
organizer = value ;
}
2009-08-10 12:39:28 -04:00
} else if ( key . startsWith ( " ATTENDEE " ) ) {
hasAttendee = true ;
2009-08-10 17:22:23 -04:00
} else if ( " TRANSP " . equals ( key ) ) {
transp = value ;
2009-08-11 05:14:16 -04:00
} else if ( line . startsWith ( " TZID:(GMT " ) | |
// additional test for Outlook created recurring events
line . startsWith ( " TZID:GMT " ) ) {
2009-08-10 12:39:28 -04:00
try {
validTimezoneId = ResourceBundle . getBundle ( " timezones " ) . getString ( value ) ;
} catch ( MissingResourceException mre ) {
LOGGER . warn ( new BundleMessage ( " LOG_INVALID_TIMEZONE " , value ) ) ;
}
2009-08-13 04:34:51 -04:00
} else if ( " X-MICROSOFT-CDO-BUSYSTATUS " . equals ( key ) ) {
2009-08-10 12:39:28 -04:00
hasCdoBusyStatus = true ;
2008-12-09 08:24:17 -05:00
}
}
}
} finally {
if ( reader ! = null ) {
reader . close ( ) ;
}
}
// second pass : fix
2008-12-09 10:49:20 -05:00
int count = 0 ;
2009-01-04 09:21:50 -05:00
ICSBufferedWriter result = new ICSBufferedWriter ( ) ;
2008-12-09 08:24:17 -05:00
try {
2009-01-04 09:21:50 -05:00
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
2008-12-09 08:24:17 -05:00
String line ;
2009-02-16 18:39:54 -05:00
2008-12-09 08:24:17 -05:00
while ( ( line = reader . readLine ( ) ) ! = null ) {
2009-08-07 09:10:33 -04:00
// remove empty properties
if ( " CLASS: " . equals ( line ) | | " LOCATION: " . equals ( line ) ) {
continue ;
2009-08-07 19:00:17 -04:00
}
// fix invalid exchange timezoneid
2009-08-11 05:14:16 -04:00
if ( validTimezoneId ! = null & & line . indexOf ( " ;TZID= " ) > = 0 ) {
2009-08-10 12:39:28 -04:00
line = fixTimezoneId ( line , validTimezoneId ) ;
2009-08-07 19:00:17 -04:00
}
if ( ! fromServer & & currentAllDayState . isAllDay & & " X-MICROSOFT-CDO-ALLDAYEVENT:FALSE " . equals ( line ) ) {
2008-12-09 08:24:17 -05:00
line = " X-MICROSOFT-CDO-ALLDAYEVENT:TRUE " ;
2009-09-22 08:37:35 -04:00
} 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 ) {
2009-09-28 19:28:23 -04:00
result . writeLine ( " ORGANIZER:MAILTO: " + email ) ;
2009-09-22 08:37:35 -04:00
}
2009-08-10 17:22:23 -04:00
} else if ( ! fromServer & & line . startsWith ( " X-MICROSOFT-CDO-BUSYSTATUS: " ) ) {
line = " X-MICROSOFT-CDO-BUSYSTATUS: " + ( ! " TRANSPARENT " . equals ( transp ) ? " BUSY " : " FREE " ) ;
2009-03-03 10:59:06 -05:00
} else if ( ! fromServer & & ! currentAllDayState . isAllDay & & " X-MICROSOFT-CDO-ALLDAYEVENT:TRUE " . equals ( line ) ) {
2008-12-09 08:24:17 -05:00
line = " X-MICROSOFT-CDO-ALLDAYEVENT:FALSE " ;
2009-03-03 10:59:06 -05:00
} else if ( fromServer & & currentAllDayState . isCdoAllDay & & line . startsWith ( " DTSTART " ) & & ! line . startsWith ( " DTSTART;VALUE=DATE " ) ) {
2008-12-09 10:06:52 -05:00
line = getAllDayLine ( line ) ;
2009-03-03 10:59:06 -05:00
} else if ( fromServer & & currentAllDayState . isCdoAllDay & & line . startsWith ( " DTEND " ) & & ! line . startsWith ( " DTEND;VALUE=DATE " ) ) {
2008-12-09 10:06:52 -05:00
line = getAllDayLine ( line ) ;
2009-08-10 12:39:28 -04:00
} else if ( line . startsWith ( " TZID: " ) & & validTimezoneId ! = null ) {
line = " TZID: " + validTimezoneId ;
2008-12-09 10:49:20 -05:00
} else if ( " BEGIN:VEVENT " . equals ( line ) ) {
currentAllDayState = allDayStates . get ( count + + ) ;
2009-02-16 18:39:54 -05:00
} else if ( line . startsWith ( " X-CALENDARSERVER-ACCESS: " ) ) {
if ( ! isAppleiCal ) {
continue ;
} else {
2009-02-19 17:00:36 -05:00
if ( " CONFIDENTIAL " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " CLASS:PRIVATE " ) ;
} else if ( " PRIVATE " . equalsIgnoreCase ( eventClass ) ) {
result . writeLine ( " CLASS:CONFIDENTIAL " ) ;
} else {
result . writeLine ( " CLASS: " + eventClass ) ;
}
2009-02-16 18:39:54 -05:00
}
2009-02-23 08:11:58 -05:00
} 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 ;
2009-09-02 06:33:54 -04:00
} 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: " ) ) {
if ( fromServer & & " DISPLAY " . equals ( action ) ) {
// Use the default iCal alarm action instead
// of the alarm Action exchange (and blackberry) understand.
// This is a bit of a hack because we don't know what type
// of alarm an iCal user really wants - but we know what the
// default is, and can setup the default action type
result . writeLine ( " ACTION:AUDIO " ) ;
if ( ! sound ) {
// Add default sound into the audio alarm
result . writeLine ( " ATTACH;VALUE=URI: " + defaultSound ) ;
}
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
2009-02-16 18:39:54 -05:00
} else if ( line . startsWith ( " CLASS: " ) ) {
if ( isAppleiCal ) {
continue ;
} else {
2009-02-19 17:00:36 -05:00
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 ) ;
}
2009-02-16 18:39:54 -05:00
}
2009-08-08 05:13:25 -04:00
// remove organizer line if user is organizer for iPhone
2009-08-10 12:39:28 -04:00
} else if ( fromServer & & line . startsWith ( " ORGANIZER " ) & & ! hasAttendee ) {
2009-08-04 16:48:35 -04:00
continue ;
2009-09-02 06:33:54 -04:00
} else if ( organizer ! = null & & line . startsWith ( " ATTENDEE " ) & & line . contains ( organizer ) ) {
// Ignore organizer as attendee
continue ;
2009-09-14 17:16:15 -04:00
} else if ( ! fromServer & & line . startsWith ( " ATTENDEE " ) ) {
line = replaceIcal4Principal ( line ) ;
2008-12-09 08:24:17 -05:00
}
2009-09-02 06:33:54 -04:00
2009-01-04 09:21:50 -05:00
result . writeLine ( line ) ;
2008-12-09 08:24:17 -05:00
}
} finally {
reader . close ( ) ;
}
2009-09-02 06:33:54 -04:00
String resultString = result . toString ( ) ;
dumpICS ( resultString , fromServer , true ) ;
2008-12-09 08:24:17 -05:00
return result . toString ( ) ;
}
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-10 12:39:28 -04:00
protected String fixTimezoneId ( String line , String validTimezoneId ) {
2009-08-11 05:14:16 -04:00
int startIndex = line . indexOf ( " TZID= " ) ;
int endIndex = line . indexOf ( ':' , startIndex + 5 ) ;
2009-08-10 12:39:28 -04:00
if ( startIndex > = 0 & & endIndex > = 0 ) {
2009-08-11 05:14:16 -04:00
return line . substring ( 0 , startIndex + 5 ) + validTimezoneId + line . substring ( endIndex ) ;
2009-08-07 19:00:17 -04:00
} else {
2009-08-10 12:39:28 -04:00
return line ;
2009-08-07 19:00:17 -04:00
}
}
2009-02-24 06:53:02 -05:00
protected void splitExDate ( ICSBufferedWriter result , String line ) {
2009-02-23 08:11:58 -05:00
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 ) ) ;
}
2008-12-09 10:06:52 -05:00
protected String getAllDayLine ( String line ) throws IOException {
int keyIndex = line . indexOf ( ';' ) ;
int valueIndex = line . lastIndexOf ( ':' ) ;
int valueEndIndex = line . lastIndexOf ( 'T' ) ;
2009-03-03 10:59:06 -05:00
if ( valueIndex < 0 | | valueEndIndex < 0 ) {
2009-04-27 19:03:58 -04:00
throw new DavMailException ( " EXCEPTION_INVALID_ICS_LINE " , line ) ;
2008-12-09 10:06:52 -05:00
}
2009-03-03 10:59:06 -05:00
String dateValue = line . substring ( valueIndex + 1 , valueEndIndex ) ;
String key = line . substring ( 0 , Math . max ( keyIndex , valueIndex ) ) ;
return key + " ;VALUE=DATE: " + dateValue ;
2008-12-09 10:06:52 -05:00
}
2009-02-09 05:12:09 -05:00
2009-08-13 04:34:51 -04:00
/ * *
* Event result object to hold HTTP status and event etag from an event creation / update .
* /
2009-03-27 09:00:47 -04:00
public static class EventResult {
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-04-23 16:53:22 -04:00
String messageUrl = URIUtil . encodePathQuery ( draftsUrl + '/' + UUID . randomUUID ( ) . toString ( ) + " .EML " ) ;
2009-02-12 06:33:19 -05:00
int status = internalCreateOrUpdateEvent ( messageUrl , " urn:content-classes:calendarmessage " , icsBody , null , null ) . status ;
2009-02-09 05:12:09 -05:00
if ( status ! = HttpStatus . SC_CREATED ) {
return status ;
} else {
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
/ * *
* Create or update event 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
2009-08-21 05:58:19 -04:00
* @param eventName event name
* @param icsBody event body in iCalendar format
* @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
* /
public EventResult createOrUpdateEvent ( String folderPath , String eventName , String icsBody , String etag , String noneMatch ) throws IOException {
String messageUrl = URIUtil . encodePath ( folderPath + '/' + eventName ) ;
2009-02-12 06:33:19 -05:00
return internalCreateOrUpdateEvent ( messageUrl , " urn:content-classes:appointment " , icsBody , etag , noneMatch ) ;
2009-02-09 05:12:09 -05:00
}
2009-02-24 06:53:02 -05:00
protected String getICSMethod ( String icsBody ) {
2009-02-12 06:33:19 -05:00
int methodIndex = icsBody . indexOf ( " METHOD: " ) ;
if ( methodIndex < 0 ) {
return " REQUEST " ;
}
int startIndex = methodIndex + " METHOD: " . length ( ) ;
2009-04-16 17:16:40 -04:00
int endIndex = icsBody . indexOf ( '\r' , startIndex ) ;
2009-02-12 06:33:19 -05:00
if ( endIndex < 0 ) {
return " REQUEST " ;
}
return icsBody . substring ( startIndex , endIndex ) ;
}
2009-09-02 06:33:54 -04:00
protected String getICSValue ( String icsBody , String prefix , String defval ) throws IOException {
2009-10-06 17:12:26 -04:00
// only return values in VEVENT section, not VALARM
2009-10-06 17:06:37 -04:00
Stack < String > sectionStack = new Stack < String > ( ) ;
2009-09-02 06:33:54 -04:00
BufferedReader reader = null ;
try {
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
2009-10-06 17:06:37 -04:00
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 ) ) {
2009-09-02 06:33:54 -04:00
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 {
2009-10-06 17:06:37 -04:00
return getICSValue ( icsBody , " DESCRIPTION: " , " " ) ;
2009-09-02 06:33:54 -04:00
}
2009-03-27 09:00:47 -04:00
static class Participants {
2009-03-21 05:49:11 -04:00
String attendees ;
String organizer ;
}
2009-06-24 19:14:25 -04:00
/ * *
* Parse ics event for attendees and organizer .
* For notifications , only include attendees with RSVP = TRUE or PARTSTAT = NEEDS - ACTION
2009-07-08 18:51:34 -04:00
*
* @param icsBody ics event
2009-06-24 19:14:25 -04:00
* @param isNotification get only notified attendees
* @return participants
* @throws IOException on error
* /
protected Participants getParticipants ( String icsBody , boolean isNotification ) throws IOException {
2009-03-21 05:49:11 -04:00
HashSet < String > attendees = new HashSet < String > ( ) ;
2009-02-16 18:42:07 -05:00
String organizer = null ;
2009-02-12 06:33:19 -05:00
BufferedReader reader = null ;
try {
reader = new ICSBufferedReader ( new StringReader ( icsBody ) ) ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null ) {
int index = line . indexOf ( ':' ) ;
if ( index > = 0 ) {
String key = line . substring ( 0 , index ) ;
String value = line . substring ( index + 1 ) ;
int semiColon = key . indexOf ( ';' ) ;
if ( semiColon > = 0 ) {
key = key . substring ( 0 , semiColon ) ;
}
2009-02-16 18:42:07 -05:00
if ( " ORGANIZER " . equals ( key ) | | " ATTENDEE " . equals ( key ) ) {
2009-02-12 06:33:19 -05:00
int colonIndex = value . indexOf ( ':' ) ;
if ( colonIndex > = 0 ) {
value = value . substring ( colonIndex + 1 ) ;
}
2009-09-14 17:16:15 -04:00
value = replaceIcal4Principal ( value ) ;
2009-02-16 18:42:07 -05:00
if ( " ORGANIZER " . equals ( key ) ) {
organizer = value ;
2009-04-28 17:01:40 -04:00
// exclude current user and invalid values from recipients
2009-06-24 19:14:25 -04:00
// also exclude no action attendees
2009-04-28 17:01:40 -04:00
} else if ( ! email . equalsIgnoreCase ( value ) & & value . indexOf ( '@' ) > = 0
2009-06-24 19:14:25 -04:00
& & ( ! isNotification
2009-07-08 18:51:34 -04:00
| | line . indexOf ( " RSVP=TRUE " ) > = 0
| | line . indexOf ( " PARTSTAT=NEEDS-ACTION " ) > = 0 ) ) {
2009-03-21 05:49:11 -04:00
attendees . add ( value ) ;
2009-02-12 06:33:19 -05:00
}
}
}
}
} finally {
if ( reader ! = null ) {
reader . close ( ) ;
}
}
2009-03-21 05:49:11 -04:00
Participants participants = new Participants ( ) ;
2009-02-16 18:42:07 -05:00
StringBuilder result = new StringBuilder ( ) ;
2009-03-21 05:49:11 -04:00
for ( String recipient : attendees ) {
if ( result . length ( ) > 0 ) {
result . append ( " , " ) ;
2009-02-16 18:42:07 -05:00
}
2009-03-21 05:49:11 -04:00
result . append ( recipient ) ;
2009-02-16 18:42:07 -05:00
}
2009-03-21 05:49:11 -04:00
participants . attendees = result . toString ( ) ;
participants . organizer = organizer ;
return participants ;
2009-02-12 06:33:19 -05:00
}
protected EventResult internalCreateOrUpdateEvent ( String messageUrl , String contentClass , String icsBody , String etag , String noneMatch ) throws IOException {
2009-02-09 05:12:09 -05:00
String uid = UUID . randomUUID ( ) . toString ( ) ;
2009-04-01 18:06:53 -04:00
ByteArrayOutputStream baos = new ByteArrayOutputStream ( ) ;
OutputStreamWriter writer = new OutputStreamWriter ( baos , " ASCII " ) ;
2009-02-23 05:08:51 -05:00
int status = 0 ;
2008-11-26 19:56:28 -05:00
PutMethod putmethod = new PutMethod ( messageUrl ) ;
putmethod . setRequestHeader ( " Translate " , " f " ) ;
putmethod . setRequestHeader ( " Overwrite " , " f " ) ;
if ( etag ! = null ) {
putmethod . setRequestHeader ( " If-Match " , etag ) ;
}
2009-01-18 17:48:53 -05:00
if ( noneMatch ! = null ) {
putmethod . setRequestHeader ( " If-None-Match " , noneMatch ) ;
}
2008-11-26 19:56:28 -05:00
putmethod . setRequestHeader ( " Content-Type " , " message/rfc822 " ) ;
2009-02-12 06:33:19 -05:00
String method = getICSMethod ( icsBody ) ;
2009-04-01 18:06:53 -04:00
writer . write ( " Content-Transfer-Encoding: 7bit \ r \ n " +
" Content-class: " ) ;
writer . write ( contentClass ) ;
writer . write ( " \ r \ n " ) ;
2009-02-12 06:33:19 -05:00
if ( " urn:content-classes:calendarmessage " . equals ( contentClass ) ) {
2009-06-24 19:14:25 -04:00
// need to parse attendees and organizer to build recipients
Participants participants = getParticipants ( icsBody , true ) ;
2009-03-21 05:49:11 -04:00
String recipients ;
if ( email . equalsIgnoreCase ( participants . organizer ) ) {
// current user is organizer => notify all
recipients = participants . attendees ;
} else {
// notify only organizer
recipients = participants . organizer ;
}
2009-09-02 06:33:54 -04:00
// Make sure invites have a proper subject line
writer . write ( " Subject: " + MimeUtility . encodeText ( getICSSummary ( icsBody ) , " UTF-8 " , null ) + " \ r \ n " ) ;
2009-04-01 18:06:53 -04:00
writer . write ( " To: " ) ;
writer . write ( recipients ) ;
writer . write ( " \ r \ n " ) ;
2009-02-23 05:08:51 -05:00
// do not send notification if no recipients found
2009-02-16 18:39:54 -05:00
if ( recipients . length ( ) = = 0 ) {
2009-02-23 05:08:51 -05:00
status = HttpStatus . SC_NO_CONTENT ;
2009-02-16 18:39:54 -05:00
}
2009-03-21 05:49:11 -04:00
} else {
2009-09-02 06:33:54 -04:00
// Make sure invites have a proper subject line
writer . write ( " Subject: " + MimeUtility . encodeText ( getICSSummary ( icsBody ) , " UTF-8 " , null ) + " \ r \ n " ) ;
2009-06-24 19:14:25 -04:00
// need to parse attendees and organizer to build recipients
Participants participants = getParticipants ( icsBody , false ) ;
2009-03-21 05:49:11 -04:00
// storing appointment, full recipients header
2009-04-01 18:06:53 -04:00
if ( participants . attendees ! = null ) {
writer . write ( " To: " ) ;
writer . write ( participants . attendees ) ;
writer . write ( " \ r \ n " ) ;
}
if ( participants . organizer ! = null ) {
writer . write ( " From: " ) ;
writer . write ( participants . organizer ) ;
writer . write ( " \ r \ n " ) ;
}
2009-08-29 19:49:45 -04:00
// if not organizer, set REPLYTIME to force Outlook in attendee mode
2009-03-21 18:04:06 -04:00
if ( participants . organizer ! = null & & ! email . equalsIgnoreCase ( participants . organizer ) ) {
2009-03-21 06:39:13 -04:00
if ( icsBody . indexOf ( " METHOD: " ) < 0 ) {
icsBody = icsBody . replaceAll ( " BEGIN:VCALENDAR " , " BEGIN:VCALENDAR \ r \ nMETHOD:REQUEST " ) ;
}
if ( icsBody . indexOf ( " X-MICROSOFT-CDO-REPLYTIME " ) < 0 ) {
icsBody = icsBody . replaceAll ( " END:VEVENT " , " X-MICROSOFT-CDO-REPLYTIME: " +
2009-04-11 08:27:10 -04:00
getZuluDateFormat ( ) . format ( new Date ( ) ) + " \ r \ nEND:VEVENT " ) ;
2009-03-21 06:39:13 -04:00
}
2009-03-21 05:49:11 -04:00
}
2009-02-12 06:33:19 -05:00
}
2009-04-01 18:06:53 -04:00
writer . write ( " MIME-Version: 1.0 \ r \ n " +
2009-02-12 06:33:19 -05:00
" Content-Type: multipart/alternative; \ r \ n " +
2009-04-01 18:06:53 -04:00
" \ tboundary= \" ----=_NextPart_ " ) ;
writer . write ( uid ) ;
writer . write ( " \" \ r \ n " +
2009-02-12 06:33:19 -05:00
" \ r \ n " +
" This is a multi-part message in MIME format. \ r \ n " +
" \ r \ n " +
2009-04-01 18:06:53 -04:00
" ------=_NextPart_ " ) ;
writer . write ( uid ) ;
2009-09-02 06:33:54 -04:00
// Write a part of the message that contains the
// ICS description so that invites contain the description text
2009-09-02 09:35:53 -04:00
String description = getICSDescription ( icsBody ) . replaceAll ( " \\ \\ [Nn] " , " \ r \ n " ) ;
2009-09-02 06:33:54 -04:00
if ( description . length ( ) > 0 ) {
writer . write ( " \ r \ n " +
" Content-Type: text/plain; \ r \ n " +
" \ tcharset= \" utf-8 \" \ r \ n " +
" content-transfer-encoding: 8bit \ r \ n " +
" \ r \ n " ) ;
writer . flush ( ) ;
baos . write ( description . getBytes ( " UTF-8 " ) ) ;
writer . write ( " \ r \ n " +
" ------=_NextPart_ " +
uid ) ;
}
2009-04-01 18:06:53 -04:00
writer . write ( " \ r \ n " +
" Content-class: " ) ;
writer . write ( contentClass ) ;
writer . write ( " \ r \ n " +
2009-02-12 06:33:19 -05:00
" Content-Type: text/calendar; \ r \ n " +
2009-04-01 18:06:53 -04:00
" \ tmethod= " ) ;
writer . write ( method ) ;
writer . write ( " ; \ r \ n " +
2009-02-12 06:33:19 -05:00
" \ tcharset= \" utf-8 \" \ r \ n " +
" Content-Transfer-Encoding: 8bit \ r \ n \ r \ n " ) ;
2009-04-01 18:06:53 -04:00
writer . flush ( ) ;
baos . write ( fixICS ( icsBody , false ) . getBytes ( " UTF-8 " ) ) ;
writer . write ( " ------=_NextPart_ " ) ;
writer . write ( uid ) ;
writer . write ( " -- \ r \ n " ) ;
writer . close ( ) ;
putmethod . setRequestEntity ( new ByteArrayRequestEntity ( baos . toByteArray ( ) , " message/rfc822 " ) ) ;
2008-11-26 19:56:28 -05:00
try {
2009-02-23 05:08:51 -05:00
if ( status = = 0 ) {
2009-03-18 13:26:33 -04:00
status = httpClient . executeMethod ( putmethod ) ;
2009-02-23 05:08:51 -05:00
if ( status = = HttpURLConnection . HTTP_OK ) {
2009-03-05 11:56:04 -05:00
if ( etag ! = null ) {
LOGGER . debug ( " Updated event " + messageUrl ) ;
} else {
LOGGER . warn ( " Overwritten event " + messageUrl ) ;
}
2009-02-23 05:08:51 -05:00
} else if ( status ! = HttpURLConnection . HTTP_CREATED ) {
2009-04-23 16:53:22 -04:00
LOGGER . warn ( " Unable to create or update message " + status + ' ' + putmethod . getStatusLine ( ) ) ;
2009-02-23 05:08:51 -05:00
}
2008-11-26 19:56:28 -05:00
}
} finally {
putmethod . releaseConnection ( ) ;
}
2009-02-11 20:33:49 -05:00
EventResult eventResult = new EventResult ( ) ;
2009-08-11 12:55:35 -04:00
// 440 means forbidden on Exchange
if ( status = = 440 ) {
status = HttpStatus . SC_FORBIDDEN ;
}
2009-02-11 20:33:49 -05:00
eventResult . status = status ;
if ( putmethod . getResponseHeader ( " GetETag " ) ! = null ) {
2009-02-12 06:33:19 -05:00
eventResult . etag = putmethod . getResponseHeader ( " GetETag " ) . getValue ( ) ;
2009-02-11 20:33:49 -05:00
}
2009-09-29 07:03:15 -04:00
2009-10-19 04:58:12 -04: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 " ) ) ) {
2009-09-29 07:03:15 -04:00
ArrayList < DavProperty > propertyList = new ArrayList < DavProperty > ( ) ;
propertyList . add ( new DefaultDavProperty ( DavPropertyName . create ( " contentclass " , Namespace . getNamespace ( " DAV: " ) ) , contentClass ) ) ;
PropPatchMethod propPatchMethod = new PropPatchMethod ( messageUrl , propertyList ) ;
int patchStatus = DavGatewayHttpClientFacade . executeHttpMethod ( httpClient , propPatchMethod ) ;
if ( patchStatus ! = HttpStatus . SC_MULTI_STATUS ) {
LOGGER . warn ( " Unable to patch event to trigger activeSync push " ) ;
}
}
2009-02-11 20:33:49 -05:00
return eventResult ;
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 {
displayName = getPropertyIfExists ( responses [ 0 ] . getProperties ( HttpStatus . SC_OK ) , " displayname " , Namespace . getNamespace ( " DAV: " ) ) ;
}
} 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
2009-04-14 19:46:37 -04:00
if ( principal ! = null & & ! alias . equals ( principal ) & & ! email . equals ( 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 ( ) ;
}
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-04-23 18:10:10 -04:00
return " /public/ " ;
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 .
*
2009-08-21 05:58:19 -04:00
* @param hostName Exchange server host name for last failover
2009-08-13 04:34:51 -04:00
* @param methodPath current httpclient method path
* /
2009-05-07 04:45:00 -04:00
public void buildEmail ( String hostName , String methodPath ) {
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
2009-05-07 04:45:00 -04:00
alias = getAliasFromOptions ( methodPath ) ;
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 ) {
email = getEmailFromOptions ( methodPath ) ;
}
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 ( )
2009-05-07 04:45:00 -04:00
+ " or " + getAliasFromOptions ( methodPath )
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 ) ;
buffer . append ( '@' ) ;
int dotIndex = hostName . indexOf ( '.' ) ;
if ( dotIndex > = 0 ) {
buffer . append ( hostName . substring ( dotIndex + 1 ) ) ;
}
}
email = buffer . toString ( ) ;
2009-03-09 11:21:03 -04:00
}
}
2009-09-21 17:34:13 -04:00
static final String MAILBOX_BASE = " cn=recipients/cn= " ;
2009-03-09 11:21:03 -04:00
protected String getAliasFromOptions ( String path ) {
String result = null ;
// get user mail URL from html body
BufferedReader optionsPageReader = null ;
GetMethod optionsMethod = new GetMethod ( path + " ?ae=Options&t=About " ) ;
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 ) {
int start = line . toLowerCase ( ) . indexOf ( 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
}
2009-05-19 06:42:56 -04:00
protected String getEmailFromOptions ( String path ) {
String result = null ;
// get user mail URL from html body
BufferedReader optionsPageReader = null ;
GetMethod optionsMethod = new GetMethod ( path + " ?ae=Options&t=About " ) ;
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 ) ;
2008-11-26 19:56:28 -05:00
if ( attendee . startsWith ( " mailto: " ) ) {
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-09-30 17:54:53 -04:00
freebusyUrl = " /public/?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 ) ;
2008-11-26 19:56:28 -05:00
String body = getMethod . getResponseBodyAsString ( ) ;
int startIndex = body . lastIndexOf ( " <a:fbdata> " ) ;
int endIndex = body . lastIndexOf ( " </a:fbdata> " ) ;
if ( startIndex > = 0 & & endIndex > = 0 ) {
String fbdata = body . substring ( startIndex + " <a:fbdata> " . length ( ) , endIndex ) ;
2009-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
/ * *
* 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
}