split SSL hostname verification to a separate option, do not call
trustAllHosts() on every API request, fix support for older tt-rss versions which do not have getApiLevel call
This commit is contained in:
parent
53a1c5d5fe
commit
aa22d8f8f3
@ -28,7 +28,7 @@
|
|||||||
<string name="share_article">Share article</string>
|
<string name="share_article">Share article</string>
|
||||||
<string name="catchup">Mark read</string>
|
<string name="catchup">Mark read</string>
|
||||||
<string name="sort_feeds_by_unread">Sort feeds by unread count</string>
|
<string name="sort_feeds_by_unread">Sort feeds by unread count</string>
|
||||||
<string name="ssl_trust_any">Accept any SSL certificate</string>
|
<string name="ssl_trust_any">Accept any certificate</string>
|
||||||
<string name="category_browse_feeds">Browse feeds</string>
|
<string name="category_browse_feeds">Browse feeds</string>
|
||||||
<string name="category_browse_articles">Browse articles</string>
|
<string name="category_browse_articles">Browse articles</string>
|
||||||
<string name="blank"></string>
|
<string name="blank"></string>
|
||||||
@ -137,4 +137,10 @@
|
|||||||
<string name="donate_thanks">Donation found, thank you for support!</string>
|
<string name="donate_thanks">Donation found, thank you for support!</string>
|
||||||
<string name="use_volume_keys">Use volume buttons</string>
|
<string name="use_volume_keys">Use volume buttons</string>
|
||||||
<string name="use_volume_keys_long">Switch between articles with hardware volume buttons</string>
|
<string name="use_volume_keys_long">Switch between articles with hardware volume buttons</string>
|
||||||
|
<string name="ssl_trust_any_host">No hostname verification</string>
|
||||||
|
<string name="error_api_unknown_method">Error: unknown API method</string>
|
||||||
|
<string name="ssl_trust_any_long">Accepts any SSL certificate without validation</string>
|
||||||
|
<string name="ssl_trust_any_host_long">Do not verify hostnames over SSL</string>
|
||||||
|
<string name="ssl">SSL</string>
|
||||||
|
<string name="error_ssl_hostname_rejected">Error: SSL hostname not verified</string>
|
||||||
</resources>
|
</resources>
|
@ -22,11 +22,20 @@
|
|||||||
android:summary="@string/ttrss_url_summary"
|
android:summary="@string/ttrss_url_summary"
|
||||||
android:title="@string/ttrss_url" >
|
android:title="@string/ttrss_url" >
|
||||||
</EditTextPreference>
|
</EditTextPreference>
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/ssl" android:summary="blah blah blah" >
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="ssl_trust_any"
|
android:key="ssl_trust_any"
|
||||||
|
android:summary="@string/ssl_trust_any_long"
|
||||||
android:title="@string/ssl_trust_any" />
|
android:title="@string/ssl_trust_any" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="ssl_trust_any_host"
|
||||||
|
android:summary="@string/ssl_trust_any_host_long"
|
||||||
|
android:title="@string/ssl_trust_any_host" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/http_authentication" >
|
<PreferenceCategory android:title="@string/http_authentication" >
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
|
@ -42,14 +42,13 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
private final String TAG = this.getClass().getSimpleName();
|
private final String TAG = this.getClass().getSimpleName();
|
||||||
|
|
||||||
public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND,
|
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,
|
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, INCORRECT_USAGE, NETWORK_UNAVAILABLE };
|
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_OK = 0;
|
||||||
public static final int API_STATUS_ERR = 1;
|
public static final int API_STATUS_ERR = 1;
|
||||||
|
|
||||||
private String m_api;
|
private String m_api;
|
||||||
private boolean m_trustAny = false;
|
|
||||||
private boolean m_transportDebugging = false;
|
private boolean m_transportDebugging = false;
|
||||||
protected int m_responseCode = 0;
|
protected int m_responseCode = 0;
|
||||||
protected String m_responseMessage;
|
protected String m_responseMessage;
|
||||||
@ -66,7 +65,6 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
m_prefs = PreferenceManager.getDefaultSharedPreferences(m_context);
|
m_prefs = PreferenceManager.getDefaultSharedPreferences(m_context);
|
||||||
|
|
||||||
m_api = m_prefs.getString("ttrss_url", null).trim();
|
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_transportDebugging = m_prefs.getBoolean("transport_debugging", false);
|
||||||
m_lastError = ApiError.NO_ERROR;
|
m_lastError = ApiError.NO_ERROR;
|
||||||
|
|
||||||
@ -88,6 +86,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
return R.string.error_http_other_error;
|
return R.string.error_http_other_error;
|
||||||
case SSL_REJECTED:
|
case SSL_REJECTED:
|
||||||
return R.string.error_ssl_rejected;
|
return R.string.error_ssl_rejected;
|
||||||
|
case SSL_HOSTNAME_REJECTED:
|
||||||
|
return R.string.error_ssl_hostname_rejected;
|
||||||
case PARSE_ERROR:
|
case PARSE_ERROR:
|
||||||
return R.string.error_parse_error;
|
return R.string.error_parse_error;
|
||||||
case IO_ERROR:
|
case IO_ERROR:
|
||||||
@ -98,11 +98,13 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
return R.string.error_api_disabled;
|
return R.string.error_api_disabled;
|
||||||
case API_UNKNOWN:
|
case API_UNKNOWN:
|
||||||
return R.string.error_api_unknown;
|
return R.string.error_api_unknown;
|
||||||
|
case API_UNKNOWN_METHOD:
|
||||||
|
return R.string.error_api_unknown_method;
|
||||||
case LOGIN_FAILED:
|
case LOGIN_FAILED:
|
||||||
return R.string.error_login_failed;
|
return R.string.error_login_failed;
|
||||||
case INVALID_URL:
|
case INVALID_URL:
|
||||||
return R.string.error_invalid_api_url;
|
return R.string.error_invalid_api_url;
|
||||||
case INCORRECT_USAGE:
|
case API_INCORRECT_USAGE:
|
||||||
return R.string.error_api_incorrect_usage;
|
return R.string.error_api_incorrect_usage;
|
||||||
case NETWORK_UNAVAILABLE:
|
case NETWORK_UNAVAILABLE:
|
||||||
return R.string.error_network_unavailable;
|
return R.string.error_network_unavailable;
|
||||||
@ -133,11 +135,12 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
disableConnectionReuseIfNecessary();
|
/* disableConnectionReuseIfNecessary(); */
|
||||||
|
|
||||||
if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api);
|
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;
|
URL url;
|
||||||
|
|
||||||
@ -218,7 +221,9 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
} else if (error.equals("NOT_LOGGED_IN")) {
|
} else if (error.equals("NOT_LOGGED_IN")) {
|
||||||
m_lastError = ApiError.LOGIN_FAILED;
|
m_lastError = ApiError.LOGIN_FAILED;
|
||||||
} else if (error.equals("INCORRECT_USAGE")) {
|
} 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 {
|
} else {
|
||||||
Log.d(TAG, "Unknown API error: " + error);
|
Log.d(TAG, "Unknown API error: " + error);
|
||||||
m_lastError = ApiError.API_UNKNOWN;
|
m_lastError = ApiError.API_UNKNOWN;
|
||||||
@ -250,6 +255,13 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
m_lastError = ApiError.IO_ERROR;
|
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();
|
e.printStackTrace();
|
||||||
} catch (com.google.gson.JsonSyntaxException e) {
|
} catch (com.google.gson.JsonSyntaxException e) {
|
||||||
m_lastError = ApiError.PARSE_ERROR;
|
m_lastError = ApiError.PARSE_ERROR;
|
||||||
@ -262,7 +274,9 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void trustAllHosts() {
|
protected static void trustAllHosts(boolean trustAnyCert, boolean trustAnyHost) {
|
||||||
|
try {
|
||||||
|
if (trustAnyCert) {
|
||||||
X509TrustManager easyTrustManager = new X509TrustManager() {
|
X509TrustManager easyTrustManager = new X509TrustManager() {
|
||||||
|
|
||||||
public void checkClientTrusted(
|
public void checkClientTrusted(
|
||||||
@ -287,18 +301,22 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
|
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
|
||||||
|
|
||||||
// Install the all-trusting trust manager
|
// Install the all-trusting trust manager
|
||||||
try {
|
|
||||||
SSLContext sc = SSLContext.getInstance("TLS");
|
SSLContext sc = SSLContext.getInstance("TLS");
|
||||||
|
|
||||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||||
|
|
||||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trustAnyHost) {
|
||||||
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
|
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -306,7 +324,7 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private static void disableConnectionReuseIfNecessary() {
|
protected static void disableConnectionReuseIfNecessary() {
|
||||||
// HTTP connection reuse which was buggy pre-froyo
|
// HTTP connection reuse which was buggy pre-froyo
|
||||||
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
|
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
|
||||||
System.setProperty("http.keepAlive", "false");
|
System.setProperty("http.keepAlive", "false");
|
||||||
|
@ -585,65 +585,13 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
|
|||||||
return null;
|
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) {
|
protected void downloadFile(String fetchUrl, String outputFile) {
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS");
|
AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS");
|
||||||
|
|
||||||
disableConnectionReuseIfNecessary();
|
/* ApiRequest.disableConnectionReuseIfNecessary(); */
|
||||||
|
|
||||||
if (m_prefs.getBoolean("ssl_trust_any", false)) {
|
/* ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false),
|
||||||
trustAllHosts();
|
m_prefs.getBoolean("ssl_trust_any_host", false)); */
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = new URL(fetchUrl);
|
URL url = new URL(fetchUrl);
|
||||||
|
@ -128,6 +128,8 @@ public class OnlineActivity extends CommonActivity {
|
|||||||
m_prefs = PreferenceManager
|
m_prefs = PreferenceManager
|
||||||
.getDefaultSharedPreferences(getApplicationContext());
|
.getDefaultSharedPreferences(getApplicationContext());
|
||||||
|
|
||||||
|
ApiRequest.disableConnectionReuseIfNecessary();
|
||||||
|
|
||||||
if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
|
if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
|
||||||
setTheme(R.style.DarkTheme);
|
setTheme(R.style.DarkTheme);
|
||||||
} else {
|
} else {
|
||||||
@ -862,6 +864,9 @@ public class OnlineActivity extends CommonActivity {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false),
|
||||||
|
m_prefs.getBoolean("ssl_trust_any_host", false));
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS);
|
filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS);
|
||||||
filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS);
|
filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS);
|
||||||
@ -1276,7 +1281,9 @@ public class OnlineActivity extends CommonActivity {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
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);
|
setLoadingStatus(getErrorMessage(), false);
|
||||||
loginFailure();
|
loginFailure();
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user