mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-31 07:10:14 -05:00
Exchange authenticaton and setup overhaul from Kris Wong
This commit is contained in:
parent
07adaaee4d
commit
c216f42eb3
@ -350,6 +350,8 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||||||
<string name="account_setup_check_settings_retr_info_msg">Retrieving account information\u2026</string>
|
<string name="account_setup_check_settings_retr_info_msg">Retrieving account information\u2026</string>
|
||||||
<string name="account_setup_check_settings_check_incoming_msg">Checking incoming server settings\u2026</string>
|
<string name="account_setup_check_settings_check_incoming_msg">Checking incoming server settings\u2026</string>
|
||||||
<string name="account_setup_check_settings_check_outgoing_msg">Checking outgoing server settings\u2026</string>
|
<string name="account_setup_check_settings_check_outgoing_msg">Checking outgoing server settings\u2026</string>
|
||||||
|
<string name="account_setup_check_settings_authenticate">Authenticating\u2026</string>
|
||||||
|
<string name="account_setup_check_settings_fetch">Fetching account settings\u2026</string>
|
||||||
<string name="account_setup_check_settings_finishing_msg">Finishing\u2026</string>
|
<string name="account_setup_check_settings_finishing_msg">Finishing\u2026</string>
|
||||||
<string name="account_setup_check_settings_canceling_msg">Canceling\u2026</string>
|
<string name="account_setup_check_settings_canceling_msg">Canceling\u2026</string>
|
||||||
|
|
||||||
@ -364,7 +366,7 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||||||
<string name="account_setup_account_type_instructions">What type of account is this?</string>
|
<string name="account_setup_account_type_instructions">What type of account is this?</string>
|
||||||
<string name="account_setup_account_type_pop_action">POP3</string>
|
<string name="account_setup_account_type_pop_action">POP3</string>
|
||||||
<string name="account_setup_account_type_imap_action">IMAP</string>
|
<string name="account_setup_account_type_imap_action">IMAP</string>
|
||||||
<string name="account_setup_account_type_webdav_action">WebDAV (Exchange)</string>
|
<string name="account_setup_account_type_webdav_action">Exchange (WebDAV)</string>
|
||||||
|
|
||||||
<string name="account_setup_incoming_title">Incoming server settings</string>
|
<string name="account_setup_incoming_title">Incoming server settings</string>
|
||||||
<string name="account_setup_incoming_username_label">Username</string>
|
<string name="account_setup_incoming_username_label">Username</string>
|
||||||
|
@ -889,7 +889,9 @@ public class MessageView extends K9Activity implements OnClickListener
|
|||||||
if (K9.mobileOptimizedLayout())
|
if (K9.mobileOptimizedLayout())
|
||||||
{
|
{
|
||||||
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
|
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ import com.fsck.k9.mail.CertificateValidationException;
|
|||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.store.TrustManagerFactory;
|
import com.fsck.k9.mail.store.TrustManagerFactory;
|
||||||
|
import com.fsck.k9.mail.store.WebDavStore;
|
||||||
|
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
@ -106,13 +108,24 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
}
|
}
|
||||||
if (mCheckIncoming)
|
if (mCheckIncoming)
|
||||||
{
|
{
|
||||||
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
|
|
||||||
store = mAccount.getRemoteStore();
|
store = mAccount.getRemoteStore();
|
||||||
|
|
||||||
|
if (store instanceof WebDavStore)
|
||||||
|
{
|
||||||
|
setMessage(R.string.account_setup_check_settings_authenticate);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setMessage(R.string.account_setup_check_settings_check_incoming_msg);
|
||||||
|
}
|
||||||
store.checkSettings();
|
store.checkSettings();
|
||||||
|
|
||||||
MessagingController.getInstance(getApplication()).listFolders(mAccount, true, null);
|
if (store instanceof WebDavStore)
|
||||||
|
{
|
||||||
|
setMessage(R.string.account_setup_check_settings_fetch);
|
||||||
|
}
|
||||||
|
MessagingController.getInstance(getApplication()).listFoldersSynchronous(mAccount, true, null);
|
||||||
MessagingController.getInstance(getApplication()).synchronizeMailbox(mAccount, K9.INBOX , null, null);
|
MessagingController.getInstance(getApplication()).synchronizeMailbox(mAccount, K9.INBOX , null, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (mDestroyed)
|
if (mDestroyed)
|
||||||
{
|
{
|
||||||
@ -125,7 +138,10 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||||||
}
|
}
|
||||||
if (mCheckOutgoing)
|
if (mCheckOutgoing)
|
||||||
{
|
{
|
||||||
setMessage(R.string.account_setup_check_settings_check_outgoing_msg);
|
if (!(mAccount.getRemoteStore() instanceof WebDavStore))
|
||||||
|
{
|
||||||
|
setMessage(R.string.account_setup_check_settings_check_outgoing_msg);
|
||||||
|
}
|
||||||
Transport transport = Transport.getInstance(mAccount);
|
Transport transport = Transport.getInstance(mAccount);
|
||||||
transport.close();
|
transport.close();
|
||||||
transport.open();
|
transport.open();
|
||||||
|
@ -409,61 +409,79 @@ public class MessagingController implements Runnable
|
|||||||
{
|
{
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
for (MessagingListener l : getListeners(listener))
|
listFoldersSynchronous(account, refreshRemote, listener);
|
||||||
{
|
|
||||||
l.listFoldersStarted(account);
|
|
||||||
}
|
|
||||||
List<? extends Folder> localFolders = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Store localStore = account.getLocalStore();
|
|
||||||
localFolders = localStore.getPersonalNamespaces(false);
|
|
||||||
|
|
||||||
Folder[] folderArray = localFolders.toArray(EMPTY_FOLDER_ARRAY);
|
|
||||||
|
|
||||||
if (refreshRemote || localFolders == null || localFolders.size() == 0)
|
|
||||||
{
|
|
||||||
doRefreshRemote(account, listener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MessagingListener l : getListeners(listener))
|
|
||||||
{
|
|
||||||
l.listFolders(account, folderArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
for (MessagingListener l : getListeners(listener))
|
|
||||||
{
|
|
||||||
l.listFoldersFailed(account, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
addErrorMessage(account, null, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (localFolders != null)
|
|
||||||
{
|
|
||||||
for (Folder localFolder : localFolders)
|
|
||||||
{
|
|
||||||
if (localFolder != null)
|
|
||||||
{
|
|
||||||
localFolder.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MessagingListener l : getListeners(listener))
|
|
||||||
{
|
|
||||||
l.listFoldersFinished(account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists folders that are available locally and remotely. This method calls
|
||||||
|
* listFoldersCallback for local folders before it returns, and then for
|
||||||
|
* remote folders at some later point. If there are no local folders
|
||||||
|
* includeRemote is forced by this method. This method is called in the
|
||||||
|
* foreground.
|
||||||
|
* TODO this needs to cache the remote folder list
|
||||||
|
*
|
||||||
|
* @param account
|
||||||
|
* @param includeRemote
|
||||||
|
* @param listener
|
||||||
|
* @throws MessagingException
|
||||||
|
*/
|
||||||
|
public void listFoldersSynchronous(final Account account, final boolean refreshRemote, final MessagingListener listener)
|
||||||
|
{
|
||||||
|
for (MessagingListener l : getListeners(listener))
|
||||||
|
{
|
||||||
|
l.listFoldersStarted(account);
|
||||||
|
}
|
||||||
|
List<? extends Folder> localFolders = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Store localStore = account.getLocalStore();
|
||||||
|
localFolders = localStore.getPersonalNamespaces(false);
|
||||||
|
|
||||||
|
Folder[] folderArray = localFolders.toArray(EMPTY_FOLDER_ARRAY);
|
||||||
|
|
||||||
|
if (refreshRemote || localFolders == null || localFolders.size() == 0)
|
||||||
|
{
|
||||||
|
doRefreshRemote(account, listener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MessagingListener l : getListeners(listener))
|
||||||
|
{
|
||||||
|
l.listFolders(account, folderArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
for (MessagingListener l : getListeners(listener))
|
||||||
|
{
|
||||||
|
l.listFoldersFailed(account, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
addErrorMessage(account, null, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (localFolders != null)
|
||||||
|
{
|
||||||
|
for (Folder localFolder : localFolders)
|
||||||
|
{
|
||||||
|
if (localFolder != null)
|
||||||
|
{
|
||||||
|
localFolder.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MessagingListener l : getListeners(listener))
|
||||||
|
{
|
||||||
|
l.listFoldersFinished(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void doRefreshRemote(final Account account, MessagingListener listener)
|
private void doRefreshRemote(final Account account, MessagingListener listener)
|
||||||
{
|
{
|
||||||
put("doRefreshRemote", listener, new Runnable()
|
put("doRefreshRemote", listener, new Runnable()
|
||||||
|
@ -12,20 +12,19 @@ import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
|||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.transport.TrustedSocketFactory;
|
import com.fsck.k9.mail.transport.TrustedSocketFactory;
|
||||||
import org.apache.http.*;
|
import org.apache.http.*;
|
||||||
import org.apache.http.auth.AuthScope;
|
|
||||||
import org.apache.http.auth.Credentials;
|
|
||||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
|
||||||
import org.apache.http.client.CookieStore;
|
import org.apache.http.client.CookieStore;
|
||||||
import org.apache.http.client.CredentialsProvider;
|
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.client.protocol.ClientContext;
|
||||||
import org.apache.http.conn.scheme.Scheme;
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.BasicCookieStore;
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
import org.apache.http.protocol.ExecutionContext;
|
import org.apache.http.protocol.ExecutionContext;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
@ -34,7 +33,6 @@ import org.xml.sax.SAXException;
|
|||||||
import org.xml.sax.XMLReader;
|
import org.xml.sax.XMLReader;
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.parsers.SAXParser;
|
import javax.xml.parsers.SAXParser;
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
@ -65,11 +63,17 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class WebDavStore extends Store
|
public class WebDavStore extends Store
|
||||||
{
|
{
|
||||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
// Security options
|
||||||
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
private static final short CONNECTION_SECURITY_NONE = 0;
|
||||||
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
private static final short CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
||||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
private static final short CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
||||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
private static final short CONNECTION_SECURITY_SSL_OPTIONAL = 3;
|
||||||
|
private static final short CONNECTION_SECURITY_SSL_REQUIRED = 4;
|
||||||
|
|
||||||
|
// Authentication types
|
||||||
|
private static final short AUTH_TYPE_NONE = 0;
|
||||||
|
private static final short AUTH_TYPE_BASIC = 1;
|
||||||
|
private static final short AUTH_TYPE_FORM_BASED = 2;
|
||||||
|
|
||||||
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.ANSWERED };
|
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN, Flag.ANSWERED };
|
||||||
|
|
||||||
@ -77,9 +81,12 @@ public class WebDavStore extends Store
|
|||||||
|
|
||||||
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
||||||
|
|
||||||
private int mConnectionSecurity;
|
private static final String DAV_MAIL_SEND_FOLDER = "##DavMailSubmissionURI##";
|
||||||
|
private static final String DAV_MAIL_TMP_FOLDER = "drafts";
|
||||||
|
|
||||||
|
private short mConnectionSecurity;
|
||||||
private String mUsername; /* Stores the username for authentications */
|
private String mUsername; /* Stores the username for authentications */
|
||||||
private String alias;
|
private String mAlias; /* Stores the alias for the user's mailbox */
|
||||||
private String mPassword; /* Stores the password for authentications */
|
private String mPassword; /* Stores the password for authentications */
|
||||||
private String mUrl; /* Stores the base URL for the server */
|
private String mUrl; /* Stores the base URL for the server */
|
||||||
private String mHost; /* Stores the host name for the server */
|
private String mHost; /* Stores the host name for the server */
|
||||||
@ -89,18 +96,16 @@ public class WebDavStore extends Store
|
|||||||
private URI mUri; /* Stores the Uniform Resource Indicator with all connection info */
|
private URI mUri; /* Stores the Uniform Resource Indicator with all connection info */
|
||||||
private String mRedirectUrl;
|
private String mRedirectUrl;
|
||||||
private String mAuthString;
|
private String mAuthString;
|
||||||
private static String DAV_MAIL_SEND_FOLDER = "##DavMailSubmissionURI##";
|
|
||||||
private static String DAV_MAIL_TMP_FOLDER = "drafts";
|
|
||||||
|
|
||||||
|
private boolean mSecure;
|
||||||
private CookieStore mAuthCookies; /* Stores cookies from authentication */
|
private WebDavHttpClient mHttpClient = null;
|
||||||
private boolean mAuthenticated = false; /* Stores authentication state */
|
private HttpContext mContext = null;
|
||||||
private long mLastAuth = -1; /* Stores the timestamp of last auth */
|
private CookieStore mAuthCookies = null;
|
||||||
|
private short mAuthentication = AUTH_TYPE_NONE;
|
||||||
|
private long mLastAuth = -1;
|
||||||
private long mAuthTimeout = 5 * 60;
|
private long mAuthTimeout = 5 * 60;
|
||||||
|
|
||||||
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||||
private boolean mSecure;
|
|
||||||
private WebDavHttpClient mHttpClient = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
||||||
@ -121,6 +126,7 @@ public class WebDavStore extends Store
|
|||||||
{
|
{
|
||||||
throw new MessagingException("Invalid WebDavStore URI", use);
|
throw new MessagingException("Invalid WebDavStore URI", use);
|
||||||
}
|
}
|
||||||
|
|
||||||
String scheme = mUri.getScheme();
|
String scheme = mUri.getScheme();
|
||||||
if (scheme.equals("webdav"))
|
if (scheme.equals("webdav"))
|
||||||
{
|
{
|
||||||
@ -157,6 +163,34 @@ public class WebDavStore extends Store
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mUri.getUserInfo() != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String[] userInfoParts = mUri.getUserInfo().split(":");
|
||||||
|
mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||||
|
String userParts[] = mUsername.split("\\\\", 2);
|
||||||
|
|
||||||
|
if (userParts.length > 1)
|
||||||
|
{
|
||||||
|
mAlias = userParts[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mAlias = mUsername;
|
||||||
|
}
|
||||||
|
if (userInfoParts.length > 1)
|
||||||
|
{
|
||||||
|
mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException enc)
|
||||||
|
{
|
||||||
|
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||||
|
Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String[] pathParts = mUri.getPath().split("\\|");
|
String[] pathParts = mUri.getPath().split("\\|");
|
||||||
|
|
||||||
for (int i = 0, count = pathParts.length; i < count; i++)
|
for (int i = 0, count = pathParts.length; i < count; i++)
|
||||||
@ -202,43 +236,27 @@ public class WebDavStore extends Store
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String path = mPath;
|
|
||||||
if (path.length() > 0 && !path.startsWith("/"))
|
if (!this.mPath.equals("") &&
|
||||||
|
this.mPath.startsWith("/"))
|
||||||
{
|
{
|
||||||
path = "/" + mPath;
|
mPath = "/" + mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mUrl = getRoot() + path;
|
if (this.mMailboxPath == null ||
|
||||||
|
this.mMailboxPath.equals(""))
|
||||||
if (mUri.getUserInfo() != null)
|
|
||||||
{
|
{
|
||||||
try
|
this.mMailboxPath = "/Exchange/" + this.mAlias;
|
||||||
{
|
|
||||||
String[] userInfoParts = mUri.getUserInfo().split(":");
|
|
||||||
mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
|
||||||
String userParts[] = mUsername.split("/", 2);
|
|
||||||
|
|
||||||
if (userParts.length > 1)
|
|
||||||
{
|
|
||||||
alias = userParts[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alias = mUsername;
|
|
||||||
}
|
|
||||||
if (userInfoParts.length > 1)
|
|
||||||
{
|
|
||||||
mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (UnsupportedEncodingException enc)
|
|
||||||
{
|
|
||||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
|
||||||
Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
else if (!this.mMailboxPath.startsWith("/"))
|
||||||
mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword);
|
{
|
||||||
|
mMailboxPath = "/" + mMailboxPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mUrl = getRoot() + mPath + mMailboxPath;
|
||||||
|
|
||||||
|
this.mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||||
|
this.mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRoot()
|
private String getRoot()
|
||||||
@ -259,11 +277,10 @@ public class WebDavStore extends Store
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkSettings() throws MessagingException
|
public void checkSettings() throws MessagingException
|
||||||
{
|
{
|
||||||
Log.e(K9.LOG_TAG, "WebDavStore.checkSettings() not implemented");
|
authenticate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -531,16 +548,58 @@ public class WebDavStore extends Store
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs Form Based authentication regardless of the current
|
* Determines which type of authentication Exchange is using and
|
||||||
* authentication state
|
* authenticates appropriately.
|
||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public void authenticate() throws MessagingException
|
public boolean authenticate() throws MessagingException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
doFBA();
|
if (mAuthentication == AUTH_TYPE_NONE)
|
||||||
//this.mAuthCookies = doAuthentication(this.mUsername, this.mPassword, this.mUrl);
|
{
|
||||||
|
ConnectionInfo info = doInitialConnection();
|
||||||
|
|
||||||
|
if (info.requiredAuthType == AUTH_TYPE_BASIC)
|
||||||
|
{
|
||||||
|
HttpGeneric request = new HttpGeneric(mUrl);
|
||||||
|
request.setMethod("GET");
|
||||||
|
request.setHeader("Authorization", mAuthString);
|
||||||
|
|
||||||
|
WebDavHttpClient httpClient = new WebDavHttpClient();
|
||||||
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
||||||
|
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode >= 200 && statusCode < 300)
|
||||||
|
{
|
||||||
|
mAuthentication = AUTH_TYPE_BASIC;
|
||||||
|
mLastAuth = System.currentTimeMillis() / 1000;
|
||||||
|
}
|
||||||
|
else if (statusCode == 401)
|
||||||
|
{
|
||||||
|
throw new MessagingException("Invalid username or password for authentication.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MessagingException("Error with code " + response.getStatusLine().getStatusCode() +
|
||||||
|
" during request processing: " + response.getStatusLine().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (info.requiredAuthType == AUTH_TYPE_FORM_BASED)
|
||||||
|
{
|
||||||
|
doFBA(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mAuthentication == AUTH_TYPE_BASIC)
|
||||||
|
{
|
||||||
|
// Nothing to do, we authenticate with every request when
|
||||||
|
// using basic authentication.
|
||||||
|
}
|
||||||
|
else if (mAuthentication == AUTH_TYPE_FORM_BASED)
|
||||||
|
{
|
||||||
|
// Our cookie expired, re-authenticate.
|
||||||
|
doFBA(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException ioe)
|
catch (IOException ioe)
|
||||||
{
|
{
|
||||||
@ -548,143 +607,154 @@ public class WebDavStore extends Store
|
|||||||
throw new MessagingException("Error during authentication", ioe);
|
throw new MessagingException("Error during authentication", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mAuthCookies == null)
|
return mAuthentication != AUTH_TYPE_NONE;
|
||||||
{
|
|
||||||
this.mAuthenticated = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.mAuthenticated = true;
|
|
||||||
this.mLastAuth = System.currentTimeMillis()/1000;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a new authentication is needed.
|
* Makes the initial connection to Exchange for authentication.
|
||||||
* Returns true if new authentication is needed.
|
* Determines the type of authentication necessary for the server.
|
||||||
*/
|
|
||||||
public boolean needAuth()
|
|
||||||
{
|
|
||||||
boolean status = false;
|
|
||||||
long currentTime = -1;
|
|
||||||
if (!this.mAuthenticated)
|
|
||||||
{
|
|
||||||
status = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTime = System.currentTimeMillis()/1000;
|
|
||||||
if ((currentTime - this.mLastAuth) > (this.mAuthTimeout))
|
|
||||||
{
|
|
||||||
status = true;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getHttpRequestResponse(HttpEntity request, HttpEntity response) throws IllegalStateException, IOException
|
|
||||||
{
|
|
||||||
String responseText = "";
|
|
||||||
String requestText = "";
|
|
||||||
if (response != null)
|
|
||||||
{
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(WebDavHttpClient.getUngzippedContent(response)), 8192);
|
|
||||||
String tempText = "";
|
|
||||||
|
|
||||||
while ((tempText = reader.readLine()) != null)
|
|
||||||
{
|
|
||||||
responseText += tempText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request != null)
|
|
||||||
{
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(WebDavHttpClient.getUngzippedContent(response)), 8192);
|
|
||||||
String tempText = "";
|
|
||||||
|
|
||||||
while ((tempText = reader.readLine()) != null)
|
|
||||||
{
|
|
||||||
requestText += tempText;
|
|
||||||
}
|
|
||||||
requestText = requestText.replaceAll("password=.*?&", "password=(omitted)&");
|
|
||||||
}
|
|
||||||
return "Request: " + requestText +
|
|
||||||
"\n\nResponse: " + responseText;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the Form Based Authentication
|
|
||||||
* Returns the CookieStore object for later use or null
|
|
||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public void doFBA() throws IOException, MessagingException
|
private ConnectionInfo doInitialConnection() throws MessagingException
|
||||||
{
|
{
|
||||||
/* public CookieStore doAuthentication(String username, String password,
|
// For our initial connection we are sending an empty GET request to
|
||||||
String url) throws IOException, MessagingException {*/
|
// the configured URL, which should be in the following form:
|
||||||
String authPath;
|
// https://mail.server.com/Exchange/alias
|
||||||
String url = this.mUrl;
|
//
|
||||||
String username = this.mUsername;
|
// Possible status codes include:
|
||||||
String password = this.mPassword;
|
// 401 - the server uses basic authentication
|
||||||
String[] urlParts = url.split("/");
|
// 30x - the server is trying to redirect us to an OWA login
|
||||||
String finalUrl = "";
|
// 20x - success
|
||||||
String loginUrl = "";
|
//
|
||||||
String destinationUrl = "";
|
// The latter two indicate form-based authentication.
|
||||||
|
ConnectionInfo info = new ConnectionInfo();
|
||||||
|
|
||||||
if (this.mAuthPath != null &&
|
WebDavHttpClient httpClient = getHttpClient();
|
||||||
!this.mAuthPath.equals("") &&
|
|
||||||
!this.mAuthPath.equals("/"))
|
|
||||||
{
|
|
||||||
authPath = this.mAuthPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
authPath = "/exchweb/bin/auth/owaauth.dll";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i <= 2; i++)
|
HttpGeneric request = new HttpGeneric(mUrl);
|
||||||
{
|
request.setMethod("GET");
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
finalUrl = finalUrl + "/" + urlParts[i];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
finalUrl = urlParts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finalUrl.equals(""))
|
|
||||||
{
|
|
||||||
throw new MessagingException("doFBA failed, unable to construct URL to post login credentials to.");
|
|
||||||
}
|
|
||||||
|
|
||||||
loginUrl = finalUrl + authPath;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
/* Browser Client */
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
||||||
WebDavHttpClient httpclient = mHttpClient;
|
info.statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
|
||||||
/**
|
if (info.statusCode == 401)
|
||||||
* This is in a separate block because I really don't like how it's done.
|
|
||||||
* This basically scrapes the OWA login page for the form submission URL.
|
|
||||||
* UGLY!WebDavHttpClient
|
|
||||||
* Added an if-check to see if there's a user supplied authentication path for FBA
|
|
||||||
*/
|
|
||||||
if (this.mAuthPath == null ||
|
|
||||||
this.mAuthPath.equals("") ||
|
|
||||||
this.mAuthPath.equals("/"))
|
|
||||||
{
|
{
|
||||||
|
// 401 is the "Unauthorized" status code, meaning the server wants
|
||||||
|
// an authentication header for basic authentication.
|
||||||
|
info.requiredAuthType = AUTH_TYPE_BASIC;
|
||||||
|
}
|
||||||
|
else if ((info.statusCode >= 200 && info.statusCode < 300) || // Success
|
||||||
|
(info.statusCode >= 300 && info.statusCode < 400) || // Redirect
|
||||||
|
(info.statusCode == 440)) // Unauthorized
|
||||||
|
|
||||||
httpclient.addRequestInterceptor(new HttpRequestInterceptor()
|
{
|
||||||
|
// We will handle all 3 situations the same. First we take an educated
|
||||||
|
// guess at where the authorization DLL is located. If this is this
|
||||||
|
// doesn't work, then we'll use the redirection URL for OWA login given
|
||||||
|
// to use by exchange. We can use this to scrape the location of the
|
||||||
|
// authorization URL.
|
||||||
|
info.requiredAuthType = AUTH_TYPE_FORM_BASED;
|
||||||
|
|
||||||
|
if (mAuthPath != null && !mAuthPath.equals(""))
|
||||||
{
|
{
|
||||||
public void process(HttpRequest request, HttpContext context)
|
// The user specified their own authentication path, use that.
|
||||||
throws HttpException, IOException
|
info.guessedAuthUrl = getRoot() + mAuthPath;
|
||||||
{
|
}
|
||||||
mRedirectUrl = ((HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST)).toURI() + request.getRequestLine().getUri();
|
else
|
||||||
}
|
{
|
||||||
});
|
// Use the default path to the authentication dll.
|
||||||
HashMap<String, String> headers = new HashMap<String, String>();
|
info.guessedAuthUrl = getRoot() + "/exchweb/bin/auth/owaauth.dll";
|
||||||
InputStream istream = sendRequest(finalUrl, "GET", null, headers, false);
|
}
|
||||||
|
|
||||||
|
// Determine where the server is trying to redirect us.
|
||||||
|
Header location = response.getFirstHeader("Location");
|
||||||
|
if (location != null)
|
||||||
|
{
|
||||||
|
info.redirectUrl = location.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IOException("Error with code " + info.statusCode + " during request processing: "+
|
||||||
|
response.getStatusLine().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
|
||||||
|
throw new MessagingException("IOException", ioe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs form-based authentication.
|
||||||
|
* @throws MessagingException
|
||||||
|
*/
|
||||||
|
public void doFBA(ConnectionInfo info) throws IOException, MessagingException
|
||||||
|
{
|
||||||
|
WebDavHttpClient httpClient = getHttpClient();
|
||||||
|
|
||||||
|
HttpGeneric request = new HttpGeneric(info.guessedAuthUrl);
|
||||||
|
request.setMethod("POST");
|
||||||
|
|
||||||
|
// Build the POST data.
|
||||||
|
ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
|
||||||
|
pairs.add(new BasicNameValuePair("destination", mUrl));
|
||||||
|
pairs.add(new BasicNameValuePair("username", mUsername));
|
||||||
|
pairs.add(new BasicNameValuePair("password", mPassword));
|
||||||
|
pairs.add(new BasicNameValuePair("flags", "0"));
|
||||||
|
pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On"));
|
||||||
|
pairs.add(new BasicNameValuePair("forcedownlevel", "0"));
|
||||||
|
pairs.add(new BasicNameValuePair("trusted", "0"));
|
||||||
|
|
||||||
|
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs);
|
||||||
|
request.setEntity(formEntity);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.executeOverride(request, mContext);
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
|
||||||
|
if (statusCode >= 200 && statusCode < 300)
|
||||||
|
{
|
||||||
|
// Success, we're logged on and an authentication cookie should
|
||||||
|
// have already been added to mAuthCookies for us.
|
||||||
|
}
|
||||||
|
else if (statusCode == 404)
|
||||||
|
{
|
||||||
|
// The resource was not found, which means we need to get tricky
|
||||||
|
// about finding the correct login path.
|
||||||
|
// Send a request to our original redirect URL, and scrape the
|
||||||
|
// login path from the returned page.
|
||||||
|
httpClient.addRequestInterceptor(new HttpRequestInterceptor()
|
||||||
|
{
|
||||||
|
public void process(HttpRequest request, HttpContext context)
|
||||||
|
throws HttpException, IOException
|
||||||
|
{
|
||||||
|
mRedirectUrl = ((HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST)).toURI() +
|
||||||
|
request.getRequestLine().getUri();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String loginUrl = "";
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
loginUrl = info.redirectUrl;
|
||||||
|
}
|
||||||
|
else if (mRedirectUrl != null && !mRedirectUrl.equals(""))
|
||||||
|
{
|
||||||
|
loginUrl = mRedirectUrl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MessagingException("No valid login URL available for form-based authentication.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InputStream istream = sendRequest(loginUrl, "GET", null, null, false);
|
||||||
if (istream != null)
|
if (istream != null)
|
||||||
{
|
{
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096);
|
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096);
|
||||||
@ -705,104 +775,36 @@ public class WebDavStore extends Store
|
|||||||
mRedirectUrl = mRedirectUrl.substring(0, mRedirectUrl.lastIndexOf('?'));
|
mRedirectUrl = mRedirectUrl.substring(0, mRedirectUrl.lastIndexOf('?'));
|
||||||
mRedirectUrl = mRedirectUrl.substring(0, mRedirectUrl.lastIndexOf('/'));
|
mRedirectUrl = mRedirectUrl.substring(0, mRedirectUrl.lastIndexOf('/'));
|
||||||
loginUrl = mRedirectUrl + "/" + tagParts[1];
|
loginUrl = mRedirectUrl + "/" + tagParts[1];
|
||||||
this.mAuthPath = "/" + tagParts[1];
|
this.mAuthPath = new URI(loginUrl).getPath();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loginUrl = finalUrl + tagParts[1];
|
loginUrl = getRoot() + tagParts[1];
|
||||||
this.mAuthPath = "/" + tagParts[1];
|
this.mAuthPath = new URI(loginUrl).getPath();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempText.indexOf("destination") >= 0)
|
|
||||||
{
|
|
||||||
String[] tagParts = tempText.split("value");
|
|
||||||
if (tagParts[1] != null)
|
|
||||||
{
|
|
||||||
String[] valueParts = tagParts[1].split("\"");
|
|
||||||
destinationUrl = valueParts[1];
|
|
||||||
matched = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
istream.close();
|
istream.close();
|
||||||
|
|
||||||
|
// Now retry the login using our scraped login URL.
|
||||||
|
request = new HttpGeneric(loginUrl);
|
||||||
|
request.setMethod("POST");
|
||||||
|
request.setEntity(formEntity);
|
||||||
|
|
||||||
|
httpClient.executeOverride(request, mContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(URISyntaxException use)
|
||||||
|
|
||||||
/** Build the POST data to use */
|
|
||||||
ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
|
|
||||||
pairs.add(new BasicNameValuePair("username", username));
|
|
||||||
pairs.add(new BasicNameValuePair("password", password));
|
|
||||||
if (this.mMailboxPath != null &&
|
|
||||||
!this.mMailboxPath.equals(""))
|
|
||||||
{
|
{
|
||||||
pairs.add(new BasicNameValuePair("destination", finalUrl + this.mMailboxPath));
|
throw new MessagingException("An invalid login URL was detected: " + loginUrl);
|
||||||
}
|
|
||||||
else if (destinationUrl != null &&
|
|
||||||
!destinationUrl.equals(""))
|
|
||||||
{
|
|
||||||
pairs.add(new BasicNameValuePair("destination", destinationUrl));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pairs.add(new BasicNameValuePair("destination", "/"));
|
|
||||||
}
|
|
||||||
pairs.add(new BasicNameValuePair("flags", "0"));
|
|
||||||
pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On"));
|
|
||||||
pairs.add(new BasicNameValuePair("forcedownlevel", "0"));
|
|
||||||
pairs.add(new BasicNameValuePair("trusted", "0"));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs);
|
|
||||||
HashMap<String, String> headers = new HashMap<String, String>();
|
|
||||||
String tempUrl = "";
|
|
||||||
InputStream istream = sendRequest(loginUrl, "POST", formEntity, headers, false);
|
|
||||||
|
|
||||||
/** Get the URL for the mailbox and set it for the store */
|
|
||||||
if (istream != null)
|
|
||||||
{
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 8192);
|
|
||||||
String tempText = "";
|
|
||||||
|
|
||||||
while ((tempText = reader.readLine()) != null)
|
|
||||||
{
|
|
||||||
if (tempText.indexOf("BASE href") >= 0)
|
|
||||||
{
|
|
||||||
String[] tagParts = tempText.split("\"");
|
|
||||||
tempUrl = tagParts[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.mMailboxPath != null &&
|
|
||||||
!this.mMailboxPath.equals(""))
|
|
||||||
{
|
|
||||||
this.mUrl = finalUrl + "/" + this.mMailboxPath + "/";
|
|
||||||
}
|
|
||||||
else if (tempUrl.equals(""))
|
|
||||||
{
|
|
||||||
this.mUrl = finalUrl + "/Exchange/" + this.alias + "/";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.mUrl = tempUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (UnsupportedEncodingException uee)
|
|
||||||
{
|
|
||||||
Log.e(K9.LOG_TAG, "Error encoding POST data for authentication: " + uee + "\nTrace: " + processException(uee));
|
|
||||||
throw new MessagingException("Error encoding POST data for authentication", uee);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SSLException e)
|
|
||||||
|
if (mAuthCookies != null && !mAuthCookies.getCookies().isEmpty())
|
||||||
{
|
{
|
||||||
throw new CertificateValidationException(e.getMessage(), e);
|
mAuthentication = AUTH_TYPE_FORM_BASED;
|
||||||
|
mLastAuth = System.currentTimeMillis() / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mAuthenticated = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CookieStore getAuthCookies()
|
public CookieStore getAuthCookies()
|
||||||
@ -812,7 +814,7 @@ public class WebDavStore extends Store
|
|||||||
|
|
||||||
public String getAlias()
|
public String getAlias()
|
||||||
{
|
{
|
||||||
return alias;
|
return mAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUrl()
|
public String getUrl()
|
||||||
@ -822,100 +824,38 @@ public class WebDavStore extends Store
|
|||||||
|
|
||||||
public WebDavHttpClient getHttpClient() throws MessagingException
|
public WebDavHttpClient getHttpClient() throws MessagingException
|
||||||
{
|
{
|
||||||
SchemeRegistry reg;
|
|
||||||
Scheme s;
|
|
||||||
boolean needAuth = false;
|
|
||||||
|
|
||||||
if (mHttpClient == null)
|
if (mHttpClient == null)
|
||||||
{
|
{
|
||||||
mHttpClient = new WebDavHttpClient();
|
mHttpClient = new WebDavHttpClient();
|
||||||
needAuth = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
// Setup a cookie store for forms-based authentication.
|
||||||
try
|
mContext = new BasicHttpContext();
|
||||||
{
|
mAuthCookies = new BasicCookieStore();
|
||||||
// Log.i(K9.LOG_TAG, "getHttpClient mHost = " + mHost);
|
mContext.setAttribute(ClientContext.COOKIE_STORE, mAuthCookies);
|
||||||
s = new Scheme("https", new TrustedSocketFactory(mHost, mSecure), 443);
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException nsa)
|
|
||||||
{
|
|
||||||
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
|
|
||||||
throw new MessagingException("NoSuchAlgorithmException in getHttpClient: " + nsa);
|
|
||||||
}
|
|
||||||
catch (KeyManagementException kme)
|
|
||||||
{
|
|
||||||
Log.e(K9.LOG_TAG, "KeyManagementException in getHttpClient: " + kme);
|
|
||||||
throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
|
|
||||||
}
|
|
||||||
reg.register(s);
|
|
||||||
|
|
||||||
if (needAuth)
|
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
||||||
{
|
try
|
||||||
HashMap<String, String> headers = new HashMap<String, String>();
|
{
|
||||||
processRequest(this.mUrl, "GET", null, headers, false);
|
Scheme s = new Scheme("https", new TrustedSocketFactory(mHost, mSecure), 443);
|
||||||
}
|
reg.register(s);
|
||||||
|
}
|
||||||
/*
|
catch (NoSuchAlgorithmException nsa)
|
||||||
if (needAuth()) {
|
{
|
||||||
if (!checkAuth()) {
|
Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
|
||||||
try {
|
throw new MessagingException("NoSuchAlgorithmException in getHttpClient: " + nsa);
|
||||||
CookieStore cookies = mHttpClient.getCookieStore();
|
}
|
||||||
cookies.clear();
|
catch (KeyManagementException kme)
|
||||||
mHttpClient.setCookieStore(cookies);
|
{
|
||||||
cookies = doAuthentication(this.mUsername, this.mPassword, this.mUrl);
|
Log.e(K9.LOG_TAG, "KeyManagementException in getHttpClient: " + kme);
|
||||||
if (cookies != null) {
|
throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
|
||||||
this.mAuthenticated = true;
|
|
||||||
this.mLastAuth = System.currentTimeMillis()/1000;
|
|
||||||
}
|
|
||||||
mHttpClient.setCookieStore(cookies);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Credentials creds = new UsernamePasswordCredentials(mUsername, mPassword);
|
|
||||||
CredentialsProvider credsProvider = mHttpClient.getCredentialsProvider();
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, 80, AuthScope.ANY_REALM), creds);
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, 443, AuthScope.ANY_REALM), creds);
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, mUri.getPort(), AuthScope.ANY_REALM), creds);
|
|
||||||
mHttpClient.setCredentialsProvider(credsProvider);
|
|
||||||
// Assume we're authenticated and ok here since the checkAuth() was 401 and we've now set the credentials
|
|
||||||
this.mAuthenticated = true;
|
|
||||||
this.mLastAuth = System.currentTimeMillis()/1000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
return mHttpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebDavHttpClient getTrustedHttpClient() throws KeyManagementException, NoSuchAlgorithmException
|
|
||||||
{
|
|
||||||
if (mHttpClient == null)
|
|
||||||
{
|
|
||||||
mHttpClient = new WebDavHttpClient();
|
|
||||||
SchemeRegistry reg = mHttpClient.getConnectionManager().getSchemeRegistry();
|
|
||||||
Scheme s = new Scheme("https",new TrustedSocketFactory(mHost,mSecure),443);
|
|
||||||
reg.register(s);
|
|
||||||
|
|
||||||
|
|
||||||
//Add credentials for NTLM/Digest/Basic Auth
|
|
||||||
Credentials creds = new UsernamePasswordCredentials(mUsername, mPassword);
|
|
||||||
CredentialsProvider credsProvider = mHttpClient.getCredentialsProvider();
|
|
||||||
// setting AuthScope for 80 and 443, in case we end up getting redirected
|
|
||||||
// from 80 to 443.
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, 80, AuthScope.ANY_REALM), creds);
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, 443, AuthScope.ANY_REALM), creds);
|
|
||||||
credsProvider.setCredentials(new AuthScope(mHost, mUri.getPort(), AuthScope.ANY_REALM), creds);
|
|
||||||
mHttpClient.setCredentialsProvider(credsProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mHttpClient;
|
return mHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream sendRequest(String url, String method, StringEntity messageBody, HashMap<String, String> headers, boolean tryAuth)
|
private InputStream sendRequest(String url, String method, StringEntity messageBody, HashMap<String, String> headers, boolean tryAuth)
|
||||||
throws MessagingException
|
throws MessagingException
|
||||||
{
|
{
|
||||||
WebDavHttpClient httpclient;
|
|
||||||
InputStream istream = null;
|
InputStream istream = null;
|
||||||
|
|
||||||
if (url == null ||
|
if (url == null ||
|
||||||
@ -924,7 +864,7 @@ public class WebDavStore extends Store
|
|||||||
return istream;
|
return istream;
|
||||||
}
|
}
|
||||||
|
|
||||||
httpclient = getHttpClient();
|
WebDavHttpClient httpclient = getHttpClient();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -943,56 +883,46 @@ public class WebDavStore extends Store
|
|||||||
httpmethod.setHeader(headerName, headers.get(headerName));
|
httpmethod.setHeader(headerName, headers.get(headerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAuthString != null && mAuthenticated)
|
if (mAuthentication == AUTH_TYPE_NONE)
|
||||||
|
{
|
||||||
|
if (!tryAuth || !authenticate())
|
||||||
|
{
|
||||||
|
throw new MessagingException("Unable to authenticate in sendRequest().");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mAuthentication == AUTH_TYPE_BASIC)
|
||||||
{
|
{
|
||||||
httpmethod.setHeader("Authorization", mAuthString);
|
httpmethod.setHeader("Authorization", mAuthString);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpmethod.setMethod(method);
|
httpmethod.setMethod(method);
|
||||||
response = httpclient.executeOverride(httpmethod);
|
response = httpclient.executeOverride(httpmethod, mContext);
|
||||||
statusCode = response.getStatusLine().getStatusCode();
|
statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
|
||||||
entity = response.getEntity();
|
entity = response.getEntity();
|
||||||
|
|
||||||
if (statusCode == 401)
|
if (statusCode == 401)
|
||||||
{
|
{
|
||||||
if (tryAuth)
|
throw new MessagingException("Invalid username or password for Basic authentication.");
|
||||||
{
|
|
||||||
mAuthenticated = true;
|
|
||||||
sendRequest(url, method, messageBody, headers, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new MessagingException("Invalid username or password for Basic authentication");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (statusCode == 440)
|
else if (statusCode == 440)
|
||||||
{
|
{
|
||||||
if (tryAuth)
|
if (tryAuth && mAuthentication == AUTH_TYPE_FORM_BASED)
|
||||||
{
|
{
|
||||||
doFBA();
|
// Our cookie expired, re-authenticate.
|
||||||
|
doFBA(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().");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (statusCode < 200 ||
|
else if (statusCode < 200 || statusCode >= 300)
|
||||||
statusCode >= 300)
|
|
||||||
{
|
{
|
||||||
throw new IOException("Error with code " + statusCode + " during request processing: "+
|
throw new IOException("Error with code " + statusCode + " during request processing: " +
|
||||||
response.getStatusLine().toString());
|
response.getStatusLine().toString());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tryAuth &&
|
|
||||||
!mAuthenticated)
|
|
||||||
{
|
|
||||||
doFBA();
|
|
||||||
sendRequest(url, method, messageBody, headers, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity != null)
|
if (entity != null)
|
||||||
{
|
{
|
||||||
@ -1284,10 +1214,9 @@ public class WebDavStore extends Store
|
|||||||
Log.i(K9.LOG_TAG, "Moving " + messages.length + " messages to " + destFolder.mFolderUrl);
|
Log.i(K9.LOG_TAG, "Moving " + messages.length + " messages to " + destFolder.mFolderUrl);
|
||||||
|
|
||||||
processRequest(mFolderUrl, action, messageBody, headers, false);
|
processRequest(mFolderUrl, action, messageBody, headers, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMessageCount(boolean read, CookieStore authCookies) throws MessagingException
|
private int getMessageCount(boolean read) throws MessagingException
|
||||||
{
|
{
|
||||||
String isRead;
|
String isRead;
|
||||||
int messageCount = 0;
|
int messageCount = 0;
|
||||||
@ -1319,8 +1248,7 @@ public class WebDavStore extends Store
|
|||||||
public int getMessageCount() throws MessagingException
|
public int getMessageCount() throws MessagingException
|
||||||
{
|
{
|
||||||
open(OpenMode.READ_WRITE);
|
open(OpenMode.READ_WRITE);
|
||||||
this.mMessageCount = getMessageCount(true, WebDavStore.this.mAuthCookies);
|
this.mMessageCount = getMessageCount(true);
|
||||||
|
|
||||||
return this.mMessageCount;
|
return this.mMessageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1328,10 +1256,10 @@ public class WebDavStore extends Store
|
|||||||
public int getUnreadMessageCount() throws MessagingException
|
public int getUnreadMessageCount() throws MessagingException
|
||||||
{
|
{
|
||||||
open(OpenMode.READ_WRITE);
|
open(OpenMode.READ_WRITE);
|
||||||
this.mUnreadMessageCount = getMessageCount(false, WebDavStore.this.mAuthCookies);
|
this.mUnreadMessageCount = getMessageCount(false);
|
||||||
|
|
||||||
return this.mUnreadMessageCount;
|
return this.mUnreadMessageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFlaggedMessageCount() throws MessagingException
|
public int getFlaggedMessageCount() throws MessagingException
|
||||||
{
|
{
|
||||||
@ -1586,11 +1514,11 @@ public class WebDavStore extends Store
|
|||||||
HttpEntity entity;
|
HttpEntity entity;
|
||||||
|
|
||||||
httpget.setHeader("translate", "f");
|
httpget.setHeader("translate", "f");
|
||||||
if (mAuthString != null && mAuthenticated)
|
if (mAuthentication == AUTH_TYPE_BASIC)
|
||||||
{
|
{
|
||||||
httpget.setHeader("Authorization", mAuthString);
|
httpget.setHeader("Authorization", mAuthString);
|
||||||
}
|
}
|
||||||
response = httpclient.executeOverride(httpget);
|
response = httpclient.executeOverride(httpget, mContext);
|
||||||
|
|
||||||
statusCode = response.getStatusLine().getStatusCode();
|
statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
|
||||||
@ -1965,7 +1893,7 @@ public class WebDavStore extends Store
|
|||||||
httpmethod.setHeader("Authorization", mAuthString);
|
httpmethod.setHeader("Authorization", mAuthString);
|
||||||
}
|
}
|
||||||
|
|
||||||
response = httpclient.executeOverride(httpmethod);
|
response = httpclient.executeOverride(httpmethod, mContext);
|
||||||
statusCode = response.getStatusLine().getStatusCode();
|
statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
|
||||||
if (statusCode < 200 ||
|
if (statusCode < 200 ||
|
||||||
@ -2619,12 +2547,22 @@ public class WebDavStore extends Store
|
|||||||
return responseStream;
|
return responseStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpResponse executeOverride(HttpUriRequest request, HttpContext context)
|
||||||
public HttpResponse executeOverride(HttpUriRequest request) throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
modifyRequestToAcceptGzipResponse(request);
|
modifyRequestToAcceptGzipResponse(request);
|
||||||
return super.execute(request);
|
return super.execute(request, context);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple data container for passing connection information.
|
||||||
|
*/
|
||||||
|
private class ConnectionInfo
|
||||||
|
{
|
||||||
|
public int statusCode;
|
||||||
|
public short requiredAuthType;
|
||||||
|
public String guessedAuthUrl;
|
||||||
|
public String redirectUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,41 +8,23 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
|
||||||
import com.fsck.k9.mail.store.WebDavStore;
|
import com.fsck.k9.mail.store.WebDavStore;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
public class WebDavTransport extends Transport
|
public class WebDavTransport extends Transport
|
||||||
{
|
{
|
||||||
public static final int CONNECTION_SECURITY_NONE = 0;
|
|
||||||
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
|
||||||
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
|
||||||
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
|
||||||
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
|
||||||
|
|
||||||
String host;
|
|
||||||
int mPort;
|
|
||||||
|
|
||||||
boolean mSecure;
|
|
||||||
Socket mSocket;
|
|
||||||
PeekableInputStream mIn;
|
|
||||||
OutputStream mOut;
|
|
||||||
private WebDavStore store;
|
private WebDavStore store;
|
||||||
|
|
||||||
/**
|
|
||||||
* webdav://user:password@server:port CONNECTION_SECURITY_NONE
|
|
||||||
* webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
|
||||||
* webdav+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
|
||||||
* webdav+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
|
||||||
* webdav+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
|
||||||
*
|
|
||||||
* @param _uri
|
|
||||||
*/
|
|
||||||
public WebDavTransport(Account account) throws MessagingException
|
public WebDavTransport(Account account) throws MessagingException
|
||||||
{
|
{
|
||||||
store = new WebDavStore(account);
|
if (account.getRemoteStore() instanceof WebDavStore)
|
||||||
|
{
|
||||||
|
store = (WebDavStore) account.getRemoteStore();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
store = new WebDavStore(account);
|
||||||
|
}
|
||||||
|
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete");
|
Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete");
|
||||||
}
|
}
|
||||||
@ -64,10 +46,6 @@ public class WebDavTransport extends Transport
|
|||||||
@Override
|
@Override
|
||||||
public void sendMessage(Message message) throws MessagingException
|
public void sendMessage(Message message) throws MessagingException
|
||||||
{
|
{
|
||||||
|
|
||||||
store.sendMessages(new Message[] { message });
|
store.sendMessages(new Message[] { message });
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user