diff --git a/src/java/davmail/DavGateway.java b/src/java/davmail/DavGateway.java index b062c30d..19fba882 100644 --- a/src/java/davmail/DavGateway.java +++ b/src/java/davmail/DavGateway.java @@ -1,7 +1,7 @@ package davmail; import davmail.http.DavGatewaySSLProtocolSocketFactory; -import davmail.http.DavGatewayHttpClientFactory; +import davmail.http.DavGatewayHttpClientFacade; import davmail.pop.PopServer; import davmail.smtp.SmtpServer; import davmail.tray.DavGatewayTray; @@ -103,9 +103,9 @@ public class DavGateway { public static String getReleasedVersion() { String version = null; BufferedReader versionReader = null; + GetMethod getMethod = new GetMethod("http://davmail.sourceforge.net/version.txt"); try { - HttpClient httpClient = DavGatewayHttpClientFactory.getInstance(); - GetMethod getMethod = new GetMethod("http://davmail.sourceforge.net/version.txt"); + HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(); int status = httpClient.executeMethod(getMethod); if (status == HttpStatus.SC_OK) { versionReader = new BufferedReader(new InputStreamReader(getMethod.getResponseBodyAsStream())); @@ -121,6 +121,7 @@ public class DavGateway { // ignore } } + getMethod.releaseConnection(); } return version; } diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 7db1425d..43606a90 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -1,7 +1,7 @@ package davmail.exchange; import davmail.Settings; -import davmail.http.DavGatewayHttpClientFactory; +import davmail.http.DavGatewayHttpClientFacade; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; @@ -63,6 +63,11 @@ public class ExchangeSession { */ private final SimpleDateFormat dateParser; + /** + * Base Exchange URL + */ + private String baseUrl; + /** * Various standard mail boxes Urls */ @@ -97,32 +102,150 @@ public class ExchangeSession { * @throws java.io.IOException unable to connect to exchange */ protected boolean isBasicAuthentication(String url) throws IOException { - // create an HttpClient instance - HttpClient httpClient = DavGatewayHttpClientFactory.getInstance(); - HttpMethod testMethod = new GetMethod(url); - int status = httpClient.executeMethod(testMethod); - testMethod.releaseConnection(); - return status == HttpStatus.SC_UNAUTHORIZED; + return DavGatewayHttpClientFacade.getHttpStatus(url) == HttpStatus.SC_UNAUTHORIZED; + } + + /** + * Try to find logon method path from logon form body. + * + * @param initmethod form body http method + * @return logon method path + */ + protected String getLogonMethodPathAndBaseUrl(HttpMethod initmethod) { + // default logon method path + String logonMethodPath = "/exchweb/bin/auth/owaauth.dll"; + + // try to parse login form to determine logon url and destination + BufferedReader loginFormReader = null; + try { + loginFormReader = new BufferedReader(new InputStreamReader(initmethod.getResponseBodyAsStream())); + String line; + // skip to form action + final String FORM_ACTION = "
= 0) { + logonMethodPath = path.substring(0, end + 1) + logonMethodPath; + } + } + } catch (IOException e) { + LOGGER.error("Error parsing login form at " + initmethod.getPath()); + } finally { + if (loginFormReader != null) { + try { + loginFormReader.close(); + } catch (IOException e) { + LOGGER.error("Error parsing login form at " + initmethod.getPath()); + } + } + initmethod.releaseConnection(); + } + return logonMethodPath; + } + + protected void formLogin(HttpClient httpClient, HttpMethod initmethod, String userName, String password) throws IOException { + LOGGER.debug("Form based authentication detected"); + // get logon method path and actual destination (baseUrl) + String logonMethodPath = getLogonMethodPathAndBaseUrl(initmethod); + + PostMethod logonMethod = new PostMethod( + logonMethodPath + "?ForcedBasic=false&Basic=false&Private=true&Language=No_Value" + ); + logonMethod.addParameter("destination", baseUrl); + logonMethod.addParameter("flags", "4"); +// logonMethod.addParameter("visusername", userName.substring(userName.lastIndexOf('\\'))); + logonMethod.addParameter("username", userName); + logonMethod.addParameter("password", password); +// logonMethod.addParameter("SubmitCreds", "Log On"); +// logonMethod.addParameter("forcedownlevel", "0"); + logonMethod.addParameter("trusted", "4"); + + try { + httpClient.executeMethod(logonMethod); + } finally { + logonMethod.releaseConnection(); + } + Header locationHeader = logonMethod.getResponseHeader("Location"); + + if (logonMethod.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP || + locationHeader == null || + !baseUrl.equals(locationHeader.getValue())) { + throw new HttpException("Authentication failed"); + } + } + + protected String getMailPath(HttpMethod method) { + String result = null; + // get user mail URL from html body (multi frame) + BufferedReader mainPageReader = null; + try { + mainPageReader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream())); + String line; + // find base url + final String BASE_HREF = "=0) { - logonMethodPath = path.substring(0, end+1) + logonMethodPath; - } - } - } catch (IOException e) { - LOGGER.error("Error parsing login form at " + initmethod.getPath()); - } finally { - if (loginFormReader != null) { - loginFormReader.close(); - } - } - PostMethod logonMethod = new PostMethod( - logonMethodPath + "?ForcedBasic=false&Basic=false&Private=true&Language=No_Value" - ); - logonMethod.addParameter("destination", destination); - logonMethod.addParameter("flags", "4"); -// logonMethod.addParameter("visusername", userName.substring(userName.lastIndexOf('\\'))); - logonMethod.addParameter("username", userName); - logonMethod.addParameter("password", password); -// logonMethod.addParameter("SubmitCreds", "Log On"); -// logonMethod.addParameter("forcedownlevel", "0"); - logonMethod.addParameter("trusted", "4"); - - httpClient.executeMethod(logonMethod); - Header locationHeader = logonMethod.getResponseHeader("Location"); - - if (logonMethod.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP || - locationHeader == null || - !destination.equals(locationHeader.getValue())) { - throw new HttpException("Authentication failed"); - } - + formLogin(httpClient, initmethod, userName, password); } // User may be authenticated, get various session information - HttpMethod method = new GetMethod(destination); - method.setFollowRedirects(false); - int status = httpClient.executeMethod(method); - redirectCount = 0; - while (redirectCount++ < 10 && method.getStatusCode() == 302) { - method.releaseConnection(); - method = new GetMethod(method.getResponseHeader("Location").getValue()); - method.setFollowRedirects(false); - status = httpClient.executeMethod(method); - } - if (method.getStatusCode() == 302) { - throw new IOException("Maximum redirections reached"); - } + HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl); + int status = method.getStatusCode(); if (status != HttpStatus.SC_OK) { HttpException ex = new HttpException(); ex.setReasonCode(status); @@ -242,35 +278,14 @@ public class ExchangeSession { // test form based authentication String queryString = method.getQueryString(); if (queryString != null && queryString.endsWith("reason=2")) { - throw new HttpException("Authentication failed: invalid user or password"); + method.releaseConnection(); + throw new HttpException("Authentication failed: invalid user or password"); } - // get user mail URL from html body (multi frame) - BufferedReader mainPageReader = null; - try { - mainPageReader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream())); - String line; - // find base url - final String BASE_HREF = " 0) { + httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort); + if (proxyUser != null && proxyUser.length() > 0) { + +/* // Only available in newer HttpClient releases, not compatible with slide library + List authPrefs = new ArrayList(); + authPrefs.add(AuthPolicy.BASIC); + httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,authPrefs); +*/ + // instead detect ntlm authentication (windows domain name in user name) + int backslashindex = proxyUser.indexOf("\\"); + if (backslashindex > 0) { + httpClient.getState().setProxyCredentials(null, proxyHost, + new NTCredentials(proxyUser.substring(backslashindex + 1), + proxyPassword, null, + proxyUser.substring(0, backslashindex))); + } else { + httpClient.getState().setProxyCredentials(null, proxyHost, + new UsernamePasswordCredentials(proxyUser, proxyPassword)); + } + } + } + + } + + + /** + * Get Http Status code for the given URL + * + * @param url url string + * @return HttpStatus code + * @throws IOException on error + */ + public static int getHttpStatus(String url) throws IOException { + int status = 0; + HttpClient httpClient = DavGatewayHttpClientFacade.getInstance(); + HttpMethod testMethod = new GetMethod(url); + try { + status = httpClient.executeMethod(testMethod); + } finally { + testMethod.releaseConnection(); + } + return status; + } + + public static boolean isRedirect(int status) { + return status == HttpStatus.SC_MOVED_PERMANENTLY + || status == HttpStatus.SC_MOVED_TEMPORARILY + || status == HttpStatus.SC_SEE_OTHER + || status == HttpStatus.SC_TEMPORARY_REDIRECT; + } + + /** + * Execute given url, manually follow redirects. + * Workaround for HttpClient bug (GET full URL over HTTPS and proxy) + * + * @param httpClient HttpClient instance + * @param url url string + * @return executed method + * @throws IOException on error + */ + public static HttpMethod executeFollowRedirects(HttpClient httpClient, String url) throws IOException { + HttpMethod method = new GetMethod(url); + try { + method.setFollowRedirects(false); + httpClient.executeMethod(method); + Header location = method.getResponseHeader("Location"); + int redirectCount = 0; + while (redirectCount++ < 10 + && location != null + && isRedirect(method.getStatusCode())) { + method.releaseConnection(); + method = new GetMethod(method.getResponseHeader("Location").getValue()); + method.setFollowRedirects(false); + httpClient.executeMethod(method); + location = method.getResponseHeader("Location"); + } + if (location != null && isRedirect(method.getStatusCode())) { + method.releaseConnection(); + throw new HttpException("Maximum redirections reached"); + } + } catch (IOException e) { + method.releaseConnection(); + throw e; + } + // caller will need to release connection + return method; + } +} diff --git a/src/java/davmail/http/DavGatewayHttpClientFactory.java b/src/java/davmail/http/DavGatewayHttpClientFactory.java deleted file mode 100644 index b7a7ce8d..00000000 --- a/src/java/davmail/http/DavGatewayHttpClientFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -package davmail.http; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.NTCredentials; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import davmail.Settings; - -/** - * Create HttpClient instance according to DavGateway Settings - */ -public class DavGatewayHttpClientFactory { - /** - * Create a configured HttpClient instance. - * - * @return httpClient - */ - public static HttpClient getInstance() { - // create an HttpClient instance - HttpClient httpClient = new HttpClient(); - configureClient(httpClient); - return httpClient; - } - - /** - * Update http client configuration (proxy) - * - * @param httpClient current Http client - */ - public static void configureClient(HttpClient httpClient) { - // do not send basic auth automatically - httpClient.getState().setAuthenticationPreemptive(false); - - boolean enableProxy = Settings.getBooleanProperty("davmail.enableProxy"); - String proxyHost = null; - int proxyPort = 0; - String proxyUser = null; - String proxyPassword = null; - - if (enableProxy) { - proxyHost = Settings.getProperty("davmail.proxyHost"); - proxyPort = Settings.getIntProperty("davmail.proxyPort"); - proxyUser = Settings.getProperty("davmail.proxyUser"); - proxyPassword = Settings.getProperty("davmail.proxyPassword"); - } - - // configure proxy - if (proxyHost != null && proxyHost.length() > 0) { - httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort); - if (proxyUser != null && proxyUser.length() > 0) { - -/* // Only available in newer HttpClient releases, not compatible with slide library - List authPrefs = new ArrayList(); - authPrefs.add(AuthPolicy.BASIC); - httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,authPrefs); -*/ - // instead detect ntlm authentication (windows domain name in user name) - int backslashindex = proxyUser.indexOf("\\"); - if (backslashindex > 0) { - httpClient.getState().setProxyCredentials(null, proxyHost, - new NTCredentials(proxyUser.substring(backslashindex + 1), - proxyPassword, null, - proxyUser.substring(0, backslashindex))); - } else { - httpClient.getState().setProxyCredentials(null, proxyHost, - new UsernamePasswordCredentials(proxyUser, proxyPassword)); - } - } - } - - } - -}