Fix checkstyle issues in WebDavStore

This commit is contained in:
Philip Whitehouse 2017-03-15 15:32:24 +00:00
parent e590efaf8e
commit 54c2e29388
2 changed files with 210 additions and 215 deletions

View file

@ -27,7 +27,6 @@ import java.io.InputStreamReader;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -373,7 +372,7 @@ class WebDavFolder extends Folder<WebDavMessage> {
if (store.getAuthentication() == WebDavConstants.AUTH_TYPE_BASIC) { if (store.getAuthentication() == WebDavConstants.AUTH_TYPE_BASIC) {
httpget.setHeader("Authorization", store.getAuthString()); httpget.setHeader("Authorization", store.getAuthString());
} }
response = httpclient.executeOverride(httpget, store.getContext()); response = httpclient.executeOverride(httpget, store.getHttpContext());
statusCode = response.getStatusLine().getStatusCode(); statusCode = response.getStatusLine().getStatusCode();

View file

@ -1,14 +1,40 @@
package com.fsck.k9.mail.store.webdav; package com.fsck.k9.mail.store.webdav;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.util.Log; import android.util.Log;
import com.fsck.k9.mail.*;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.CertificateValidationException; import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.store.StoreConfig;
import javax.net.ssl.SSLException;
import org.apache.http.*; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore; import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.protocol.ClientContext;
@ -23,22 +49,9 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; import org.xml.sax.XMLReader;
import javax.net.ssl.SSLException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_WEBDAV; import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_WEBDAV;
import static com.fsck.k9.mail.K9MailLib.LOG_TAG; import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8; import static com.fsck.k9.mail.helper.UrlEncodingHelper.decodeUtf8;
import static com.fsck.k9.mail.helper.UrlEncodingHelper.encodeUtf8;
/** /**
@ -47,6 +60,7 @@ import static com.fsck.k9.mail.helper.UrlEncodingHelper.encodeUtf8;
* and email information. * and email information.
* </pre> * </pre>
*/ */
@SuppressWarnings("deprecation")
public class WebDavStore extends RemoteStore { public class WebDavStore extends RemoteStore {
public static WebDavStoreSettings decodeUri(String uri) { public static WebDavStoreSettings decodeUri(String uri) {
@ -58,31 +72,31 @@ public class WebDavStore extends RemoteStore {
} }
private ConnectionSecurity mConnectionSecurity; private ConnectionSecurity mConnectionSecurity;
private String mUsername; /* Stores the username for authentications */ private String username;
private String mAlias; /* Stores the alias for the user's mailbox */ private String alias;
private String mPassword; /* Stores the password for authentications */ private String password;
private String mUrl; /* Stores the base URL for the server */ private String baseUrl;
private String mHost; /* Stores the host name for the server */ private String hostname;
private int mPort; private int port;
private String mPath; /* Stores the path for the server */ private String path;
private String mAuthPath; /* Stores the path off of the server to post data to for form based authentication */ private String formBasedAuthPath;
private String mMailboxPath; /* Stores the user specified path to the mailbox */ private String mailboxPath;
private final WebDavHttpClient.WebDavHttpClientFactory mHttpClientFactory; private final WebDavHttpClient.WebDavHttpClientFactory httpClientFactory;
private WebDavHttpClient mHttpClient = null; private WebDavHttpClient httpClient = null;
private HttpContext mContext = null; private HttpContext httpContext = null;
private String mAuthString; private String authString;
private CookieStore mAuthCookies = null; private CookieStore authCookies = null;
private short mAuthentication = WebDavConstants.AUTH_TYPE_NONE; private short authenticationType = WebDavConstants.AUTH_TYPE_NONE;
private String mCachedLoginUrl; private String cachedLoginUrl;
private Folder mSendFolder = null; private Folder sendFolder = null;
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>(); private Map<String, WebDavFolder> folderList = new HashMap<>();
public WebDavStore(StoreConfig storeConfig, WebDavHttpClient.WebDavHttpClientFactory clientFactory) public WebDavStore(StoreConfig storeConfig, WebDavHttpClient.WebDavHttpClientFactory clientFactory)
throws MessagingException { throws MessagingException {
super(storeConfig, null); super(storeConfig, null);
mHttpClientFactory = clientFactory; httpClientFactory = clientFactory;
WebDavStoreSettings settings; WebDavStoreSettings settings;
try { try {
@ -91,43 +105,43 @@ public class WebDavStore extends RemoteStore {
throw new MessagingException("Error while decoding store URI", e); throw new MessagingException("Error while decoding store URI", e);
} }
mHost = settings.host; hostname = settings.host;
mPort = settings.port; port = settings.port;
mConnectionSecurity = settings.connectionSecurity; mConnectionSecurity = settings.connectionSecurity;
mUsername = settings.username; username = settings.username;
mPassword = settings.password; password = settings.password;
mAlias = settings.alias; alias = settings.alias;
mPath = settings.path; path = settings.path;
mAuthPath = settings.authPath; formBasedAuthPath = settings.authPath;
mMailboxPath = settings.mailboxPath; mailboxPath = settings.mailboxPath;
if (mPath == null || mPath.equals("")) { if (path == null || path.equals("")) {
mPath = "/Exchange"; path = "/Exchange";
} else if (!mPath.startsWith("/")) { } else if (!path.startsWith("/")) {
mPath = "/" + mPath; path = "/" + path;
} }
if (mMailboxPath == null || mMailboxPath.equals("")) { if (mailboxPath == null || mailboxPath.equals("")) {
mMailboxPath = "/" + mAlias; mailboxPath = "/" + alias;
} else if (!mMailboxPath.startsWith("/")) { } else if (!mailboxPath.startsWith("/")) {
mMailboxPath = "/" + mMailboxPath; mailboxPath = "/" + mailboxPath;
} }
if (mAuthPath != null && if (formBasedAuthPath != null &&
!mAuthPath.equals("") && !formBasedAuthPath.equals("") &&
!mAuthPath.startsWith("/")) { !formBasedAuthPath.startsWith("/")) {
mAuthPath = "/" + mAuthPath; formBasedAuthPath = "/" + formBasedAuthPath;
} }
// The URL typically looks like the following: "https://mail.domain.com/Exchange/alias". // The URL typically looks like the following: "https://mail.domain.com/Exchange/alias".
// The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox". // The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox".
mUrl = getRoot() + mPath + mMailboxPath; baseUrl = getRoot() + path + mailboxPath;
mAuthString = "Basic " + Base64.encode(mUsername + ":" + mPassword); authString = "Basic " + Base64.encode(username + ":" + password);
} }
private String getRoot() { private String getRoot() {
@ -137,16 +151,16 @@ public class WebDavStore extends RemoteStore {
} else { } else {
root = "http"; root = "http";
} }
root += "://" + mHost + ":" + mPort; root += "://" + hostname + ":" + port;
return root; return root;
} }
HttpContext getContext() { HttpContext getHttpContext() {
return mContext; return httpContext;
} }
short getAuthentication() { short getAuthentication() {
return mAuthentication; return authenticationType;
} }
StoreConfig getStoreConfig() { StoreConfig getStoreConfig() {
@ -160,20 +174,20 @@ public class WebDavStore extends RemoteStore {
@Override @Override
public List<? extends Folder> getPersonalNamespaces(boolean forceListAll) throws MessagingException { public List<? extends Folder> getPersonalNamespaces(boolean forceListAll) throws MessagingException {
List<Folder> folderList = new LinkedList<Folder>(); List<Folder> folderList = new LinkedList<>();
/** /*
* We have to check authentication here so we have the proper URL stored * We have to check authentication here so we have the proper URL stored
*/ */
getHttpClient(); getHttpClient();
/** /*
* Firstly we get the "special" folders list (inbox, outbox, etc) * Firstly we get the "special" folders list (inbox, outbox, etc)
* and setup the account accordingly * and setup the account accordingly
*/ */
Map<String, String> headers = new HashMap<String, String>(); Map<String, String> headers = new HashMap<>();
headers.put("Depth", "0"); headers.put("Depth", "0");
headers.put("Brief", "t"); headers.put("Brief", "t");
DataSet dataset = processRequest(this.mUrl, "PROPFIND", getSpecialFoldersList(), headers); DataSet dataset = processRequest(this.baseUrl, "PROPFIND", getSpecialFoldersList(), headers);
Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl(); Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
String folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_INBOX_FOLDER)); String folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_INBOX_FOLDER));
@ -183,16 +197,19 @@ public class WebDavStore extends RemoteStore {
} }
folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_DRAFTS_FOLDER)); folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_DRAFTS_FOLDER));
if (folderName != null) if (folderName != null) {
mStoreConfig.setDraftsFolderName(folderName); mStoreConfig.setDraftsFolderName(folderName);
}
folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_TRASH_FOLDER)); folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_TRASH_FOLDER));
if (folderName != null) if (folderName != null) {
mStoreConfig.setTrashFolderName(folderName); mStoreConfig.setTrashFolderName(folderName);
}
folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_SPAM_FOLDER)); folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_SPAM_FOLDER));
if (folderName != null) if (folderName != null) {
mStoreConfig.setSpamFolderName(folderName); mStoreConfig.setSpamFolderName(folderName);
}
// K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox. // K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox.
/* /*
@ -202,21 +219,23 @@ public class WebDavStore extends RemoteStore {
*/ */
folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_SENT_FOLDER)); folderName = getFolderName(specialFoldersMap.get(WebDavConstants.DAV_MAIL_SENT_FOLDER));
if (folderName != null) if (folderName != null) {
mStoreConfig.setSentFolderName(folderName); mStoreConfig.setSentFolderName(folderName);
}
/** /*
* Next we get all the folders (including "special" ones) * Next we get all the folders (including "special" ones)
*/ */
headers = new HashMap<String, String>(); headers = new HashMap<>();
headers.put("Brief", "t"); headers.put("Brief", "t");
dataset = processRequest(this.mUrl, "SEARCH", getFolderListXml(), headers); dataset = processRequest(this.baseUrl, "SEARCH", getFolderListXml(), headers);
String[] folderUrls = dataset.getHrefs(); String[] folderUrls = dataset.getHrefs();
for (String tempUrl : folderUrls) { for (String tempUrl : folderUrls) {
WebDavFolder folder = createFolder(tempUrl); WebDavFolder folder = createFolder(tempUrl);
if (folder != null) if (folder != null) {
folderList.add(folder); folderList.add(folder);
}
} }
return folderList; return folderList;
@ -227,11 +246,14 @@ public class WebDavStore extends RemoteStore {
* already created) and adds this to our store folder map. * already created) and adds this to our store folder map.
* *
* @param folderUrl * @param folderUrl
* @return * URL
*
* @return WebDAV remote folder
*/ */
private WebDavFolder createFolder(String folderUrl) { private WebDavFolder createFolder(String folderUrl) {
if (folderUrl == null) if (folderUrl == null) {
return null; return null;
}
WebDavFolder wdFolder = null; WebDavFolder wdFolder = null;
String folderName = getFolderName(folderUrl); String folderName = getFolderName(folderUrl);
@ -247,8 +269,9 @@ public class WebDavStore extends RemoteStore {
} }
private String getFolderName(String folderUrl) { private String getFolderName(String folderUrl) {
if (folderUrl == null) if (folderUrl == null) {
return null; return null;
}
// Here we extract the folder name starting from the complete url. // Here we extract the folder name starting from the complete url.
// folderUrl is in the form http://mail.domain.com/exchange/username/foldername // folderUrl is in the form http://mail.domain.com/exchange/username/foldername
@ -256,18 +279,20 @@ public class WebDavStore extends RemoteStore {
int folderSlash = -1; int folderSlash = -1;
for (int j = 0; j < 5; j++) { for (int j = 0; j < 5; j++) {
folderSlash = folderUrl.indexOf('/', folderSlash + 1); folderSlash = folderUrl.indexOf('/', folderSlash + 1);
if (folderSlash < 0) if (folderSlash < 0) {
break; break;
}
} }
if (folderSlash > 0) { if (folderSlash > 0) {
String fullPathName; String fullPathName;
// Removes the final slash if present // Removes the final slash if present
if (folderUrl.charAt(folderUrl.length() - 1) == '/') if (folderUrl.charAt(folderUrl.length() - 1) == '/') {
fullPathName = folderUrl.substring(folderSlash + 1, folderUrl.length() - 1); fullPathName = folderUrl.substring(folderSlash + 1, folderUrl.length() - 1);
else } else {
fullPathName = folderUrl.substring(folderSlash + 1); fullPathName = folderUrl.substring(folderSlash + 1);
}
// Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder" // Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder"
@ -279,21 +304,22 @@ public class WebDavStore extends RemoteStore {
@Override @Override
public WebDavFolder getFolder(String name) { public WebDavFolder getFolder(String name) {
WebDavFolder folder; WebDavFolder folder = this.folderList.get(name);
if ((folder = this.mFolderList.get(name)) == null) { if (folder == null) {
folder = new WebDavFolder(this, name); folder = new WebDavFolder(this, name);
mFolderList.put(name, folder); folderList.put(name, folder);
} }
return folder; return folder;
} }
public Folder getSendSpoolFolder() throws MessagingException { private Folder getSendSpoolFolder() throws MessagingException {
if (mSendFolder == null) if (sendFolder == null) {
mSendFolder = getFolder(WebDavConstants.DAV_MAIL_SEND_FOLDER); sendFolder = getFolder(WebDavConstants.DAV_MAIL_SEND_FOLDER);
}
return mSendFolder; return sendFolder;
} }
@Override @Override
@ -307,50 +333,42 @@ public class WebDavStore extends RemoteStore {
} }
private String getSpecialFoldersList() { private String getSpecialFoldersList() {
StringBuilder builder = new StringBuilder(200); return "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>" +
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>"); "<propfind xmlns=\"DAV:\">" +
builder.append("<propfind xmlns=\"DAV:\">"); "<prop>" +
builder.append("<prop>"); "<" + WebDavConstants.DAV_MAIL_INBOX_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_INBOX_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "<" + WebDavConstants.DAV_MAIL_DRAFTS_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_DRAFTS_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "<" + WebDavConstants.DAV_MAIL_OUTBOX_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_OUTBOX_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "<" + WebDavConstants.DAV_MAIL_SENT_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_SENT_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "<" + WebDavConstants.DAV_MAIL_TRASH_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_TRASH_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "<" + WebDavConstants.DAV_MAIL_SPAM_FOLDER + " xmlns=\"urn:schemas:httpmail:\"/>" +
// This should always be ##DavMailSubmissionURI## for which we already have a constant // This should always be ##DavMailSubmissionURI## for which we already have a constant
// buffer.append("<sendmsg xmlns=\"urn:schemas:httpmail:\"/>"); // "<sendmsg xmlns=\"urn:schemas:httpmail:\"/>" +
"</prop>" +
builder.append("<").append(WebDavConstants.DAV_MAIL_SPAM_FOLDER).append(" xmlns=\"urn:schemas:httpmail:\"/>"); "</propfind>";
builder.append("</prop>");
builder.append("</propfind>");
return builder.toString();
} }
/*************************************************************** /***************************************************************
* WebDAV XML Request body retrieval functions * WebDAV XML Request body retrieval functions
*/ */
private String getFolderListXml() { private String getFolderListXml() {
StringBuilder builder = new StringBuilder(200); return "<?xml version='1.0' ?>" +
builder.append("<?xml version='1.0' ?>"); "<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n" +
builder.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n"); "SELECT \"DAV:uid\", \"DAV:ishidden\"\r\n" +
builder.append("SELECT \"DAV:uid\", \"DAV:ishidden\"\r\n"); " FROM SCOPE('deep traversal of \"" + this.baseUrl + "\"')\r\n" +
builder.append(" FROM SCOPE('deep traversal of \"").append(this.mUrl).append("\"')\r\n"); " WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=True\r\n" +
builder.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=True\r\n"); "</a:sql></a:searchrequest>\r\n";
builder.append("</a:sql></a:searchrequest>\r\n");
return builder.toString();
} }
String getMessageCountXml(String messageState) { String getMessageCountXml(String messageState) {
StringBuilder builder = new StringBuilder(200); return "<?xml version='1.0' ?>" +
builder.append("<?xml version='1.0' ?>"); "<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n" +
builder.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n"); "SELECT \"DAV:visiblecount\"\r\n" +
builder.append("SELECT \"DAV:visiblecount\"\r\n"); " FROM \"\"\r\n" +
builder.append(" FROM \"\"\r\n"); " WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND \"urn:schemas:httpmail:read\"=" +
builder.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND \"urn:schemas:httpmail:read\"=") messageState + "\r\n" +
.append(messageState).append("\r\n"); " GROUP BY \"DAV:ishidden\"\r\n" +
builder.append(" GROUP BY \"DAV:ishidden\"\r\n"); "</a:sql></a:searchrequest>\r\n";
builder.append("</a:sql></a:searchrequest>\r\n");
return builder.toString();
} }
String getMessageEnvelopeXml(String[] uids) { String getMessageEnvelopeXml(String[] uids) {
@ -384,14 +402,12 @@ public class WebDavStore extends RemoteStore {
} }
String getMessagesXml() { String getMessagesXml() {
StringBuilder builder = new StringBuilder(200); return "<?xml version='1.0' ?>" +
builder.append("<?xml version='1.0' ?>"); "<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n" +
builder.append("<a:searchrequest xmlns:a='DAV:'><a:sql>\r\n"); "SELECT \"DAV:uid\"\r\n" +
builder.append("SELECT \"DAV:uid\"\r\n"); " FROM \"\"\r\n" +
builder.append(" FROM \"\"\r\n"); " WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False\r\n" +
builder.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False\r\n"); "</a:sql></a:searchrequest>\r\n";
builder.append("</a:sql></a:searchrequest>\r\n");
return builder.toString();
} }
String getMessageUrlsXml(String[] uids) { String getMessageUrlsXml(String[] uids) {
@ -475,32 +491,23 @@ public class WebDavStore extends RemoteStore {
return buffer.toString(); return buffer.toString();
} }
/*************************************************************** private boolean authenticate()
* Authentication related methods
*/
/**
* Determines which type of authentication Exchange is using and authenticates appropriately.
*
* @throws MessagingException
*/
public boolean authenticate()
throws MessagingException { throws MessagingException {
try { try {
if (mAuthentication == WebDavConstants.AUTH_TYPE_NONE) { if (authenticationType == WebDavConstants.AUTH_TYPE_NONE) {
ConnectionInfo info = doInitialConnection(); ConnectionInfo info = doInitialConnection();
if (info.requiredAuthType == WebDavConstants.AUTH_TYPE_BASIC) { if (info.requiredAuthType == WebDavConstants.AUTH_TYPE_BASIC) {
HttpGeneric request = new HttpGeneric(mUrl); HttpGeneric request = new HttpGeneric(baseUrl);
request.setMethod("GET"); request.setMethod("GET");
request.setHeader("Authorization", mAuthString); request.setHeader("Authorization", authString);
WebDavHttpClient httpClient = getHttpClient(); WebDavHttpClient httpClient = getHttpClient();
HttpResponse response = httpClient.executeOverride(request, mContext); HttpResponse response = httpClient.executeOverride(request, httpContext);
int statusCode = response.getStatusLine().getStatusCode(); int statusCode = response.getStatusLine().getStatusCode();
if (statusCode >= 200 && statusCode < 300) { if (statusCode >= 200 && statusCode < 300) {
mAuthentication = WebDavConstants.AUTH_TYPE_BASIC; authenticationType = WebDavConstants.AUTH_TYPE_BASIC;
} else if (statusCode == 401) { } else if (statusCode == 401) {
throw new MessagingException("Invalid username or password for authentication."); throw new MessagingException("Invalid username or password for authentication.");
} else { } else {
@ -508,29 +515,23 @@ public class WebDavStore extends RemoteStore {
" during request processing: " + response.getStatusLine().toString()); " during request processing: " + response.getStatusLine().toString());
} }
} else if (info.requiredAuthType == WebDavConstants.AUTH_TYPE_FORM_BASED) { } else if (info.requiredAuthType == WebDavConstants.AUTH_TYPE_FORM_BASED) {
doFBA(info); performFormBasedAuthentication(info);
} }
} else if (mAuthentication == WebDavConstants.AUTH_TYPE_BASIC) { } else if (authenticationType == WebDavConstants.AUTH_TYPE_BASIC) {
// Nothing to do, we authenticate with every request when // Nothing to do, we authenticate with every request when
// using basic authentication. // using basic authentication.
} else if (mAuthentication == WebDavConstants.AUTH_TYPE_FORM_BASED) { } else if (authenticationType == WebDavConstants.AUTH_TYPE_FORM_BASED) {
// Our cookie expired, re-authenticate. // Our cookie expired, re-authenticate.
doFBA(null); performFormBasedAuthentication(null);
} }
} catch (IOException ioe) { } catch (IOException ioe) {
Log.e(LOG_TAG, "Error during authentication: " + ioe + "\nStack: " + WebDavUtils.processException(ioe)); Log.e(LOG_TAG, "Error during authentication: " + ioe + "\nStack: " + WebDavUtils.processException(ioe));
throw new MessagingException("Error during authentication", ioe); throw new MessagingException("Error during authentication", ioe);
} }
return mAuthentication != WebDavConstants.AUTH_TYPE_NONE; return authenticationType != WebDavConstants.AUTH_TYPE_NONE;
} }
/**
* Makes the initial connection to Exchange for authentication. Determines the type of authentication necessary for
* the server.
*
* @throws MessagingException
*/
private ConnectionInfo doInitialConnection() private ConnectionInfo doInitialConnection()
throws MessagingException { throws MessagingException {
// For our initial connection we are sending an empty GET request to // For our initial connection we are sending an empty GET request to
@ -547,11 +548,11 @@ public class WebDavStore extends RemoteStore {
WebDavHttpClient httpClient = getHttpClient(); WebDavHttpClient httpClient = getHttpClient();
HttpGeneric request = new HttpGeneric(mUrl); HttpGeneric request = new HttpGeneric(baseUrl);
request.setMethod("GET"); request.setMethod("GET");
try { try {
HttpResponse response = httpClient.executeOverride(request, mContext); HttpResponse response = httpClient.executeOverride(request, httpContext);
info.statusCode = response.getStatusLine().getStatusCode(); info.statusCode = response.getStatusLine().getStatusCode();
if (info.statusCode == 401) { if (info.statusCode == 401) {
@ -568,9 +569,9 @@ public class WebDavStore extends RemoteStore {
// authorization URL. // authorization URL.
info.requiredAuthType = WebDavConstants.AUTH_TYPE_FORM_BASED; info.requiredAuthType = WebDavConstants.AUTH_TYPE_FORM_BASED;
if (mAuthPath != null && !mAuthPath.equals("")) { if (formBasedAuthPath != null && !formBasedAuthPath.equals("")) {
// The user specified their own authentication path, use that. // The user specified their own authentication path, use that.
info.guessedAuthUrl = getRoot() + mAuthPath; info.guessedAuthUrl = getRoot() + formBasedAuthPath;
} else { } else {
// Use the default path to the authentication dll. // Use the default path to the authentication dll.
info.guessedAuthUrl = getRoot() + "/exchweb/bin/auth/owaauth.dll"; info.guessedAuthUrl = getRoot() + "/exchweb/bin/auth/owaauth.dll";
@ -595,23 +596,20 @@ public class WebDavStore extends RemoteStore {
return info; return info;
} }
/** private void performFormBasedAuthentication(ConnectionInfo info)
* Performs form-based authentication.
*
* @throws MessagingException
*/
public void doFBA(ConnectionInfo info)
throws IOException, MessagingException { throws IOException, MessagingException {
// Clear out cookies from any previous authentication. // Clear out cookies from any previous authentication.
if (mAuthCookies != null) mAuthCookies.clear(); if (authCookies != null) {
authCookies.clear();
}
WebDavHttpClient httpClient = getHttpClient(); WebDavHttpClient httpClient = getHttpClient();
String loginUrl; String loginUrl;
if (info != null) { if (info != null) {
loginUrl = info.guessedAuthUrl; loginUrl = info.guessedAuthUrl;
} else if (mCachedLoginUrl != null && !mCachedLoginUrl.equals("")) { } else if (cachedLoginUrl != null && !cachedLoginUrl.equals("")) {
loginUrl = mCachedLoginUrl; loginUrl = cachedLoginUrl;
} else { } else {
throw new MessagingException("No valid login URL available for form-based authentication."); throw new MessagingException("No valid login URL available for form-based authentication.");
} }
@ -620,10 +618,10 @@ public class WebDavStore extends RemoteStore {
request.setMethod("POST"); request.setMethod("POST");
// Build the POST data. // Build the POST data.
List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>(); List<BasicNameValuePair> pairs = new ArrayList<>();
pairs.add(new BasicNameValuePair("destination", mUrl)); pairs.add(new BasicNameValuePair("destination", baseUrl));
pairs.add(new BasicNameValuePair("username", mUsername)); pairs.add(new BasicNameValuePair("username", username));
pairs.add(new BasicNameValuePair("password", mPassword)); pairs.add(new BasicNameValuePair("password", password));
pairs.add(new BasicNameValuePair("flags", "0")); pairs.add(new BasicNameValuePair("flags", "0"));
pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On")); pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On"));
pairs.add(new BasicNameValuePair("forcedownlevel", "0")); pairs.add(new BasicNameValuePair("forcedownlevel", "0"));
@ -632,7 +630,7 @@ public class WebDavStore extends RemoteStore {
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs); UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs);
request.setEntity(formEntity); request.setEntity(formEntity);
HttpResponse response = httpClient.executeOverride(request, mContext); HttpResponse response = httpClient.executeOverride(request, httpContext);
boolean authenticated = testAuthenticationResponse(response); boolean authenticated = testAuthenticationResponse(response);
if (!authenticated) { if (!authenticated) {
// Check the response from the authentication request above for a form action. // Check the response from the authentication request above for a form action.
@ -645,7 +643,7 @@ public class WebDavStore extends RemoteStore {
request = new HttpGeneric(loginUrl); request = new HttpGeneric(loginUrl);
request.setMethod("GET"); request.setMethod("GET");
response = httpClient.executeOverride(request, mContext); response = httpClient.executeOverride(request, httpContext);
formAction = findFormAction(WebDavHttpClient.getUngzippedContent(response.getEntity())); formAction = findFormAction(WebDavHttpClient.getUngzippedContent(response.getEntity()));
} }
} }
@ -687,7 +685,7 @@ public class WebDavStore extends RemoteStore {
request.setMethod("POST"); request.setMethod("POST");
request.setEntity(formEntity); request.setEntity(formEntity);
response = httpClient.executeOverride(request, mContext); response = httpClient.executeOverride(request, httpContext);
authenticated = testAuthenticationResponse(response); authenticated = testAuthenticationResponse(response);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
Log.e(LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + WebDavUtils.processException(e)); Log.e(LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + WebDavUtils.processException(e));
@ -699,18 +697,13 @@ public class WebDavStore extends RemoteStore {
} }
if (authenticated) { if (authenticated) {
mAuthentication = WebDavConstants.AUTH_TYPE_FORM_BASED; authenticationType = WebDavConstants.AUTH_TYPE_FORM_BASED;
mCachedLoginUrl = loginUrl; cachedLoginUrl = loginUrl;
} else { } else {
throw new MessagingException("Invalid credentials provided for authentication."); throw new MessagingException("Invalid credentials provided for authentication.");
} }
} }
/**
* Searches the specified stream for an HTML form and returns the form's action target.
*
* @throws IOException
*/
private String findFormAction(InputStream istream) private String findFormAction(InputStream istream)
throws IOException { throws IOException {
String formAction = null; String formAction = null;
@ -718,9 +711,10 @@ public class WebDavStore extends RemoteStore {
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096); BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096);
String tempText; String tempText;
//TODO: Use proper HTML parsing for this
// Read line by line until we find something like: <form action="owaauth.dll"...>. // Read line by line until we find something like: <form action="owaauth.dll"...>.
while ((tempText = reader.readLine()) != null && tempText = reader.readLine();
formAction == null) { while (formAction == null) {
if (tempText.contains(" action=")) { if (tempText.contains(" action=")) {
String[] actionParts = tempText.split(" action="); String[] actionParts = tempText.split(" action=");
if (actionParts.length > 1 && actionParts[1].length() > 1) { if (actionParts.length > 1 && actionParts[1].length() > 1) {
@ -736,6 +730,7 @@ public class WebDavStore extends RemoteStore {
} }
} }
} }
tempText = reader.readLine();
} }
return formAction; return formAction;
@ -747,7 +742,7 @@ public class WebDavStore extends RemoteStore {
int statusCode = response.getStatusLine().getStatusCode(); int statusCode = response.getStatusLine().getStatusCode();
// Exchange 2007 will return a 302 status code no matter what. // Exchange 2007 will return a 302 status code no matter what.
if (((statusCode >= 200 && statusCode < 300) || statusCode == 302) && if (((statusCode >= 200 && statusCode < 300) || statusCode == 302) &&
mAuthCookies != null && !mAuthCookies.getCookies().isEmpty()) { authCookies != null && !authCookies.getCookies().isEmpty()) {
// We may be authenticated, we need to send a test request to know for sure. // We may be authenticated, we need to send a test request to know for sure.
// Exchange 2007 adds the same cookies whether the username and password were valid or not. // Exchange 2007 adds the same cookies whether the username and password were valid or not.
ConnectionInfo info = doInitialConnection(); ConnectionInfo info = doInitialConnection();
@ -760,7 +755,7 @@ public class WebDavStore extends RemoteStore {
// The redirect is in the form: https://hostname:port/owa/alias. // The redirect is in the form: https://hostname:port/owa/alias.
// Do a simple replace and compare the resulting strings. // Do a simple replace and compare the resulting strings.
try { try {
String thisPath = new URI(mUrl).getPath(); String thisPath = new URI(baseUrl).getPath();
String redirectPath = new URI(info.redirectUrl).getPath(); String redirectPath = new URI(info.redirectUrl).getPath();
if (!thisPath.endsWith("/")) { if (!thisPath.endsWith("/")) {
@ -793,31 +788,31 @@ public class WebDavStore extends RemoteStore {
} }
public CookieStore getAuthCookies() { public CookieStore getAuthCookies() {
return mAuthCookies; return authCookies;
} }
public String getAlias() { public String getAlias() {
return mAlias; return alias;
} }
public String getUrl() { public String getUrl() {
return mUrl; return baseUrl;
} }
public WebDavHttpClient getHttpClient() throws MessagingException { public WebDavHttpClient getHttpClient() throws MessagingException {
if (mHttpClient == null) { if (httpClient == null) {
mHttpClient = mHttpClientFactory.create(); httpClient = httpClientFactory.create();
// Disable automatic redirects on the http client. // Disable automatic redirects on the http client.
mHttpClient.getParams().setBooleanParameter("http.protocol.handle-redirects", false); httpClient.getParams().setBooleanParameter("http.protocol.handle-redirects", false);
// Setup a cookie store for forms-based authentication. // Setup a cookie store for forms-based authentication.
mContext = new BasicHttpContext(); httpContext = new BasicHttpContext();
mAuthCookies = new BasicCookieStore(); authCookies = new BasicCookieStore();
mContext.setAttribute(ClientContext.COOKIE_STORE, mAuthCookies); httpContext.setAttribute(ClientContext.COOKIE_STORE, authCookies);
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry(); SchemeRegistry reg = httpClient.getConnectionManager().getSchemeRegistry();
try { try {
Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, 443), 443); Scheme s = new Scheme("https", new WebDavSocketFactory(hostname, 443), 443);
reg.register(s); reg.register(s);
} catch (NoSuchAlgorithmException nsa) { } catch (NoSuchAlgorithmException nsa) {
Log.e(LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa); Log.e(LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
@ -827,11 +822,11 @@ public class WebDavStore extends RemoteStore {
throw new MessagingException("KeyManagementException in getHttpClient: " + kme); throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
} }
} }
return mHttpClient; return httpClient;
} }
protected InputStream sendRequest(String url, String method, StringEntity messageBody, protected InputStream sendRequest(String url, String method, StringEntity messageBody,
Map<String, String> headers, boolean tryAuth) Map<String, String> headers, boolean tryAuth)
throws MessagingException { throws MessagingException {
if (url == null || method == null) { if (url == null || method == null) {
return null; return null;
@ -855,16 +850,16 @@ public class WebDavStore extends RemoteStore {
} }
} }
if (mAuthentication == WebDavConstants.AUTH_TYPE_NONE) { if (authenticationType == WebDavConstants.AUTH_TYPE_NONE) {
if (!tryAuth || !authenticate()) { if (!tryAuth || !authenticate()) {
throw new MessagingException("Unable to authenticate in sendRequest()."); throw new MessagingException("Unable to authenticate in sendRequest().");
} }
} else if (mAuthentication == WebDavConstants.AUTH_TYPE_BASIC) { } else if (authenticationType == WebDavConstants.AUTH_TYPE_BASIC) {
httpMethod.setHeader("Authorization", mAuthString); httpMethod.setHeader("Authorization", authString);
} }
httpMethod.setMethod(method); httpMethod.setMethod(method);
response = httpClient.executeOverride(httpMethod, mContext); response = httpClient.executeOverride(httpMethod, httpContext);
statusCode = response.getStatusLine().getStatusCode(); statusCode = response.getStatusLine().getStatusCode();
entity = response.getEntity(); entity = response.getEntity();
@ -872,9 +867,9 @@ public class WebDavStore extends RemoteStore {
if (statusCode == 401) { if (statusCode == 401) {
throw new MessagingException("Invalid username or password for Basic authentication."); throw new MessagingException("Invalid username or password for Basic authentication.");
} else if (statusCode == 440) { } else if (statusCode == 440) {
if (tryAuth && mAuthentication == WebDavConstants.AUTH_TYPE_FORM_BASED) { if (tryAuth && authenticationType == WebDavConstants.AUTH_TYPE_FORM_BASED) {
// Our cookie expired, re-authenticate. // Our cookie expired, re-authenticate.
doFBA(null); performFormBasedAuthentication(null);
sendRequest(url, method, messageBody, headers, false); sendRequest(url, method, messageBody, headers, false);
} else { } else {
throw new MessagingException("Authentication failure in sendRequest()."); throw new MessagingException("Authentication failure in sendRequest().");
@ -904,7 +899,7 @@ public class WebDavStore extends RemoteStore {
if (response.getFirstHeader("Location") != null) { if (response.getFirstHeader("Location") != null) {
// TODO: This may indicate lack of authentication or may alternatively be something we should follow // TODO: This may indicate lack of authentication or may alternatively be something we should follow
throw new IOException("Unexpected redirect during request processing. " + throw new IOException("Unexpected redirect during request processing. " +
"Expected response from: "+url+" but told to redirect to:" + "Expected response from: " + url + " but told to redirect to:" +
response.getFirstHeader("Location").getValue()); response.getFirstHeader("Location").getValue());
} else { } else {
throw new IOException("Unexpected redirect during request processing. " + throw new IOException("Unexpected redirect during request processing. " +
@ -913,11 +908,11 @@ public class WebDavStore extends RemoteStore {
} }
public String getAuthString() { public String getAuthString() {
return mAuthString; return authString;
} }
/** /**
* Performs an httprequest to the supplied url using the supplied method. messageBody and headers are optional as * Performs an HttpRequest to the supplied url using the supplied method. messageBody and headers are optional as
* not all requests will need them. There are two signatures to support calls that don't require parsing of the * not all requests will need them. There are two signatures to support calls that don't require parsing of the
* response. * response.
*/ */
@ -927,7 +922,7 @@ public class WebDavStore extends RemoteStore {
} }
DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers, DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers,
boolean needsParsing) boolean needsParsing)
throws MessagingException { throws MessagingException {
DataSet dataset = new DataSet(); DataSet dataset = new DataSet();
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) { if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
@ -964,7 +959,8 @@ public class WebDavStore extends RemoteStore {
dataset = myHandler.getDataSet(); dataset = myHandler.getDataSet();
} catch (SAXException se) { } catch (SAXException se) {
Log.e(LOG_TAG, "SAXException in processRequest() " + se + "\nTrace: " + WebDavUtils.processException(se)); Log.e(LOG_TAG,
"SAXException in processRequest() " + se + "\nTrace: " + WebDavUtils.processException(se));
throw new MessagingException("SAXException in processRequest() ", se); throw new MessagingException("SAXException in processRequest() ", se);
} catch (ParserConfigurationException pce) { } catch (ParserConfigurationException pce) {
Log.e(LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: " Log.e(LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
@ -992,7 +988,7 @@ public class WebDavStore extends RemoteStore {
@Override @Override
public void sendMessages(List<? extends Message> messages) throws MessagingException { public void sendMessages(List<? extends Message> messages) throws MessagingException {
WebDavFolder tmpFolder = (WebDavFolder) getFolder(mStoreConfig.getDraftsFolderName()); WebDavFolder tmpFolder = getFolder(mStoreConfig.getDraftsFolderName());
try { try {
tmpFolder.open(Folder.OPEN_MODE_RW); tmpFolder.open(Folder.OPEN_MODE_RW);
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages); List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);