diff --git a/src/com/android/email/mail/store/TrustManagerFactory.java b/src/com/android/email/mail/store/TrustManagerFactory.java index acd61750d..c2174db14 100644 --- a/src/com/android/email/mail/store/TrustManagerFactory.java +++ b/src/com/android/email/mail/store/TrustManagerFactory.java @@ -107,7 +107,6 @@ public final class TrustManagerFactory { Application app = Email.app; keyStoreFile = new File(app.getDir("KeyStore", Context.MODE_PRIVATE) + File.separator + "KeyStore.bks"); keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - //TODO: read store from disk. java.io.FileInputStream fis; try { fis = new java.io.FileInputStream(keyStoreFile); @@ -116,6 +115,9 @@ public final class TrustManagerFactory { } try { keyStore.load(fis, "".toCharArray()); + //if (fis != null) { + // fis.close(); + //} } catch (IOException e) { Log.e(LOG_TAG, "KeyStore IOException while initializing TrustManagerFactory ", e); keyStore = null; diff --git a/src/com/android/email/mail/store/WebDavStore.java b/src/com/android/email/mail/store/WebDavStore.java index 6bdd354f1..d1535ec69 100644 --- a/src/com/android/email/mail/store/WebDavStore.java +++ b/src/com/android/email/mail/store/WebDavStore.java @@ -1,39 +1,51 @@ package com.android.email.mail.store; import java.io.BufferedReader; -import java.io.InputStreamReader; -import android.util.Log; - -import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; import java.io.IOException; -import java.io.OutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.text.DateFormat; -import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Stack; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import javax.xml.parsers.ParserConfigurationException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +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.CredentialsProvider; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import android.util.Log; import com.android.email.Email; import com.android.email.mail.CertificateValidationException; @@ -44,37 +56,9 @@ import com.android.email.mail.Message; import com.android.email.mail.MessageRetrievalListener; import com.android.email.mail.MessagingException; import com.android.email.mail.Store; -import com.android.email.mail.internet.MimeBodyPart; import com.android.email.mail.internet.MimeMessage; -import com.android.email.mail.internet.TextBody; -import com.android.email.mail.transport.EOLConvertingOutputStream; import com.android.email.mail.transport.TrustedSocketFactory; -import org.apache.http.HttpResponse; -import org.apache.http.HttpEntity; -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.CredentialsProvider; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.scheme.SocketFactory; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; - -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; - /** *
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch emails @@ -107,7 +91,8 @@ public class WebDavStore extends Store { private HashMapmFolderList = new HashMap (); private boolean mSecure; - + private DefaultHttpClient mHttpClient = null; + /** * webdav://user:password@server:port CONNECTION_SECURITY_NONE * webdav+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL @@ -150,9 +135,9 @@ public class WebDavStore extends Store { mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED || mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL || mConnectionSecurity == CONNECTION_SECURITY_SSL_OPTIONAL) { - this.mUrl = "https://" + mHost; + this.mUrl = "https://" + mHost + ":" + mUri.getPort(); } else { - this.mUrl = "http://" + mHost; + this.mUrl = "http://" + mHost + ":" + mUri.getPort(); } if (mUri.getUserInfo() != null) { @@ -448,7 +433,7 @@ public class WebDavStore extends Store { ArrayList pairs = new ArrayList(); pairs.add(new BasicNameValuePair("username", username)); pairs.add(new BasicNameValuePair("password", password)); - pairs.add(new BasicNameValuePair("destination", finalUrl + "/Exchange/")); + pairs.add(new BasicNameValuePair("destination", finalUrl + "/exchange/" +username+"/")); pairs.add(new BasicNameValuePair("flags", "0")); pairs.add(new BasicNameValuePair("SubmitCreds", "Log+On")); pairs.add(new BasicNameValuePair("forcedownlevel", "0")); @@ -466,7 +451,7 @@ public class WebDavStore extends Store { /** Verify success */ if (status_code < 500 && - status_code > 400) { + status_code >= 400) { String errorText = ""; String requestText = ""; if (entity != null) { @@ -483,6 +468,7 @@ public class WebDavStore extends Store { while ((tempText = reader.readLine()) != null) { requestText += tempText; } + requestText = requestText.replaceAll("password=.*?&", "password=(omitted)&"); throw new MessagingException("Error during authentication: "+ response.getStatusLine().toString()+ "\n\nRequest: "+ requestText + "\n\nResponse: " + @@ -538,25 +524,25 @@ public class WebDavStore extends Store { } public DefaultHttpClient getTrustedHttpClient() throws KeyManagementException, NoSuchAlgorithmException{ - DefaultHttpClient httpclient = new DefaultHttpClient(); - /* - SchemeRegistry reg = httpclient.getConnectionManager().getSchemeRegistry(); - reg.unregister("https"); - 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 = httpclient.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); - httpclient.setCredentialsProvider(credsProvider); - */ - return httpclient; + if (mHttpClient == null) { + mHttpClient = new DefaultHttpClient(); + 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; } /** @@ -622,13 +608,29 @@ public class WebDavStore extends Store { response = httpclient.execute(httpmethod); statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + + if (statusCode < 500 && + statusCode >= 400) { + String errorText = ""; + if (entity != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()), 8192); + String tempText = ""; + + while ((tempText = reader.readLine()) != null) { + errorText += tempText; + } + } + throw new IOException("Error during authentication: "+ + response.getStatusLine().toString()+ "\n\nRequest: "+ + messageBody + "\n\nResponse: " + + errorText); + } if (statusCode < 200 || statusCode > 300) { throw new IOException("Error processing request, returned HTTP Response Code was " + statusCode); } - entity = response.getEntity(); - if (entity != null && needsParsing) { try { @@ -1053,7 +1055,7 @@ public class WebDavStore extends Store { if (statusCode < 200 || statusCode > 300) { - throw new IOException("Status Code in invalid range"); + throw new IOException("Status Code in invalid range, URL: "+wdMessage.getUrl()); } entity = response.getEntity(); @@ -1091,7 +1093,7 @@ public class WebDavStore extends Store { } catch (URISyntaxException use) { Log.e(Email.LOG_TAG, "URISyntaxException caught " + use); } catch (IOException ioe) { - Log.e(Email.LOG_TAG, "Non-success response code loading message, response code was " + statusCode); + Log.e(Email.LOG_TAG, "Non-success response code loading message, response code was: " + statusCode + " Error: "+ioe.getMessage()); } if (listener != null) { @@ -1362,6 +1364,18 @@ public class WebDavStore extends Store { } public void setUrl(String url) { + //TODO: bleh. This is an ugly hack, but does prevent a crash if the url is relative + //FIXME: so fix, or better yet: + //XXX: prevent URLs from getting to us that are broken. + if (! (url.toLowerCase().contains(WebDavStore.this.mUrl.toLowerCase()+this.mFolder.toString().toLowerCase()))) { + if (!(url.startsWith("/"))){ + url = "/" + url; + } + url = WebDavStore.this.mUrl + this.mFolder+url; + } + if (!(url.toLowerCase().contains(WebDavStore.this.mUrl.toLowerCase()))) { + url = WebDavStore.this.mUrl + url; + } String[] urlParts = url.split("/"); int length = urlParts.length; String end = urlParts[length - 1]; diff --git a/src/com/android/email/mail/transport/TrustedSocketFactory.java b/src/com/android/email/mail/transport/TrustedSocketFactory.java index ff62f4a30..755c07d8c 100644 --- a/src/com/android/email/mail/transport/TrustedSocketFactory.java +++ b/src/com/android/email/mail/transport/TrustedSocketFactory.java @@ -9,16 +9,17 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.SocketFactory; +import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.params.HttpParams; import com.android.email.mail.store.TrustManagerFactory; -public class TrustedSocketFactory implements SocketFactory { +public class TrustedSocketFactory implements LayeredSocketFactory { private SSLSocketFactory mSocketFactory; private org.apache.http.conn.ssl.SSLSocketFactory mSchemeSocketFactory; @@ -46,5 +47,19 @@ public class TrustedSocketFactory implements SocketFactory { public boolean isSecure(Socket sock) throws IllegalArgumentException { return mSchemeSocketFactory.isSecure(sock); } - -} + public Socket createSocket( + final Socket socket, + final String host, + final int port, + final boolean autoClose + ) throws IOException, UnknownHostException { + SSLSocket sslSocket = (SSLSocket) mSocketFactory.createSocket( + socket, + host, + port, + autoClose + ); + //hostnameVerifier.verify(host, sslSocket); + // verifyHostName() didn't blowup - good! + return sslSocket; + }}