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\slide-webdavlib-2.1.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>
<embeddedJar>false</embeddedJar>
<executableName>dist\davmail.exe</executableName>

View File

@ -18,6 +18,7 @@
<classPath>dist\lib\saxpath-1.0-FCS.jar</classPath>
<classPath>dist\lib\slide-webdavlib-2.1.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>
<embeddedJar>false</embeddedJar>
<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>
<version>2.4</version>
</dependency>
<dependency>
<groupId>htmlcleaner</groupId>
<artifactId>htmlcleaner</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
<build>
<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.WebdavResource;
import org.apache.webdav.lib.methods.SearchMethod;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import javax.mail.internet.MimeUtility;
import javax.xml.stream.XMLInputFactory;
@ -100,88 +102,73 @@ public class ExchangeSession {
* Try to find logon method path from logon form body.
*
* @param initmethod form body http method
* @return logon method path
* @return logon method
*/
protected String getLogonMethodPathAndBaseUrl(HttpMethod initmethod) {
// default logon method path
String logonMethodPath = "/exchweb/bin/auth/owaauth.dll";
protected PostMethod buildLogonMethod(HttpClient httpClient, HttpMethod initmethod) throws IOException {
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 {
loginFormReader = new BufferedReader(new InputStreamReader(initmethod.getResponseBodyAsStream()));
String line;
// skip to form action
final String FORM_ACTION = "<form action=\"";
final String DESTINATION_INPUT = "name=\"destination\" value=\"";
//noinspection StatementWithEmptyBody
while ((line = loginFormReader.readLine()) != null && line.toLowerCase().indexOf(FORM_ACTION) == -1) {
}
if (line != null) {
int start = line.toLowerCase().indexOf(FORM_ACTION) + FORM_ACTION.length();
int end = line.indexOf("\"", start);
logonMethodPath = line.substring(start, end);
//noinspection StatementWithEmptyBody
while ((line = loginFormReader.readLine()) != null && line.indexOf(DESTINATION_INPUT) == -1) {
TagNode node = cleaner.clean(initmethod.getResponseBodyAsStream());
List<TagNode> forms = node.getElementListByName("form", true);
if (forms.size() == 1) {
TagNode form = forms.get(0);
String logonMethodPath = form.getAttributeByName("action");
// allow relative URLs
if (!logonMethodPath.startsWith("/")) {
String path = initmethod.getPath();
int end = path.lastIndexOf('/');
if (end >= 0) {
logonMethodPath = path.substring(0, end + 1) + logonMethodPath;
}
}
if (line != null) {
start = line.indexOf(DESTINATION_INPUT) + DESTINATION_INPUT.length();
end = line.indexOf("\"", start);
baseUrl = line.substring(start, end);
logonMethod = new PostMethod(logonMethodPath);
List<TagNode> inputList = form.getElementListByName("input", true);
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);
}
}
}
// allow relative URLs
if (!logonMethodPath.startsWith("/")) {
String path = initmethod.getPath();
int end = path.lastIndexOf('/');
if (end >= 0) {
logonMethodPath = path.substring(0, end + 1) + logonMethodPath;
} else {
List<TagNode> frameList = node.getElementListByName("frame", true);
if (frameList.size() == 1) {
String src = frameList.get(0).getAttributeByName("src");
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);
}
}
}
} catch (IOException e) {
LOGGER.error("Error parsing login form at " + initmethod.getPath());
LOGGER.error("Error parsing login form at " + initmethod.getURI());
} finally {
if (loginFormReader != null) {
try {
loginFormReader.close();
} catch (IOException e) {
LOGGER.error("Error parsing login form at " + initmethod.getPath());
}
}
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");
// 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");
}
// build logon method with actual destination (baseUrl)
HttpMethod logonMethod = buildLogonMethod(httpClient, initmethod);
((PostMethod) logonMethod).addParameter("username", userName);
((PostMethod) logonMethod).addParameter("password", password);
logonMethod = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, logonMethod);
return logonMethod;
}
protected String getMailPath(HttpMethod method) {
@ -255,13 +242,13 @@ public class ExchangeSession {
HttpMethod method = DavGatewayHttpClientFacade.executeFollowRedirects(httpClient, baseUrl);
if (!isBasicAuthentication) {
formLogin(httpClient, method, userName, password);
method = formLogin(httpClient, method, userName, password);
// 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
int status = method.getStatusCode();
if (status != HttpStatus.SC_OK) {
HttpException ex = new HttpException();
ex.setReasonCode(status);
@ -270,7 +257,7 @@ public class ExchangeSession {
}
// test form based authentication
String queryString = method.getQueryString();
if (queryString != null && queryString.endsWith("reason=2")) {
if (queryString != null && queryString.contains("reason=2")) {
method.releaseConnection();
if (userName != null && userName.contains("\\")) {
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 {
HttpMethod method = new GetMethod(url);
method.setFollowRedirects(false);
return executeFollowRedirects(httpClient, method);
}
public static HttpMethod executeFollowRedirects(HttpClient httpClient, HttpMethod method) throws IOException {
try {
method.setFollowRedirects(false);
httpClient.executeMethod(method);
Header location = method.getResponseHeader("Location");
int redirectCount = 0;