diff --git a/res/values/strings.xml b/res/values/strings.xml index 194de703..d57cae6f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -28,7 +28,7 @@ Share article Mark read Sort feeds by unread count - Accept any SSL certificate + Accept any certificate Browse feeds Browse articles @@ -137,4 +137,10 @@ Donation found, thank you for support! Use volume buttons Switch between articles with hardware volume buttons + No hostname verification + Error: unknown API method + Accepts any SSL certificate without validation + Do not verify hostnames over SSL + SSL + Error: SSL hostname not verified \ No newline at end of file diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index a98cd8ad..648114bb 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -22,11 +22,20 @@ android:summary="@string/ttrss_url_summary" android:title="@string/ttrss_url" > - + + + + + , Integer, JsonE private final String TAG = this.getClass().getSimpleName(); public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, - HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, - API_UNKNOWN, LOGIN_FAILED, INVALID_URL, INCORRECT_USAGE, NETWORK_UNAVAILABLE }; + HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, SSL_HOSTNAME_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, + API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD }; public static final int API_STATUS_OK = 0; public static final int API_STATUS_ERR = 1; private String m_api; - private boolean m_trustAny = false; private boolean m_transportDebugging = false; protected int m_responseCode = 0; protected String m_responseMessage; @@ -66,7 +65,6 @@ public class ApiRequest extends AsyncTask, Integer, JsonE m_prefs = PreferenceManager.getDefaultSharedPreferences(m_context); m_api = m_prefs.getString("ttrss_url", null).trim(); - m_trustAny = m_prefs.getBoolean("ssl_trust_any", false); m_transportDebugging = m_prefs.getBoolean("transport_debugging", false); m_lastError = ApiError.NO_ERROR; @@ -88,6 +86,8 @@ public class ApiRequest extends AsyncTask, Integer, JsonE return R.string.error_http_other_error; case SSL_REJECTED: return R.string.error_ssl_rejected; + case SSL_HOSTNAME_REJECTED: + return R.string.error_ssl_hostname_rejected; case PARSE_ERROR: return R.string.error_parse_error; case IO_ERROR: @@ -98,11 +98,13 @@ public class ApiRequest extends AsyncTask, Integer, JsonE return R.string.error_api_disabled; case API_UNKNOWN: return R.string.error_api_unknown; + case API_UNKNOWN_METHOD: + return R.string.error_api_unknown_method; case LOGIN_FAILED: return R.string.error_login_failed; case INVALID_URL: return R.string.error_invalid_api_url; - case INCORRECT_USAGE: + case API_INCORRECT_USAGE: return R.string.error_api_incorrect_usage; case NETWORK_UNAVAILABLE: return R.string.error_network_unavailable; @@ -133,11 +135,12 @@ public class ApiRequest extends AsyncTask, Integer, JsonE return null; } - disableConnectionReuseIfNecessary(); + /* disableConnectionReuseIfNecessary(); */ if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api); - if (m_trustAny) trustAllHosts(); + /* ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), + m_prefs.getBoolean("ssl_trust_any_host", false)); */ URL url; @@ -218,7 +221,9 @@ public class ApiRequest extends AsyncTask, Integer, JsonE } else if (error.equals("NOT_LOGGED_IN")) { m_lastError = ApiError.LOGIN_FAILED; } else if (error.equals("INCORRECT_USAGE")) { - m_lastError = ApiError.INCORRECT_USAGE; + m_lastError = ApiError.API_INCORRECT_USAGE; + } else if (error.equals("UNKNOWN_METHOD")) { + m_lastError = ApiError.API_UNKNOWN_METHOD; } else { Log.d(TAG, "Unknown API error: " + error); m_lastError = ApiError.API_UNKNOWN; @@ -250,6 +255,13 @@ public class ApiRequest extends AsyncTask, Integer, JsonE e.printStackTrace(); } catch (IOException e) { m_lastError = ApiError.IO_ERROR; + + if (e.getMessage() != null) { + if (e.getMessage().matches("Hostname [^ ]+ was not verified")) { + m_lastError = ApiError.SSL_HOSTNAME_REJECTED; + } + } + e.printStackTrace(); } catch (com.google.gson.JsonSyntaxException e) { m_lastError = ApiError.PARSE_ERROR; @@ -262,43 +274,49 @@ public class ApiRequest extends AsyncTask, Integer, JsonE return null; } - private static void trustAllHosts() { - X509TrustManager easyTrustManager = new X509TrustManager() { - - public void checkClientTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public void checkServerTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - }; - - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; - - // Install the all-trusting trust manager + protected static void trustAllHosts(boolean trustAnyCert, boolean trustAnyHost) { try { - SSLContext sc = SSLContext.getInstance("TLS"); + if (trustAnyCert) { + X509TrustManager easyTrustManager = new X509TrustManager() { - sc.init(null, trustAllCerts, new java.security.SecureRandom()); + public void checkClientTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + // Oh, I am easy! + } - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); + public void checkServerTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + // Oh, I am easy! + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + }; + + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; + + // Install the all-trusting trust manager + + SSLContext sc = SSLContext.getInstance("TLS"); + + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } + + if (trustAnyHost) { + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + } } catch (Exception e) { e.printStackTrace(); @@ -306,7 +324,7 @@ public class ApiRequest extends AsyncTask, Integer, JsonE } @SuppressWarnings("deprecation") - private static void disableConnectionReuseIfNecessary() { + protected static void disableConnectionReuseIfNecessary() { // HTTP connection reuse which was buggy pre-froyo if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); diff --git a/src/org/fox/ttrss/FeedsFragment.java b/src/org/fox/ttrss/FeedsFragment.java index 88538399..056932b3 100644 --- a/src/org/fox/ttrss/FeedsFragment.java +++ b/src/org/fox/ttrss/FeedsFragment.java @@ -585,65 +585,13 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh return null; } - private void trustAllHosts() { - X509TrustManager easyTrustManager = new X509TrustManager() { - - public void checkClientTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public void checkServerTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - }; - - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; - - // Install the all-trusting trust manager - try { - SSLContext sc = SSLContext.getInstance("TLS"); - - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - @SuppressWarnings("deprecation") - private void disableConnectionReuseIfNecessary() { - // HTTP connection reuse which was buggy pre-froyo - if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { - System.setProperty("http.keepAlive", "false"); - } - } - protected void downloadFile(String fetchUrl, String outputFile) { AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS"); - disableConnectionReuseIfNecessary(); + /* ApiRequest.disableConnectionReuseIfNecessary(); */ - if (m_prefs.getBoolean("ssl_trust_any", false)) { - trustAllHosts(); - } + /* ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), + m_prefs.getBoolean("ssl_trust_any_host", false)); */ try { URL url = new URL(fetchUrl); diff --git a/src/org/fox/ttrss/OnlineActivity.java b/src/org/fox/ttrss/OnlineActivity.java index f0bcb320..f8abb5e5 100644 --- a/src/org/fox/ttrss/OnlineActivity.java +++ b/src/org/fox/ttrss/OnlineActivity.java @@ -128,6 +128,8 @@ public class OnlineActivity extends CommonActivity { m_prefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); + ApiRequest.disableConnectionReuseIfNecessary(); + if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) { setTheme(R.style.DarkTheme); } else { @@ -862,6 +864,9 @@ public class OnlineActivity extends CommonActivity { public void onResume() { super.onResume(); + ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), + m_prefs.getBoolean("ssl_trust_any_host", false)); + IntentFilter filter = new IntentFilter(); filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS); filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS); @@ -1276,7 +1281,9 @@ public class OnlineActivity extends CommonActivity { } catch (Exception e) { e.printStackTrace(); } - } else { + } else if (m_lastError != ApiError.API_UNKNOWN_METHOD) { + // Unknown method means old tt-rss, in that case we assume API 0 and continue + setLoadingStatus(getErrorMessage(), false); loginFailure(); return;