Refactor form based authentication : parse html content with htmlcleaner-2.1

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@185 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2008-12-01 12:38:49 +00:00
parent 39d4bafb91
commit 932bc7468c
6 changed files with 71 additions and 73 deletions

View File

@ -18,6 +18,7 @@
<classPath>dist\lib\saxpath-1.0-FCS.jar</classPath> <classPath>dist\lib\saxpath-1.0-FCS.jar</classPath>
<classPath>dist\lib\slide-webdavlib-2.1.jar</classPath> <classPath>dist\lib\slide-webdavlib-2.1.jar</classPath>
<classPath>dist\lib\swt-3.3-win32-x86.jar</classPath> <classPath>dist\lib\swt-3.3-win32-x86.jar</classPath>
<classPath>dist\lib\htmlcleaner-2.1.jar</classPath>
<classPath>dist\davmail.jar</classPath> <classPath>dist\davmail.jar</classPath>
<embeddedJar>false</embeddedJar> <embeddedJar>false</embeddedJar>
<executableName>dist\davmail.exe</executableName> <executableName>dist\davmail.exe</executableName>

View File

@ -18,6 +18,7 @@
<classPath>dist\lib\saxpath-1.0-FCS.jar</classPath> <classPath>dist\lib\saxpath-1.0-FCS.jar</classPath>
<classPath>dist\lib\slide-webdavlib-2.1.jar</classPath> <classPath>dist\lib\slide-webdavlib-2.1.jar</classPath>
<classPath>dist\lib\swt-3.3-win32-x86.jar</classPath> <classPath>dist\lib\swt-3.3-win32-x86.jar</classPath>
<classPath>dist\lib\htmlcleaner-2.1.jar</classPath>
<classPath>dist\davmail.jar</classPath> <classPath>dist\davmail.jar</classPath>
<embeddedJar>false</embeddedJar> <embeddedJar>false</embeddedJar>
<executableName>dist\davmailconsole.exe</executableName> <executableName>dist\davmailconsole.exe</executableName>

BIN
lib/htmlcleaner-2.1.jar Normal file

Binary file not shown.

View File

@ -107,6 +107,11 @@
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.4</version> <version>2.4</version>
</dependency> </dependency>
<dependency>
<groupId>htmlcleaner</groupId>
<artifactId>htmlcleaner</artifactId>
<version>2.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>src/java</sourceDirectory> <sourceDirectory>src/java</sourceDirectory>

View File

@ -13,6 +13,8 @@ import org.apache.webdav.lib.Property;
import org.apache.webdav.lib.ResponseEntity; import org.apache.webdav.lib.ResponseEntity;
import org.apache.webdav.lib.WebdavResource; import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.methods.SearchMethod; import org.apache.webdav.lib.methods.SearchMethod;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import javax.mail.internet.MimeUtility; import javax.mail.internet.MimeUtility;
import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLInputFactory;
@ -100,88 +102,73 @@ public class ExchangeSession {
* Try to find logon method path from logon form body. * Try to find logon method path from logon form body.
* *
* @param initmethod form body http method * @param initmethod form body http method
* @return logon method path * @return logon method
*/ */
protected String getLogonMethodPathAndBaseUrl(HttpMethod initmethod) { protected PostMethod buildLogonMethod(HttpClient httpClient, HttpMethod initmethod) throws IOException {
// default logon method path
String logonMethodPath = "/exchweb/bin/auth/owaauth.dll"; PostMethod logonMethod = null;
// create an instance of HtmlCleaner
HtmlCleaner cleaner = new HtmlCleaner();
// try to parse login form to determine logon url and destination
BufferedReader loginFormReader = null;
try { try {
loginFormReader = new BufferedReader(new InputStreamReader(initmethod.getResponseBodyAsStream())); TagNode node = cleaner.clean(initmethod.getResponseBodyAsStream());
String line; List<TagNode> forms = node.getElementListByName("form", true);
// skip to form action if (forms.size() == 1) {
final String FORM_ACTION = "<form action=\""; TagNode form = forms.get(0);
final String DESTINATION_INPUT = "name=\"destination\" value=\""; String logonMethodPath = form.getAttributeByName("action");
//noinspection StatementWithEmptyBody
while ((line = loginFormReader.readLine()) != null && line.toLowerCase().indexOf(FORM_ACTION) == -1) { // allow relative URLs
} if (!logonMethodPath.startsWith("/")) {
if (line != null) { String path = initmethod.getPath();
int start = line.toLowerCase().indexOf(FORM_ACTION) + FORM_ACTION.length(); int end = path.lastIndexOf('/');
int end = line.indexOf("\"", start); if (end >= 0) {
logonMethodPath = line.substring(start, end); logonMethodPath = path.substring(0, end + 1) + logonMethodPath;
//noinspection StatementWithEmptyBody }
while ((line = loginFormReader.readLine()) != null && line.indexOf(DESTINATION_INPUT) == -1) {
} }
if (line != null) { logonMethod = new PostMethod(logonMethodPath);
start = line.indexOf(DESTINATION_INPUT) + DESTINATION_INPUT.length();
end = line.indexOf("\"", start); List<TagNode> inputList = form.getElementListByName("input", true);
baseUrl = line.substring(start, end); for (TagNode input : inputList) {
String type = input.getAttributeByName("type");
String name = input.getAttributeByName("name");
String value = input.getAttributeByName("value");
if ("hidden".equalsIgnoreCase(type) && name != null && value != null) {
logonMethod.addParameter(name, value);
}
} }
} } else {
// allow relative URLs List<TagNode> frameList = node.getElementListByName("frame", true);
if (!logonMethodPath.startsWith("/")) { if (frameList.size() == 1) {
String path = initmethod.getPath(); String src = frameList.get(0).getAttributeByName("src");
int end = path.lastIndexOf('/'); if (src != null) {
if (end >= 0) { LOGGER.debug("Frames detected in form page, try frame content");
logonMethodPath = path.substring(0, end + 1) + logonMethodPath; initmethod.releaseConnection();
HttpMethod newInitMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, src);
logonMethod = buildLogonMethod(httpClient, newInitMethod);
}
} }
} }
} catch (IOException e) { } catch (IOException e) {
LOGGER.error("Error parsing login form at " + initmethod.getPath()); LOGGER.error("Error parsing login form at " + initmethod.getURI());
} finally { } finally {
if (loginFormReader != null) {
try {
loginFormReader.close();
} catch (IOException e) {
LOGGER.error("Error parsing login form at " + initmethod.getPath());
}
}
initmethod.releaseConnection(); initmethod.releaseConnection();
} }
return logonMethodPath;
if (logonMethod == null) {
throw new IOException("Authentication form not found at " + initmethod.getURI() + ", retry with " + initmethod.getURI() + "exchange");
}
return logonMethod;
} }
protected void formLogin(HttpClient httpClient, HttpMethod initmethod, String userName, String password) throws IOException { protected HttpMethod formLogin(HttpClient httpClient, HttpMethod initmethod, String userName, String password) throws IOException {
LOGGER.debug("Form based authentication detected"); LOGGER.debug("Form based authentication detected");
// get logon method path and actual destination (baseUrl) // build logon method with actual destination (baseUrl)
String logonMethodPath = getLogonMethodPathAndBaseUrl(initmethod); HttpMethod logonMethod = buildLogonMethod(httpClient, initmethod);
((PostMethod) logonMethod).addParameter("username", userName);
PostMethod logonMethod = new PostMethod( ((PostMethod) logonMethod).addParameter("password", password);
logonMethodPath + "?ForcedBasic=false&Basic=false&Private=true&Language=No_Value" logonMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
); return logonMethod;
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) { protected String getMailPath(HttpMethod method) {
@ -255,13 +242,13 @@ public class ExchangeSession {
HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl); HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl);
if (!isBasicAuthentication) { if (!isBasicAuthentication) {
formLogin(httpClient, method, userName, password); method = formLogin(httpClient, method, userName, password);
// reexecute method with new base URL // reexecute method with new base URL
method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl); // method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl);
} }
int status = method.getStatusCode();
// User may be authenticated, get various session information // User may be authenticated, get various session information
int status = method.getStatusCode();
if (status != HttpStatus.SC_OK) { if (status != HttpStatus.SC_OK) {
HttpException ex = new HttpException(); HttpException ex = new HttpException();
ex.setReasonCode(status); ex.setReasonCode(status);
@ -270,7 +257,7 @@ public class ExchangeSession {
} }
// test form based authentication // test form based authentication
String queryString = method.getQueryString(); String queryString = method.getQueryString();
if (queryString != null && queryString.endsWith("reason=2")) { if (queryString != null && queryString.contains("reason=2")) {
method.releaseConnection(); method.releaseConnection();
if (userName != null && userName.contains("\\")) { if (userName != null && userName.contains("\\")) {
throw new HttpException("Authentication failed: invalid user or password"); throw new HttpException("Authentication failed: invalid user or password");

View File

@ -121,8 +121,12 @@ public class DavGatewayHttpClientFacade {
*/ */
public static HttpMethod executeFollowRedirects(HttpClient httpClient, String url) throws IOException { public static HttpMethod executeFollowRedirects(HttpClient httpClient, String url) throws IOException {
HttpMethod method = new GetMethod(url); HttpMethod method = new GetMethod(url);
method.setFollowRedirects(false);
return executeFollowRedirects(httpClient, method);
}
public static HttpMethod executeFollowRedirects(HttpClient httpClient, HttpMethod method) throws IOException {
try { try {
method.setFollowRedirects(false);
httpClient.executeMethod(method); httpClient.executeMethod(method);
Header location = method.getResponseHeader("Location"); Header location = method.getResponseHeader("Location");
int redirectCount = 0; int redirectCount = 0;