From aa697d11ad4ff9e5246e1ed6cfbc45f506b116f1 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Mon, 30 Mar 2020 23:01:54 +0200 Subject: [PATCH 01/24] Adding, customizing and modifying MemorizingTrustManager. --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 1 + .../mian/gitnex/activities/LoginActivity.java | 9 +- .../mian/gitnex/clients/IssuesService.java | 62 +- .../gitnex/clients/PullRequestsService.java | 62 +- .../mian/gitnex/clients/RetrofitClient.java | 63 +- .../java/org/mian/gitnex/ssl/MTMDecision.java | 33 + .../mian/gitnex/ssl/MemorizingActivity.java | 59 ++ .../gitnex/ssl/MemorizingTrustManager.java | 692 ++++++++++++++++++ app/src/main/res/values/strings.xml | 15 + 10 files changed, 911 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java create mode 100644 app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java create mode 100644 app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java diff --git a/app/build.gradle b/app/build.gradle index d6ea30b5..5e3b8124 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,4 +79,5 @@ dependencies { implementation "com.github.chrisbanes:PhotoView:2.3.0" implementation "com.pddstudio:highlightjs-android:1.5.0" implementation "com.github.barteksc:android-pdf-viewer:3.2.0-beta.1" + implementation 'org.conscrypt:conscrypt-android:2.2.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a3415c93..6de240b1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 17ff3cef..1b6aef02 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -21,6 +21,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import com.tooltip.Tooltip; +import org.jetbrains.annotations.NotNull; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.Toasty; @@ -454,12 +455,9 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener } @Override - public void onFailure(@NonNull Call callVersion, Throwable t) { - - Log.e("onFailure-version", t.toString()); - + public void onFailure(@NonNull Call callVersion, @NotNull Throwable throwable) { + Log.e("onFailure-version", throwable.toString()); } - }); } @@ -483,7 +481,6 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener if (response.isSuccessful()) { if (response.code() == 200) { - tinyDb.putBoolean("loggedInMode", true); assert userDetails != null; tinyDb.putString(userDetails.getLogin() + "-token", loginToken_); diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index e8393d25..d65afc57 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -2,9 +2,14 @@ package org.mian.gitnex.clients; import android.content.Context; import androidx.annotation.NonNull; +import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; +import java.security.SecureRandom; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; import okhttp3.Cache; import okhttp3.Interceptor; import okhttp3.OkHttpClient; @@ -20,42 +25,47 @@ import retrofit2.converter.gson.GsonConverterFactory; public class IssuesService { - public static S createService(Class serviceClass, String instanceURL, Context ctx) { + public static S createService(Class serviceClass, String instanceURL, Context context) { - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + final boolean connToInternet = AppUtil.haveNetworkConnection(context); + File httpCacheDirectory = new File(context.getCacheDir(), "responses"); int cacheSize = 50 * 1024 * 1024; // 50MB Cache cache = new Cache(httpCacheDirectory, cacheSize); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - OkHttpClient okHttpClient = new OkHttpClient.Builder() - .cache(cache) - //.addInterceptor(logging) - .addInterceptor(new Interceptor() { - @NonNull - @Override public Response intercept(@NonNull Chain chain) throws IOException { - Request request = chain.request(); - if (connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } - return chain.proceed(request); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); + + OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + + @NonNull + @Override + public Response intercept(@NonNull Chain chain) throws IOException { + + Request request = chain.request(); + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); } - }) - .build(); + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } + return chain.proceed(request); + } + }).build(); - Retrofit.Builder builder = new Retrofit.Builder() - .baseUrl(instanceURL) - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); - Retrofit retrofit = builder.build(); - - return retrofit.create(serviceClass); + Retrofit retrofit = builder.build(); + return retrofit.create(serviceClass); + } catch(Exception e) { + e.printStackTrace(); + } + return null; } - } diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index 0f5f8ad3..c54077ef 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -2,9 +2,14 @@ package org.mian.gitnex.clients; import android.content.Context; import androidx.annotation.NonNull; +import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; +import java.security.SecureRandom; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; import okhttp3.Cache; import okhttp3.Interceptor; import okhttp3.OkHttpClient; @@ -20,42 +25,47 @@ import retrofit2.converter.gson.GsonConverterFactory; public class PullRequestsService { - public static S createService(Class serviceClass, String instanceURL, Context ctx) { + public static S createService(Class serviceClass, String instanceURL, Context context) { - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + final boolean connToInternet = AppUtil.haveNetworkConnection(context); + File httpCacheDirectory = new File(context.getCacheDir(), "responses"); int cacheSize = 50 * 1024 * 1024; // 50MB Cache cache = new Cache(httpCacheDirectory, cacheSize); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - OkHttpClient okHttpClient = new OkHttpClient.Builder() - .cache(cache) - //.addInterceptor(logging) - .addInterceptor(new Interceptor() { - @NonNull - @Override public Response intercept(@NonNull Chain chain) throws IOException { - Request request = chain.request(); - if (connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } - return chain.proceed(request); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); + + OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + + @NonNull + @Override + public Response intercept(@NonNull Chain chain) throws IOException { + + Request request = chain.request(); + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); } - }) - .build(); + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } + return chain.proceed(request); + } + }).build(); - Retrofit.Builder builder = new Retrofit.Builder() - .baseUrl(instanceURL) - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); - Retrofit retrofit = builder.build(); - - return retrofit.create(serviceClass); + Retrofit retrofit = builder.build(); + return retrofit.create(serviceClass); + } catch(Exception e) { + e.printStackTrace(); + } + return null; } - } diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index c127c63c..7c19f895 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -1,16 +1,17 @@ package org.mian.gitnex.clients; import android.content.Context; -import androidx.annotation.NonNull; import org.mian.gitnex.interfaces.ApiInterface; +import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; -import java.io.IOException; +import java.security.SecureRandom; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; import okhttp3.Cache; -import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; -import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; @@ -24,41 +25,47 @@ public class RetrofitClient { private Retrofit retrofit; - private RetrofitClient(String instanceUrl, Context ctx) { - - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + private RetrofitClient(String instanceUrl, Context context) { + final boolean connToInternet = AppUtil.haveNetworkConnection(context); int cacheSize = 50 * 1024 * 1024; // 50MB - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + File httpCacheDirectory = new File(context.getCacheDir(), "responses"); Cache cache = new Cache(httpCacheDirectory, cacheSize); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - OkHttpClient okHttpClient = new OkHttpClient.Builder() - .cache(cache) - //.addInterceptor(logging) - .addInterceptor(new Interceptor() { - @NonNull - @Override public Response intercept(@NonNull Chain chain) throws IOException { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + sslContext.init(null, new X509TrustManager[] { memorizingTrustManager }, new SecureRandom()); + + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() + .cache(cache) + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) + .addInterceptor(chain -> { Request request = chain.request(); - if (connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } + + request = (connToInternet) ? + request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build() : + request.newBuilder().header("Cache-Control", + "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + return chain.proceed(request); - } - }) - .build(); + }); - Retrofit.Builder builder = new Retrofit.Builder() - .baseUrl(instanceUrl) - .client(okHttpClient) - .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder() + .baseUrl(instanceUrl) + .client(okHttpClient.build()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()); - retrofit = builder.build(); + retrofit = builder.build(); + } catch(Exception e) { + e.printStackTrace(); + } } public static synchronized RetrofitClient getInstance(String instanceUrl, Context ctx) { diff --git a/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java b/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java new file mode 100644 index 00000000..9eba8ee2 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java @@ -0,0 +1,33 @@ +/* MemorizingTrustManager - a TrustManager which asks the user about invalid + * certificates and memorizes their decision. + * + * Copyright (c) 2010 Georg Lukas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.mian.gitnex.ssl; + +class MTMDecision { + public final static int DECISION_INVALID = 0; + public final static int DECISION_ABORT = 1; + public final static int DECISION_ONCE = 2; + public final static int DECISION_ALWAYS = 3; + + int state = DECISION_INVALID; +} diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java new file mode 100644 index 00000000..b2da9f45 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java @@ -0,0 +1,59 @@ +/* MemorizingTrustManager - a TrustManager which asks the user about invalid + * certificates and memorizes their decision. + * + * Copyright (c) 2010 Georg Lukas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.mian.gitnex.ssl; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Intent; +import android.os.Bundle; +import org.mian.gitnex.R; + +public class MemorizingActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + int decisionId = intent.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID); + int titleId = intent.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert); + String cert = intent.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT); + + AlertDialog.Builder builder = new AlertDialog.Builder(MemorizingActivity.this); + builder.setTitle(titleId); + builder.setMessage(cert); + + builder.setPositiveButton(R.string.mtm_decision_always, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ALWAYS)); + builder.setNeutralButton(R.string.mtm_decision_once, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ONCE)); + builder.setNegativeButton(R.string.mtm_decision_abort, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); + builder.setOnCancelListener(dialog -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); + + builder.create().show(); + } + + private void onSendResult(int decisionId, int decision) { + MemorizingTrustManager.interactResult(decisionId, decision); + finish(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java new file mode 100644 index 00000000..44f65716 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java @@ -0,0 +1,692 @@ +/* MemorizingTrustManager - a TrustManager which asks the user about invalid + * certificates and memorizes their decision. + * + * Copyright (c) 2010 Georg Lukas + * + * MemorizingTrustManager.java contains the actual trust manager and interface + * code to create a MemorizingActivity and obtain the results. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.mian.gitnex.ssl; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.util.Base64; +import android.util.SparseArray; +import android.os.Build; +import android.os.Handler; + +import androidx.core.app.NotificationCompat; +import org.mian.gitnex.R; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.cert.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * A X509 trust manager implementation which asks the user about invalid + * certificates and memorizes their decision. + *

+ * The certificate validity is checked using the system default X509 + * TrustManager, creating a query Dialog if the check fails. + *

+ * WARNING: This only works if a dedicated thread is used for + * opening sockets! + */ +public class MemorizingTrustManager implements X509TrustManager { + private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; + final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; + final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert"; + + private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName()); + final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId"; + private final static int NOTIFICATION_ID = 100509; + + private final static String KEYSTORE_NAME = "keystore"; + private final static String KEYSTORE_KEY = "keystore"; + + private Context context; + private Activity foregroundAct; + private NotificationManager notificationManager; + private static int decisionId = 0; + private static final SparseArray openDecisions = new SparseArray<>(); + + private Handler masterHandler; + private SharedPreferences keyStoreStorage; + private KeyStore appKeyStore; + private X509TrustManager defaultTrustManager; + private X509TrustManager appTrustManager; + + /** Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager. + * + * You need to supply the application context. This has to be one of: + * - Application + * - Activity + * - Service + * + * The context is used for file management, to display the dialog / + * notification and for obtaining translated strings. + * + * @param m Context for the application. + * @param defaultTrustManager Delegate trust management to this TM. If null, the user must accept every certificate. + */ + public MemorizingTrustManager(Context m, X509TrustManager defaultTrustManager) { + init(m); + this.appTrustManager = getTrustManager(appKeyStore); + this.defaultTrustManager = defaultTrustManager; + } + + /** Creates an instance of the MemorizingTrustManager class using the system X509TrustManager. + * + * You need to supply the application context. This has to be one of: + * - Application + * - Activity + * - Service + * + * The context is used for file management, to display the dialog / + * notification and for obtaining translated strings. + * + * @param m Context for the application. + */ + public MemorizingTrustManager(Context m) { + init(m); + this.appTrustManager = getTrustManager(appKeyStore); + this.defaultTrustManager = getTrustManager(null); + } + + private void init(Context m) { + context = m; + masterHandler = new Handler(m.getMainLooper()); + notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + keyStoreStorage = m.getSharedPreferences(KEYSTORE_NAME, Context.MODE_PRIVATE); + appKeyStore = loadAppKeyStore(); + } + + + /** + * Returns a X509TrustManager list containing a new instance of + * TrustManagerFactory. + * + * This function is meant for convenience only. You can use it + * as follows to integrate TrustManagerFactory for HTTPS sockets: + * + *

+	 *     SSLContext sc = SSLContext.getInstance("TLS");
+	 *     sc.init(null, MemorizingTrustManager.getInstanceList(this),
+	 *         new java.security.SecureRandom());
+	 *     HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+	 * 
+ * @param c Activity or Service to show the Dialog / Notification + */ + public static X509TrustManager[] getInstanceList(Context c) { + return new X509TrustManager[] { new MemorizingTrustManager(c) }; + } + + /** + * Binds an Activity to the MTM for displaying the query dialog. + * + * This is useful if your connection is run from a service that is + * triggered by user interaction -- in such cases the activity is + * visible and the user tends to ignore the service notification. + * + * You should never have a hidden activity bound to MTM! Use this + * function in onResume() and @see unbindDisplayActivity in onPause(). + * + * @param act Activity to be bound + */ + private void bindDisplayActivity(Activity act) { + foregroundAct = act; + } + + /** + * Removes an Activity from the MTM display stack. + * + * Always call this function when the Activity added with + * {@link #bindDisplayActivity(Activity)} is hidden. + * + * @param act Activity to be unbound + */ + public void unbindDisplayActivity(Activity act) { + // do not remove if it was overridden by a different activity + if (foregroundAct == act) + foregroundAct = null; + } + + /** + * Get a list of all certificate aliases stored in MTM. + * + * @return an {@link Enumeration} of all certificates + */ + private Enumeration getCertificates() { + try { + return appKeyStore.aliases(); + } catch (KeyStoreException e) { + // this should never happen, however... + throw new RuntimeException(e); + } + } + + /** + * Get a certificate for a given alias. + * + * @param alias the certificate's alias as returned by {@link #getCertificates()}. + * + * @return the certificate associated with the alias or null if none found. + */ + public Certificate getCertificate(String alias) { + try { + return appKeyStore.getCertificate(alias); + } catch (KeyStoreException e) { + // this should never happen, however... + throw new RuntimeException(e); + } + } + + /** + * Removes the given certificate from MTMs key store. + * + *

+ * WARNING: this does not immediately invalidate the certificate. It is + * well possible that (a) data is transmitted over still existing connections or + * (b) new connections are created using TLS renegotiation, without a new cert + * check. + *

+ * @param alias the certificate's alias as returned by {@link #getCertificates()}. + * + * @throws KeyStoreException if the certificate could not be deleted. + */ + public void deleteCertificate(String alias) throws KeyStoreException { + appKeyStore.deleteEntry(alias); + keyStoreUpdated(); + } + + /** + * Creates a new hostname verifier supporting user interaction. + * + *

This method creates a new {@link HostnameVerifier} that is bound to + * the given instance of {@link MemorizingTrustManager}, and leverages an + * existing {@link HostnameVerifier}. The returned verifier performs the + * following steps, returning as soon as one of them succeeds: + *

+ *
    + *
  1. Success, if the wrapped defaultVerifier accepts the certificate.
  2. + *
  3. Success, if the server certificate is stored in the keystore under the given hostname.
  4. + *
  5. Ask the user and return accordingly.
  6. + *
  7. Failure on exception.
  8. + *
+ * + * @param defaultVerifier the {@link HostnameVerifier} that should perform the actual check + * @return a new hostname verifier using the MTM's key store + * + * @throws IllegalArgumentException if the defaultVerifier parameter is null + */ + public HostnameVerifier wrapHostnameVerifier(final HostnameVerifier defaultVerifier) { + if (defaultVerifier == null) + throw new IllegalArgumentException("The default verifier may not be null"); + + return new MemorizingHostnameVerifier(defaultVerifier); + } + + private X509TrustManager getTrustManager(KeyStore ks) { + try { + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); + tmf.init(ks); + for (TrustManager t : tmf.getTrustManagers()) { + if (t instanceof X509TrustManager) { + return (X509TrustManager)t; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private KeyStore loadAppKeyStore() { + KeyStore keyStore; + + try { + keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + } catch (KeyStoreException e) { + e.printStackTrace(); + return null; + } + + try { + keyStore.load(null, null); + } catch (NoSuchAlgorithmException | CertificateException | IOException e) { + e.printStackTrace(); + } + + String keystore = keyStoreStorage.getString(KEYSTORE_KEY, null); + + if(keystore != null) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.decode(keystore, Base64.DEFAULT)); + + try { + keyStore.load(inputStream, "MTM".toCharArray()); + inputStream.close(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + return keyStore; + } + + private void storeCert(String alias, Certificate cert) { + try { + appKeyStore.setCertificateEntry(alias, cert); + } catch (KeyStoreException e) { + e.printStackTrace(); + return; + } + + keyStoreUpdated(); + } + + private void storeCert(X509Certificate cert) { + storeCert(cert.getSubjectDN().toString(), cert); + } + + private void keyStoreUpdated() { + // reload appTrustManager + appTrustManager = getTrustManager(appKeyStore); + + // store KeyStore to shared preferences + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + try { + appKeyStore.store(byteArrayOutputStream, "MTM".toCharArray()); + byteArrayOutputStream.flush(); + byteArrayOutputStream.close(); + + keyStoreStorage.edit().putString(KEYSTORE_KEY, Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT)).apply(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // if the certificate is stored in the app key store, it is considered "known" + private boolean isCertKnown(X509Certificate cert) { + try { + return appKeyStore.getCertificateAlias(cert) != null; + } catch (KeyStoreException e) { + return false; + } + } + + private static boolean isExpiredException(Throwable e) { + do { + if (e instanceof CertificateExpiredException) + return true; + e = e.getCause(); + } while (e != null); + return false; + } + + private static boolean isPathException(Throwable e) { + do { + if (e instanceof CertPathValidatorException) + return true; + e = e.getCause(); + } while (e != null); + return false; + } + + private void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer) throws CertificateException { + LOGGER.log(Level.FINE, "checkCertTrusted(" + Arrays.toString(chain) + ", " + authType + ", " + isServer + ")"); + + try { + LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager"); + if (isServer) + appTrustManager.checkServerTrusted(chain, authType); + else + appTrustManager.checkClientTrusted(chain, authType); + } catch (CertificateException ae) { + LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager did not verify certificate. Will fall back to secondary verification mechanisms (if any).", ae); + // if the cert is stored in our appTrustManager, we ignore expiredness + if (isExpiredException(ae)) { + LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore"); + return; + } + if (isCertKnown(chain[0])) { + LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore"); + return; + } + try { + if (defaultTrustManager == null) { + LOGGER.fine("No defaultTrustManager set. Verification failed, throwing " + ae); + throw ae; + } + LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager"); + if (isServer) + defaultTrustManager.checkServerTrusted(chain, authType); + else + defaultTrustManager.checkClientTrusted(chain, authType); + } catch (CertificateException e) { + LOGGER.log(Level.FINER, "checkCertTrusted: defaultTrustManager failed", e); + interactCert(chain, authType, e); + } + } + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + checkCertTrusted(chain, authType, false); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + checkCertTrusted(chain, authType, true); + } + + public X509Certificate[] getAcceptedIssuers() { + return defaultTrustManager.getAcceptedIssuers(); + } + + private static int createDecisionId(MTMDecision d) { + int myId; + synchronized(openDecisions) { + myId = decisionId; + openDecisions.put(myId, d); + decisionId += 1; + } + return myId; + } + + private static String hexString(byte[] data) { + StringBuilder si = new StringBuilder(); + for (int i = 0; i < data.length; i++) { + si.append(String.format("%02x", data[i])); + if (i < data.length - 1) + si.append(":"); + } + return si.toString(); + } + + private static String certHash(final X509Certificate cert, String digest) { + try { + MessageDigest md = MessageDigest.getInstance(digest); + md.update(cert.getEncoded()); + return hexString(md.digest()); + } catch (CertificateEncodingException | NoSuchAlgorithmException e) { + return e.getMessage(); + } + } + + private static void certDetails(StringBuilder si, X509Certificate c) { + SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + si.append("\n"); + si.append(c.getSubjectDN().toString()); + si.append("\n"); + si.append(validityDateFormater.format(c.getNotBefore())); + si.append(" - "); + si.append(validityDateFormater.format(c.getNotAfter())); + si.append("\nSHA-256: "); + si.append(certHash(c, "SHA-256")); + si.append("\nSHA-1: "); + si.append(certHash(c, "SHA-1")); + si.append("\nSigned by: "); + si.append(c.getIssuerDN().toString()); + si.append("\n"); + } + + private String certChainMessage(final X509Certificate[] chain, CertificateException cause) { + Throwable e = cause; + StringBuilder si = new StringBuilder(); + + if (isPathException(e)) { + si.append(context.getString(R.string.mtm_trust_anchor)); + } else if (isExpiredException(e)) { + si.append(context.getString(R.string.mtm_cert_expired)); + } else { + // get to the cause + while (e.getCause() != null) e = e.getCause(); + si.append(e.getLocalizedMessage()); + } + + si.append("\n\n"); + si.append(context.getString(R.string.mtm_connect_anyway)); + si.append("\n\n"); + si.append(context.getString(R.string.mtm_cert_details)); + for (X509Certificate c : chain) { + certDetails(si, c); + } + return si.toString(); + } + + private String hostNameMessage(X509Certificate cert, String hostname) { + StringBuilder si = new StringBuilder(); + + si.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); + si.append("\n\n"); + + try { + Collection> sans = cert.getSubjectAlternativeNames(); + if (sans == null) { + si.append(cert.getSubjectDN()); + si.append("\n"); + } else for (List altName : sans) { + Object name = altName.get(1); + if (name instanceof String) { + si.append("["); + si.append(altName.get(0)); + si.append("] "); + si.append(name); + si.append("\n"); + } + } + } catch (CertificateParsingException e) { + e.printStackTrace(); + si.append("\n"); + } + + si.append("\n"); + si.append(context.getString(R.string.mtm_connect_anyway)); + si.append("\n\n"); + si.append(context.getString(R.string.mtm_cert_details)); + certDetails(si, cert); + return si.toString(); + } + + /** + * Reflectively call + * Notification.setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent) + * since it was remove in Android API level 23. + * + */ + private static void setLatestEventInfoReflective(Notification notification, Context context, CharSequence mtmNotification, CharSequence certName, PendingIntent call) { + Method setLatestEventInfo; + + try { + setLatestEventInfo = notification.getClass().getMethod( + "setLatestEventInfo", Context.class, CharSequence.class, + CharSequence.class, PendingIntent.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + + try { + setLatestEventInfo.invoke(notification, context, mtmNotification, certName, call); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException(e); + } + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + private void startActivityNotification(Intent intent, int decisionId, String certName) { + final PendingIntent call = PendingIntent.getActivity(context, 0, intent, 0); + final String mtmNotification = context.getString(R.string.mtm_notification); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl") + .setSmallIcon(android.R.drawable.ic_lock_lock) + .setContentTitle(mtmNotification) + .setContentText(certName) + .setTicker(certName) + .setContentIntent(call) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_HIGH); + + notificationManager.notify(NOTIFICATION_ID + decisionId, builder.build()); + } + + /** + * Returns the top-most entry of the activity stack. + * + * @return the Context of the currently bound UI or the master context if none is bound + */ + Context getUI() { + return (foregroundAct != null) ? foregroundAct : context; + } + + private int interact(final String message, final int titleId) { + /* prepare the MTMDecision blocker object */ + MTMDecision choice = new MTMDecision(); + final int myId = createDecisionId(choice); + + masterHandler.post(new Runnable() { + public void run() { + Intent intent = new Intent(context, MemorizingActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId)); + intent.putExtra(DECISION_INTENT_ID, myId); + intent.putExtra(DECISION_INTENT_CERT, message); + intent.putExtra(DECISION_TITLE_ID, titleId); + + // we try to directly start the activity and fall back to + // making a notification. If no foreground activity is set + // (foregroundAct==null) or if the app developer set an + // invalid / expired activity, the catch-all fallback is + // deployed. + try { + foregroundAct.startActivity(intent); + } catch (Exception e) { + startActivityNotification(intent, myId, message); + } + } + }); + + try { + synchronized(choice) { + choice.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return choice.state; + } + + private void interactCert(final X509Certificate[] chain, String authType, CertificateException cause) throws CertificateException { + switch (interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) { + case MTMDecision.DECISION_ALWAYS: + storeCert(chain[0]); // only store the server cert, not the whole chain + case MTMDecision.DECISION_ONCE: + break; + default: + throw (cause); + } + } + + private boolean interactHostname(X509Certificate cert, String hostname) { + switch (interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) { + case MTMDecision.DECISION_ALWAYS: + storeCert(hostname, cert); + case MTMDecision.DECISION_ONCE: + return true; + default: + return false; + } + } + + static void interactResult(int decisionId, int choice) { + MTMDecision d; + + synchronized(openDecisions) { + d = openDecisions.get(decisionId); + openDecisions.remove(decisionId); + } + + if (d == null) return; + + synchronized(d) { + d.state = choice; + d.notify(); + } + } + + class MemorizingHostnameVerifier implements HostnameVerifier { + private HostnameVerifier defaultVerifier; + + MemorizingHostnameVerifier(HostnameVerifier wrapped) { + defaultVerifier = wrapped; + } + + @Override + public boolean verify(String hostname, SSLSession session) { + // if the default verifier accepts the hostname, we are done + if (defaultVerifier.verify(hostname, session)) return true; + + // otherwise, we check if the hostname is an alias for this cert in our keystore + try { + X509Certificate cert = (X509Certificate)session.getPeerCertificates()[0]; + + if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) { + return true; + } else { + return interactHostname(cert, hostname); + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a1a10a6e..fa2f1c16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -566,4 +566,19 @@ Changelog + + Accept Unknown Certificate? + The server certificate is not signed by a known Certificate Authority. + The server certificate is expired. + Accept Mismatching Server Name? + Server could not authenticate as \"%s\". The certificate is only valid for: + + Do you want to connect anyway? + Certificate details: + + Always + Once + Abort + + Certificate Verification From 2b6d22ed949f3376f469a3731592a0464bcb3115 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Mon, 30 Mar 2020 23:25:49 +0200 Subject: [PATCH 02/24] Modifying license headers. --- .../java/org/mian/gitnex/ssl/MTMDecision.java | 26 ++------------ .../mian/gitnex/ssl/MemorizingActivity.java | 26 ++------------ .../gitnex/ssl/MemorizingTrustManager.java | 35 +------------------ 3 files changed, 7 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java b/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java index 9eba8ee2..cb141175 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java +++ b/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java @@ -1,28 +1,8 @@ -/* MemorizingTrustManager - a TrustManager which asks the user about invalid - * certificates and memorizes their decision. - * - * Copyright (c) 2010 Georg Lukas - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package org.mian.gitnex.ssl; +/** + * Author Georg Lukas, modified by anonTree1417 + */ class MTMDecision { public final static int DECISION_INVALID = 0; public final static int DECISION_ABORT = 1; diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java index b2da9f45..68397e84 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java @@ -1,26 +1,3 @@ -/* MemorizingTrustManager - a TrustManager which asks the user about invalid - * certificates and memorizes their decision. - * - * Copyright (c) 2010 Georg Lukas - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package org.mian.gitnex.ssl; import android.app.Activity; @@ -29,6 +6,9 @@ import android.content.Intent; import android.os.Bundle; import org.mian.gitnex.R; +/** + * Author Georg Lukas, modified by anonTree1417 + */ public class MemorizingActivity extends Activity { @Override diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java index 44f65716..fc184b5d 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java @@ -1,29 +1,3 @@ -/* MemorizingTrustManager - a TrustManager which asks the user about invalid - * certificates and memorizes their decision. - * - * Copyright (c) 2010 Georg Lukas - * - * MemorizingTrustManager.java contains the actual trust manager and interface - * code to create a MemorizingActivity and obtain the results. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package org.mian.gitnex.ssl; import android.annotation.TargetApi; @@ -68,14 +42,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** - * A X509 trust manager implementation which asks the user about invalid - * certificates and memorizes their decision. - *

- * The certificate validity is checked using the system default X509 - * TrustManager, creating a query Dialog if the check fails. - *

- * WARNING: This only works if a dedicated thread is used for - * opening sockets! + * Author Georg Lukas, modified by anonTree1417 */ public class MemorizingTrustManager implements X509TrustManager { private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; From 6b2be06eda93487ce348e744630be63a306faab4 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 04:04:11 +0200 Subject: [PATCH 03/24] Adjusting proportions of issue labels. --- .../org/mian/gitnex/activities/IssueDetailActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index 675e20b7..65ce4753 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -427,7 +427,7 @@ public class IssueDetailActivity extends BaseActivity { if(singleIssue.getLabels() != null) { labelsScrollView.setVisibility(View.VISIBLE); - int width = 33; + int width = 25; for (int i = 0; i < singleIssue.getLabels().size(); i++) { String labelColor = singleIssue.getLabels().get(i).getColor(); @@ -443,11 +443,11 @@ public class IssueDetailActivity extends BaseActivity { .beginConfig() .useFont(Typeface.DEFAULT) .textColor(new ColorInverter().getContrastColor(color)) - .fontSize(36) + .fontSize(30) .width((width * labelName.length()) - ((width / 4) * labelName.length())) - .height(60) + .height(50) .endConfig() - .buildRoundRect(labelName, color, 8); + .buildRoundRect(labelName, color, 10); labelsView.setImageDrawable(drawable); labelsLayout.addView(labelsView); From 60781ac4c38c6fce6081763675dcfa4b9a850ba3 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 05:14:24 +0200 Subject: [PATCH 04/24] Small design changes. --- app/src/main/res/layout/branches_list.xml | 4 ++-- app/src/main/res/layout/labels_list.xml | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/branches_list.xml b/app/src/main/res/layout/branches_list.xml index 885565d3..0f43ab80 100644 --- a/app/src/main/res/layout/branches_list.xml +++ b/app/src/main/res/layout/branches_list.xml @@ -18,17 +18,17 @@ android:id="@+id/branchName" android:layout_width="match_parent" android:layout_height="wrap_content" + android:fontFamily="monospace" android:textColor="?attr/primaryTextColor" android:textIsSelectable="true" android:textSize="18sp" /> + android:scaleType="fitEnd" + android:src="@drawable/ic_dotted_menu_horizontal" /> Date: Tue, 31 Mar 2020 16:41:50 +0200 Subject: [PATCH 05/24] Fixed formatting. --- .../mian/gitnex/activities/LoginActivity.java | 7 +++-- .../mian/gitnex/clients/IssuesService.java | 14 ++++++---- .../gitnex/clients/PullRequestsService.java | 14 ++++++---- .../mian/gitnex/clients/RetrofitClient.java | 28 +++++++++++-------- .../gitnex/ssl/MemorizingTrustManager.java | 1 + app/src/main/res/values/strings.xml | 4 +-- 6 files changed, 38 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 1b6aef02..326dca9e 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -21,7 +21,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import com.tooltip.Tooltip; -import org.jetbrains.annotations.NotNull; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.Toasty; @@ -455,9 +454,10 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener } @Override - public void onFailure(@NonNull Call callVersion, @NotNull Throwable throwable) { - Log.e("onFailure-version", throwable.toString()); + public void onFailure(@NonNull Call callVersion, Throwable t) { + Log.e("onFailure-version", t.toString()); } + }); } @@ -481,6 +481,7 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener if (response.isSuccessful()) { if (response.code() == 200) { + tinyDb.putBoolean("loggedInMode", true); assert userDetails != null; tinyDb.putString(userDetails.getLogin() + "-token", loginToken_); diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index d65afc57..e1600578 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -1,6 +1,7 @@ package org.mian.gitnex.clients; import android.content.Context; +import android.util.Log; import androidx.annotation.NonNull; import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; @@ -25,10 +26,10 @@ import retrofit2.converter.gson.GsonConverterFactory; public class IssuesService { - public static S createService(Class serviceClass, String instanceURL, Context context) { + public static S createService(Class serviceClass, String instanceURL, Context ctx) { - final boolean connToInternet = AppUtil.haveNetworkConnection(context); - File httpCacheDirectory = new File(context.getCacheDir(), "responses"); + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); int cacheSize = 50 * 1024 * 1024; // 50MB Cache cache = new Cache(httpCacheDirectory, cacheSize); @@ -38,7 +39,7 @@ public class IssuesService { try { SSLContext sslContext = SSLContext.getInstance("TLS"); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { @@ -62,8 +63,9 @@ public class IssuesService { Retrofit retrofit = builder.build(); return retrofit.create(serviceClass); - } catch(Exception e) { - e.printStackTrace(); + } + catch(Exception e) { + Log.e("onFailure", e.toString()); } return null; diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index c54077ef..e5e6a29c 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -1,6 +1,7 @@ package org.mian.gitnex.clients; import android.content.Context; +import android.util.Log; import androidx.annotation.NonNull; import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; @@ -25,10 +26,10 @@ import retrofit2.converter.gson.GsonConverterFactory; public class PullRequestsService { - public static S createService(Class serviceClass, String instanceURL, Context context) { + public static S createService(Class serviceClass, String instanceURL, Context ctx) { - final boolean connToInternet = AppUtil.haveNetworkConnection(context); - File httpCacheDirectory = new File(context.getCacheDir(), "responses"); + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); int cacheSize = 50 * 1024 * 1024; // 50MB Cache cache = new Cache(httpCacheDirectory, cacheSize); @@ -38,7 +39,7 @@ public class PullRequestsService { try { SSLContext sslContext = SSLContext.getInstance("TLS"); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { @@ -62,8 +63,9 @@ public class PullRequestsService { Retrofit retrofit = builder.build(); return retrofit.create(serviceClass); - } catch(Exception e) { - e.printStackTrace(); + } + catch(Exception e) { + Log.e("onFailure", e.toString()); } return null; diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index 7c19f895..a9a0102d 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -1,6 +1,7 @@ package org.mian.gitnex.clients; import android.content.Context; +import android.util.Log; import org.mian.gitnex.interfaces.ApiInterface; import org.mian.gitnex.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; @@ -25,10 +26,10 @@ public class RetrofitClient { private Retrofit retrofit; - private RetrofitClient(String instanceUrl, Context context) { - final boolean connToInternet = AppUtil.haveNetworkConnection(context); + private RetrofitClient(String instanceUrl, Context ctx) { + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); int cacheSize = 50 * 1024 * 1024; // 50MB - File httpCacheDirectory = new File(context.getCacheDir(), "responses"); + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); Cache cache = new Cache(httpCacheDirectory, cacheSize); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); @@ -37,21 +38,23 @@ public class RetrofitClient { try { SSLContext sslContext = SSLContext.getInstance("TLS"); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[] { memorizingTrustManager }, new SecureRandom()); OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() .cache(cache) + //.addInterceptor(logging) .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) .addInterceptor(chain -> { + Request request = chain.request(); - - request = (connToInternet) ? - request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build() : - request.newBuilder().header("Cache-Control", - "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); + } + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } return chain.proceed(request); }); @@ -63,8 +66,9 @@ public class RetrofitClient { retrofit = builder.build(); - } catch(Exception e) { - e.printStackTrace(); + } + catch(Exception e) { + Log.e("onFailure", e.toString()); } } diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java index fc184b5d..fc275ef9 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java @@ -44,6 +44,7 @@ import javax.net.ssl.X509TrustManager; /** * Author Georg Lukas, modified by anonTree1417 */ + public class MemorizingTrustManager implements X509TrustManager { private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa2f1c16..a8d14bf7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -567,18 +567,16 @@ Changelog + Certificate Verification Accept Unknown Certificate? The server certificate is not signed by a known Certificate Authority. The server certificate is expired. Accept Mismatching Server Name? Server could not authenticate as \"%s\". The certificate is only valid for: - Do you want to connect anyway? Certificate details: - Always Once Abort - Certificate Verification From d2190ad18937246508d5746cc988ade14b565d20 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 16:44:11 +0200 Subject: [PATCH 06/24] Moving classes to helpers/ --- app/src/main/AndroidManifest.xml | 2 +- .../org/mian/gitnex/clients/IssuesService.java | 2 +- .../mian/gitnex/clients/PullRequestsService.java | 2 +- .../org/mian/gitnex/clients/RetrofitClient.java | 2 +- .../java/org/mian/gitnex/helpers/MTMDecision.java | 14 ++++++++++++++ .../{ssl => helpers}/MemorizingActivity.java | 3 ++- .../{ssl => helpers}/MemorizingTrustManager.java | 2 +- .../main/java/org/mian/gitnex/ssl/MTMDecision.java | 13 ------------- 8 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java rename app/src/main/java/org/mian/gitnex/{ssl => helpers}/MemorizingActivity.java (97%) rename app/src/main/java/org/mian/gitnex/{ssl => helpers}/MemorizingTrustManager.java (99%) delete mode 100644 app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6de240b1..d54d9876 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,7 +73,7 @@ - + \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index e1600578..28d26ac7 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; -import org.mian.gitnex.ssl.MemorizingTrustManager; +import org.mian.gitnex.helpers.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index e5e6a29c..50731225 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; -import org.mian.gitnex.ssl.MemorizingTrustManager; +import org.mian.gitnex.helpers.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index a9a0102d..a2794ec7 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import org.mian.gitnex.interfaces.ApiInterface; -import org.mian.gitnex.ssl.MemorizingTrustManager; +import org.mian.gitnex.helpers.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.security.SecureRandom; diff --git a/app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java b/app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java new file mode 100644 index 00000000..86280426 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java @@ -0,0 +1,14 @@ +package org.mian.gitnex.helpers; + +/** + * Author Georg Lukas, modified by anonTree1417 + */ + +class MTMDecision { + final static int DECISION_INVALID = 0; + final static int DECISION_ABORT = 1; + final static int DECISION_ONCE = 2; + final static int DECISION_ALWAYS = 3; + + int state = DECISION_INVALID; +} diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java similarity index 97% rename from app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java rename to app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java index 68397e84..1de5c9fa 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java @@ -1,4 +1,4 @@ -package org.mian.gitnex.ssl; +package org.mian.gitnex.helpers; import android.app.Activity; import android.app.AlertDialog; @@ -9,6 +9,7 @@ import org.mian.gitnex.R; /** * Author Georg Lukas, modified by anonTree1417 */ + public class MemorizingActivity extends Activity { @Override diff --git a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java similarity index 99% rename from app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java rename to app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java index fc275ef9..033cd9b7 100644 --- a/app/src/main/java/org/mian/gitnex/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java @@ -1,4 +1,4 @@ -package org.mian.gitnex.ssl; +package org.mian.gitnex.helpers; import android.annotation.TargetApi; import android.app.Activity; diff --git a/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java b/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java deleted file mode 100644 index cb141175..00000000 --- a/app/src/main/java/org/mian/gitnex/ssl/MTMDecision.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mian.gitnex.ssl; - -/** - * Author Georg Lukas, modified by anonTree1417 - */ -class MTMDecision { - public final static int DECISION_INVALID = 0; - public final static int DECISION_ABORT = 1; - public final static int DECISION_ONCE = 2; - public final static int DECISION_ALWAYS = 3; - - int state = DECISION_INVALID; -} From f12219ace75a5ab5dd7a9adad5554ac2d312bfe0 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 16:52:39 +0200 Subject: [PATCH 07/24] Restoring branches_list.xml --- app/src/main/res/layout/branches_list.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/branches_list.xml b/app/src/main/res/layout/branches_list.xml index 0f43ab80..885565d3 100644 --- a/app/src/main/res/layout/branches_list.xml +++ b/app/src/main/res/layout/branches_list.xml @@ -18,17 +18,17 @@ android:id="@+id/branchName" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="monospace" android:textColor="?attr/primaryTextColor" android:textIsSelectable="true" android:textSize="18sp" /> Date: Tue, 31 Mar 2020 18:43:52 +0200 Subject: [PATCH 08/24] Adjusting sizes in LabelsAdapter --- .../main/java/org/mian/gitnex/adapters/LabelsAdapter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java index e6200440..61a336cf 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java @@ -146,11 +146,11 @@ public class LabelsAdapter extends RecyclerView.Adapter Date: Tue, 31 Mar 2020 21:04:08 +0200 Subject: [PATCH 09/24] Adding information at error and resetting button. --- .../org/mian/gitnex/activities/LoginActivity.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 1890ee3f..337ac401 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -31,6 +31,7 @@ import org.mian.gitnex.models.UserInfo; import org.mian.gitnex.models.UserTokens; import org.mian.gitnex.util.AppUtil; import org.mian.gitnex.util.TinyDB; +import java.net.NoRouteToHostException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -465,7 +466,20 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener @Override public void onFailure(@NonNull Call callVersion, Throwable t) { + Log.e("onFailure-version", t.toString()); + + if(t instanceof NoRouteToHostException) { + Toasty.info(getApplicationContext(), "Could not connect to host. Please check your url or port " + + "for issues."); + } + else { + Toasty.info(getApplicationContext(), getResources().getString(R.string.commentError)); + } + + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + } }); From a53a5d55bfbdfc6cef1ac5eecb492f99ae7f9626 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 21:04:53 +0200 Subject: [PATCH 10/24] Reformatting applied. --- .../mian/gitnex/activities/LoginActivity.java | 1010 ++++++++--------- 1 file changed, 495 insertions(+), 515 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 337ac401..71f44827 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -48,698 +48,678 @@ import retrofit2.Callback; public class LoginActivity extends BaseActivity implements View.OnClickListener { - private Button loginButton; - private EditText instanceUrlET, loginUidET, loginPassword, otpCode, loginTokenCode; - private Spinner protocolSpinner; - private TextView otpInfo; - private RadioGroup loginMethod; - final Context ctx = this; - private String device_id = "token"; + private Button loginButton; + private EditText instanceUrlET, loginUidET, loginPassword, otpCode, loginTokenCode; + private Spinner protocolSpinner; + private TextView otpInfo; + private RadioGroup loginMethod; + final Context ctx = this; + private String device_id = "token"; - @Override - protected int getLayoutResourceId(){ - return R.layout.activity_login; - } + @Override + protected int getLayoutResourceId() { - @Override - public void onCreate(Bundle savedInstanceState) { + return R.layout.activity_login; + } - super.onCreate(savedInstanceState); + @Override + public void onCreate(Bundle savedInstanceState) { - TinyDB tinyDb = new TinyDB(getApplicationContext()); - NetworkObserver networkMonitor = new NetworkObserver(this); + super.onCreate(savedInstanceState); - loginButton = findViewById(R.id.login_button); - instanceUrlET = findViewById(R.id.instance_url); - loginUidET = findViewById(R.id.login_uid); - loginPassword = findViewById(R.id.login_passwd); - otpCode = findViewById(R.id.otpCode); - otpInfo = findViewById(R.id.otpInfo); - ImageView info_button = findViewById(R.id.info); - final TextView viewTextAppVersion = findViewById(R.id.appVersion); - protocolSpinner = findViewById(R.id.httpsSpinner); - loginMethod = findViewById(R.id.loginMethod); - loginTokenCode = findViewById(R.id.loginTokenCode); + TinyDB tinyDb = new TinyDB(getApplicationContext()); + NetworkObserver networkMonitor = new NetworkObserver(this); - viewTextAppVersion.setText(AppUtil.getAppVersion(getApplicationContext())); + loginButton = findViewById(R.id.login_button); + instanceUrlET = findViewById(R.id.instance_url); + loginUidET = findViewById(R.id.login_uid); + loginPassword = findViewById(R.id.login_passwd); + otpCode = findViewById(R.id.otpCode); + otpInfo = findViewById(R.id.otpInfo); + ImageView info_button = findViewById(R.id.info); + final TextView viewTextAppVersion = findViewById(R.id.appVersion); + protocolSpinner = findViewById(R.id.httpsSpinner); + loginMethod = findViewById(R.id.loginMethod); + loginTokenCode = findViewById(R.id.loginTokenCode); - Resources res = getResources(); - String[] allProtocols = res.getStringArray(R.array.protocolValues); + viewTextAppVersion.setText(AppUtil.getAppVersion(getApplicationContext())); - final ArrayAdapter adapterProtocols = new ArrayAdapter(Objects.requireNonNull(getApplicationContext()), - R.layout.spinner_item, allProtocols); + Resources res = getResources(); + String[] allProtocols = res.getStringArray(R.array.protocolValues); - adapterProtocols.setDropDownViewResource(R.layout.spinner_dropdown_item); - protocolSpinner.setAdapter(adapterProtocols); + final ArrayAdapter adapterProtocols = new ArrayAdapter(Objects.requireNonNull(getApplicationContext()), R.layout.spinner_item, allProtocols); - protocolSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - public void onItemSelected(AdapterView parent, View view, int pos, long id) { + adapterProtocols.setDropDownViewResource(R.layout.spinner_dropdown_item); + protocolSpinner.setAdapter(adapterProtocols); - String value = getResources().getStringArray(R.array.protocolValues)[pos]; - if(value.toLowerCase().equals("http")) { - Toasty.info(getApplicationContext(), getResources().getString(R.string.protocolError)); - } - - } - public void onNothingSelected(AdapterView parent) { - } - }); - - info_button.setOnClickListener(infoListener); + protocolSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - loginMethod.setOnCheckedChangeListener((group, checkedId) -> { - - if(checkedId == R.id.loginToken) { + public void onItemSelected(AdapterView parent, View view, int pos, long id) { - loginUidET.setVisibility(View.GONE); - loginPassword.setVisibility(View.GONE); - otpCode.setVisibility(View.GONE); - otpInfo.setVisibility(View.GONE); - loginTokenCode.setVisibility(View.VISIBLE); - - } - else { - - loginUidET.setVisibility(View.VISIBLE); - loginPassword.setVisibility(View.VISIBLE); - otpCode.setVisibility(View.VISIBLE); - otpInfo.setVisibility(View.VISIBLE); - loginTokenCode.setVisibility(View.GONE); + String value = getResources().getStringArray(R.array.protocolValues)[pos]; + if(value.toLowerCase().equals("http")) { + Toasty.info(getApplicationContext(), getResources().getString(R.string.protocolError)); + } - } + } - }); + public void onNothingSelected(AdapterView parent) { - networkMonitor.onInternetStateListener(isAvailable -> { + } + }); - if(isAvailable) { - enableProcessButton(); - } - else { - disableProcessButton(); - Toasty.info(getApplicationContext(), getResources().getString(R.string.checkNetConnection)); - } + info_button.setOnClickListener(infoListener); - }); + loginMethod.setOnCheckedChangeListener((group, checkedId) -> { - //login_button.setOnClickListener(this); - if(!tinyDb.getString("instanceUrlRaw").isEmpty()) { - instanceUrlET.setText(tinyDb.getString("instanceUrlRaw")); - } - if(!tinyDb.getString("loginUid").isEmpty()) { - loginUidET.setText(tinyDb.getString("loginUid")); - } + if(checkedId == R.id.loginToken) { - if(tinyDb.getBoolean("loggedInMode")) { + loginUidET.setVisibility(View.GONE); + loginPassword.setVisibility(View.GONE); + otpCode.setVisibility(View.GONE); + otpInfo.setVisibility(View.GONE); + loginTokenCode.setVisibility(View.VISIBLE); - startActivity(new Intent(LoginActivity.this, MainActivity.class)); - finish(); + } + else { - } - - loginButton.setOnClickListener(loginListener); + loginUidET.setVisibility(View.VISIBLE); + loginPassword.setVisibility(View.VISIBLE); + otpCode.setVisibility(View.VISIBLE); + otpInfo.setVisibility(View.VISIBLE); + loginTokenCode.setVisibility(View.GONE); - if(!tinyDb.getString("uniqueAppId").isEmpty()) { - device_id = tinyDb.getString("uniqueAppId"); - } - else { - device_id = UUID.randomUUID().toString(); - tinyDb.putString("uniqueAppId", device_id); - } - - } + } - @Override - public void onClick(View v) { - - if (v.getId() == R.id.login_button) { - login(); - } + }); - } + networkMonitor.onInternetStateListener(isAvailable -> { - private View.OnClickListener loginListener = new View.OnClickListener() { - public void onClick(View v) { + if(isAvailable) { + enableProcessButton(); + } + else { + disableProcessButton(); + Toasty.info(getApplicationContext(), getResources().getString(R.string.checkNetConnection)); + } - disableProcessButton(); - loginButton.setText(R.string.processingText); - login(); + }); - } - }; + //login_button.setOnClickListener(this); + if(!tinyDb.getString("instanceUrlRaw").isEmpty()) { + instanceUrlET.setText(tinyDb.getString("instanceUrlRaw")); + } + if(!tinyDb.getString("loginUid").isEmpty()) { + loginUidET.setText(tinyDb.getString("loginUid")); + } - private View.OnClickListener infoListener = new View.OnClickListener() { - public void onClick(View v) { - new Tooltip.Builder(v) - .setText(R.string.urlInfoTooltip) - .setTextColor(getResources().getColor(R.color.white)) - .setBackgroundColor(getResources().getColor(R.color.tooltipBackground)) - .setCancelable(true) - .setDismissOnClick(true) - .setPadding(30) - .setCornerRadius(R.dimen.tooltipCornor) - .setGravity(Gravity.BOTTOM) - .show(); - } - }; + if(tinyDb.getBoolean("loggedInMode")) { - @SuppressLint("ResourceAsColor") - private void login() { + startActivity(new Intent(LoginActivity.this, MainActivity.class)); + finish(); - TinyDB tinyDb = new TinyDB(getApplicationContext()); - AppUtil appUtil = new AppUtil(); - boolean connToInternet = AppUtil.haveNetworkConnection(LoginActivity.this); + } - String instanceUrl = instanceUrlET.getText().toString().trim(); - String loginUid = loginUidET.getText().toString(); - String loginPass = loginPassword.getText().toString(); - String protocol = protocolSpinner.getSelectedItem().toString(); - String loginOTP_ = otpCode.getText().toString().trim(); - int loginMethodType = loginMethod.getCheckedRadioButtonId(); - String loginToken_ = loginTokenCode.getText().toString().trim(); + loginButton.setOnClickListener(loginListener); - if(loginMethodType == R.id.loginUsernamePassword) { + if(!tinyDb.getString("uniqueAppId").isEmpty()) { + device_id = tinyDb.getString("uniqueAppId"); + } + else { + device_id = UUID.randomUUID().toString(); + tinyDb.putString("uniqueAppId", device_id); + } - if(instanceUrl.contains("@")) { + } - String[] urlForHttpAuth = instanceUrl.split("@"); + @Override + public void onClick(View v) { - tinyDb.putString("basicAuthPassword", loginPass); - tinyDb.putBoolean("basicAuthFlag", true); + if(v.getId() == R.id.login_button) { + login(); + } - instanceUrl = urlForHttpAuth[1]; - loginUid = urlForHttpAuth[0]; + } - } + private View.OnClickListener loginListener = new View.OnClickListener() { - String instanceHost; - if(AppUtil.httpCheck(instanceUrl)) { + public void onClick(View v) { - URI uri = null; - try { - uri = new URI(instanceUrl); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - assert uri != null; - instanceHost = uri.getHost(); + disableProcessButton(); + loginButton.setText(R.string.processingText); + login(); - } - else { - instanceHost = instanceUrl; - } + } + }; - String instanceUrlWithProtocol; - if(protocol.toLowerCase().equals("https")) { - instanceUrl = "https://" + instanceHost + "/api/v1/"; - instanceUrlWithProtocol = "https://" + instanceHost; - } - else { - instanceUrl = "http://" + instanceHost + "/api/v1/"; - instanceUrlWithProtocol = "http://" + instanceHost; - } + private View.OnClickListener infoListener = new View.OnClickListener() { - tinyDb.putString("instanceUrlRaw", instanceHost); - tinyDb.putString("loginUid", loginUid); - tinyDb.putString("instanceUrl", instanceUrl); - tinyDb.putString("instanceUrlWithProtocol", instanceUrlWithProtocol); + public void onClick(View v) { - if(connToInternet) { + new Tooltip.Builder(v).setText(R.string.urlInfoTooltip).setTextColor(getResources().getColor(R.color.white)).setBackgroundColor(getResources().getColor(R.color.tooltipBackground)).setCancelable(true).setDismissOnClick(true).setPadding(30).setCornerRadius(R.dimen.tooltipCornor).setGravity(Gravity.BOTTOM).show(); + } + }; - if(instanceUrlET.getText().toString().equals("")) { + @SuppressLint("ResourceAsColor") + private void login() { - Toasty.info(getApplicationContext(), getString(R.string.emptyFieldURL)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + TinyDB tinyDb = new TinyDB(getApplicationContext()); + AppUtil appUtil = new AppUtil(); + boolean connToInternet = AppUtil.haveNetworkConnection(LoginActivity.this); - } - if(loginUid.equals("")) { + String instanceUrl = instanceUrlET.getText().toString().trim(); + String loginUid = loginUidET.getText().toString(); + String loginPass = loginPassword.getText().toString(); + String protocol = protocolSpinner.getSelectedItem().toString(); + String loginOTP_ = otpCode.getText().toString().trim(); + int loginMethodType = loginMethod.getCheckedRadioButtonId(); + String loginToken_ = loginTokenCode.getText().toString().trim(); - Toasty.info(getApplicationContext(), getString(R.string.emptyFieldUsername)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + if(loginMethodType == R.id.loginUsernamePassword) { - } - if(loginPassword.getText().toString().equals("")) { + if(instanceUrl.contains("@")) { - Toasty.info(getApplicationContext(), getString(R.string.emptyFieldPassword)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + String[] urlForHttpAuth = instanceUrl.split("@"); - } + tinyDb.putString("basicAuthPassword", loginPass); + tinyDb.putBoolean("basicAuthFlag", true); - int loginOTP = 0; - if(loginOTP_.length() == 6) { + instanceUrl = urlForHttpAuth[1]; + loginUid = urlForHttpAuth[0]; - if(appUtil.checkIntegers(loginOTP_)) { + } - loginOTP = Integer.parseInt(loginOTP_); - } - else { + String instanceHost; + if(AppUtil.httpCheck(instanceUrl)) { - Toasty.info(getApplicationContext(), getString(R.string.loginOTPTypeError)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + URI uri = null; + try { + uri = new URI(instanceUrl); + } + catch(URISyntaxException e) { + e.printStackTrace(); + } + assert uri != null; + instanceHost = uri.getHost(); - } + } + else { + instanceHost = instanceUrl; + } - } + String instanceUrlWithProtocol; + if(protocol.toLowerCase().equals("https")) { + instanceUrl = "https://" + instanceHost + "/api/v1/"; + instanceUrlWithProtocol = "https://" + instanceHost; + } + else { + instanceUrl = "http://" + instanceHost + "/api/v1/"; + instanceUrlWithProtocol = "http://" + instanceHost; + } - versionCheck(instanceUrl, loginUid, loginPass, loginOTP, loginToken_, 1); + tinyDb.putString("instanceUrlRaw", instanceHost); + tinyDb.putString("loginUid", loginUid); + tinyDb.putString("instanceUrl", instanceUrl); + tinyDb.putString("instanceUrlWithProtocol", instanceUrlWithProtocol); - } - else { + if(connToInternet) { - Toasty.info(getApplicationContext(), getString(R.string.checkNetConnection)); + if(instanceUrlET.getText().toString().equals("")) { - } + Toasty.info(getApplicationContext(), getString(R.string.emptyFieldURL)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - } - else { + } + if(loginUid.equals("")) { - String instanceHost; - if(AppUtil.httpCheck(instanceUrl)) { + Toasty.info(getApplicationContext(), getString(R.string.emptyFieldUsername)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - URI uri = null; - try { - uri = new URI(instanceUrl); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - assert uri != null; - instanceHost = uri.getHost(); + } + if(loginPassword.getText().toString().equals("")) { - } - else { - instanceHost = instanceUrl; - } + Toasty.info(getApplicationContext(), getString(R.string.emptyFieldPassword)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - String instanceUrlWithProtocol; - if(protocol.toLowerCase().equals("https")) { - instanceUrl = "https://" + instanceHost + "/api/v1/"; - instanceUrlWithProtocol = "https://" + instanceHost; - } - else { - instanceUrl = "http://" + instanceHost + "/api/v1/"; - instanceUrlWithProtocol = "http://" + instanceHost; - } + } - tinyDb.putString("instanceUrlRaw", instanceHost); - //tinyDb.putString("loginUid", loginUid); - tinyDb.putString("instanceUrl", instanceUrl); - tinyDb.putString("instanceUrlWithProtocol", instanceUrlWithProtocol); + int loginOTP = 0; + if(loginOTP_.length() == 6) { - if(connToInternet) { + if(appUtil.checkIntegers(loginOTP_)) { - if (instanceUrlET.getText().toString().equals("")) { + loginOTP = Integer.parseInt(loginOTP_); + } + else { - Toasty.info(getApplicationContext(), getString(R.string.emptyFieldURL)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + Toasty.info(getApplicationContext(), getString(R.string.loginOTPTypeError)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - } - if (loginToken_.equals("")) { + } - Toasty.info(getApplicationContext(), getString(R.string.loginTokenError)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - return; + } - } + versionCheck(instanceUrl, loginUid, loginPass, loginOTP, loginToken_, 1); - versionCheck(instanceUrl, loginUid, loginPass, 123, loginToken_, 2); - } - else { + } + else { - Toasty.info(getApplicationContext(), getString(R.string.checkNetConnection)); + Toasty.info(getApplicationContext(), getString(R.string.checkNetConnection)); - } + } - } + } + else { - } + String instanceHost; + if(AppUtil.httpCheck(instanceUrl)) { - private void versionCheck(final String instanceUrl, final String loginUid, final String loginPass, final int loginOTP, final String loginToken_, final int loginType) { + URI uri = null; + try { + uri = new URI(instanceUrl); + } + catch(URISyntaxException e) { + e.printStackTrace(); + } + assert uri != null; + instanceHost = uri.getHost(); - final TinyDB tinyDb = new TinyDB(getApplicationContext()); + } + else { + instanceHost = instanceUrl; + } - Call callVersion = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .getGiteaVersion(); + String instanceUrlWithProtocol; + if(protocol.toLowerCase().equals("https")) { + instanceUrl = "https://" + instanceHost + "/api/v1/"; + instanceUrlWithProtocol = "https://" + instanceHost; + } + else { + instanceUrl = "http://" + instanceHost + "/api/v1/"; + instanceUrlWithProtocol = "http://" + instanceHost; + } - callVersion.enqueue(new Callback() { + tinyDb.putString("instanceUrlRaw", instanceHost); + //tinyDb.putString("loginUid", loginUid); + tinyDb.putString("instanceUrl", instanceUrl); + tinyDb.putString("instanceUrlWithProtocol", instanceUrlWithProtocol); - @Override - public void onResponse(@NonNull final Call callVersion, @NonNull retrofit2.Response responseVersion) { + if(connToInternet) { - if (responseVersion.code() == 200) { + if(instanceUrlET.getText().toString().equals("")) { - GiteaVersion version = responseVersion.body(); - assert version != null; + Toasty.info(getApplicationContext(), getString(R.string.emptyFieldURL)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - VersionCheck vt = VersionCheck.check(getString(R.string.versionLow), getString(R.string.versionHigh), version.getVersion()); + } + if(loginToken_.equals("")) { - switch (vt) { - case UNSUPPORTED_NEW: - //Toasty.info(getApplicationContext(), getString(R.string.versionUnsupportedNew)); - case SUPPORTED_LATEST: - case SUPPORTED_OLD: - case DEVELOPMENT: - login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); - return; - case UNSUPPORTED_OLD: + Toasty.info(getApplicationContext(), getString(R.string.loginTokenError)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + return; - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ctx); + } - alertDialogBuilder - .setTitle(getString(R.string.versionAlertDialogHeader)) - .setMessage(getResources().getString(R.string.versionUnsupportedOld, version.getVersion())) - .setCancelable(true) - .setIcon(R.drawable.ic_warning) - .setNegativeButton(getString(R.string.cancelButton), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - enableProcessButton(); - } - }) - .setPositiveButton(getString(R.string.textContinue), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { + versionCheck(instanceUrl, loginUid, loginPass, 123, loginToken_, 2); + } + else { - dialog.dismiss(); - login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); + Toasty.info(getApplicationContext(), getString(R.string.checkNetConnection)); - } - }); + } - AlertDialog alertDialog = alertDialogBuilder.create(); + } - alertDialog.show(); - return; - default: // UNKNOWN - Toasty.info(getApplicationContext(), getString(R.string.versionUnknow)); - enableProcessButton(); + } - } + private void versionCheck(final String instanceUrl, final String loginUid, final String loginPass, final int loginOTP, final String loginToken_, final int loginType) { - } - else if (responseVersion.code() == 403) { - login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); - } - } + final TinyDB tinyDb = new TinyDB(getApplicationContext()); - private void login(int loginType, String instanceUrl, String loginUid, String loginPass, int loginOTP, String loginToken_) { - if (loginType == 1) { - letTheUserIn(instanceUrl, loginUid, loginPass, loginOTP); - } - else if (loginType == 2) { // token - letTheUserInViaToken(instanceUrl, loginToken_); - } - } + Call callVersion = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().getGiteaVersion(); - @Override - public void onFailure(@NonNull Call callVersion, Throwable t) { + callVersion.enqueue(new Callback() { - Log.e("onFailure-version", t.toString()); + @Override + public void onResponse(@NonNull final Call callVersion, @NonNull retrofit2.Response responseVersion) { - if(t instanceof NoRouteToHostException) { - Toasty.info(getApplicationContext(), "Could not connect to host. Please check your url or port " + - "for issues."); - } - else { - Toasty.info(getApplicationContext(), getResources().getString(R.string.commentError)); - } + if(responseVersion.code() == 200) { - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + GiteaVersion version = responseVersion.body(); + assert version != null; - } + VersionCheck vt = VersionCheck.check(getString(R.string.versionLow), getString(R.string.versionHigh), version.getVersion()); - }); + switch(vt) { + case UNSUPPORTED_NEW: + //Toasty.info(getApplicationContext(), getString(R.string.versionUnsupportedNew)); + case SUPPORTED_LATEST: + case SUPPORTED_OLD: + case DEVELOPMENT: + login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); + return; + case UNSUPPORTED_OLD: - } + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ctx); - private void letTheUserInViaToken(String instanceUrl, final String loginToken_) { + alertDialogBuilder.setTitle(getString(R.string.versionAlertDialogHeader)).setMessage(getResources().getString(R.string.versionUnsupportedOld, version.getVersion())).setCancelable(true).setIcon(R.drawable.ic_warning).setNegativeButton(getString(R.string.cancelButton), new DialogInterface.OnClickListener() { - final TinyDB tinyDb = new TinyDB(getApplicationContext()); + @Override + public void onClick(DialogInterface dialog, int which) { - Call call = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .getUserInfo("token " + loginToken_); + dialog.dismiss(); + enableProcessButton(); + } + }).setPositiveButton(getString(R.string.textContinue), new DialogInterface.OnClickListener() { - call.enqueue(new Callback() { + @Override + public void onClick(DialogInterface dialog, int which) { - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + dialog.dismiss(); + login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); - UserInfo userDetails = response.body(); + } + }); - if (response.isSuccessful()) { + AlertDialog alertDialog = alertDialogBuilder.create(); - if (response.code() == 200) { + alertDialog.show(); + return; + default: // UNKNOWN + Toasty.info(getApplicationContext(), getString(R.string.versionUnknow)); + enableProcessButton(); - tinyDb.putBoolean("loggedInMode", true); - assert userDetails != null; - tinyDb.putString(userDetails.getLogin() + "-token", loginToken_); - tinyDb.putString("loginUid", userDetails.getLogin()); + } - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - startActivity(new Intent(LoginActivity.this, MainActivity.class)); - finish(); + } + else if(responseVersion.code() == 403) { + login(loginType, instanceUrl, loginUid, loginPass, loginOTP, loginToken_); + } + } - } + private void login(int loginType, String instanceUrl, String loginUid, String loginPass, int loginOTP, String loginToken_) { - } - else if(response.code() == 401) { + if(loginType == 1) { + letTheUserIn(instanceUrl, loginUid, loginPass, loginOTP); + } + else if(loginType == 2) { // token + letTheUserInViaToken(instanceUrl, loginToken_); + } + } - String toastError = getResources().getString(R.string.unauthorizedApiError); - Toasty.info(getApplicationContext(), toastError); + @Override + public void onFailure(@NonNull Call callVersion, Throwable t) { - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + Log.e("onFailure-version", t.toString()); - } - else { + if(t instanceof NoRouteToHostException) { + Toasty.info(getApplicationContext(), "Could not connect to host. Please check your url or port " + "for issues."); + } + else { + Toasty.info(getApplicationContext(), getResources().getString(R.string.commentError)); + } - String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); - Toasty.info(getApplicationContext(), toastError); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + } - } + }); - } + } - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Log.e("onFailure", t.toString()); - Toasty.info(getApplicationContext(), getResources().getString(R.string.genericError)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - } - }); + private void letTheUserInViaToken(String instanceUrl, final String loginToken_) { - } + final TinyDB tinyDb = new TinyDB(getApplicationContext()); - private void letTheUserIn(final String instanceUrl, final String loginUid, final String loginPass, final int loginOTP) { + Call call = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().getUserInfo("token " + loginToken_); - final String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8); + call.enqueue(new Callback() { - Call> call; - if(loginOTP != 0) { - call = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .getUserTokensWithOTP(credential, loginOTP, loginUid); - } - else { - call = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .getUserTokens(credential, loginUid); - } + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { - call.enqueue(new Callback>() { + UserInfo userDetails = response.body(); - @Override - public void onResponse(@NonNull Call> call, @NonNull retrofit2.Response> response) { + if(response.isSuccessful()) { - List userTokens = response.body(); - final TinyDB tinyDb = new TinyDB(getApplicationContext()); - final AppUtil appUtil = new AppUtil(); - //Headers responseHeaders = response.headers(); + if(response.code() == 200) { - if (response.isSuccessful()) { + tinyDb.putBoolean("loggedInMode", true); + assert userDetails != null; + tinyDb.putString(userDetails.getLogin() + "-token", loginToken_); + tinyDb.putString("loginUid", userDetails.getLogin()); - if (response.code() == 200) { + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + startActivity(new Intent(LoginActivity.this, MainActivity.class)); + finish(); - boolean setTokenFlag = false; + } - assert userTokens != null; - if (userTokens.size() > 0) { + } + else if(response.code() == 401) { - if(userTokens.get(0).getToken_last_eight() != null) { + String toastError = getResources().getString(R.string.unauthorizedApiError); + Toasty.info(getApplicationContext(), toastError); - for (int i = 0; i < userTokens.size(); i++) { - if (userTokens.get(i).getToken_last_eight().equals(tinyDb.getString(loginUid + "-token-last-eight"))) { - setTokenFlag = true; - break; - } - //Log.i("Tokens: ", userTokens.get(i).getToken_last_eight()); - } + enableProcessButton(); + loginButton.setText(R.string.btnLogin); - } - else { + } + else { - for (int i = 0; i < userTokens.size(); i++) { - if (userTokens.get(i).getSha1().equals(tinyDb.getString(loginUid + "-token"))) { - setTokenFlag = true; - break; - } - //Log.i("Tokens: ", userTokens.get(i).getSha1()); - } + String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); + Toasty.info(getApplicationContext(), toastError); - } + enableProcessButton(); + loginButton.setText(R.string.btnLogin); - } + } - if(tinyDb.getString(loginUid + "-token").isEmpty() || !setTokenFlag) { + } - UserTokens createUserToken = new UserTokens("gitnex-app-" + device_id); + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Call callCreateToken; - if(loginOTP != 0) { - callCreateToken = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .createNewTokenWithOTP(credential, loginOTP, loginUid, createUserToken); - } - else { - callCreateToken = RetrofitClient - .getInstance(instanceUrl, getApplicationContext()) - .getApiInterface() - .createNewToken(credential, loginUid, createUserToken); - } + Log.e("onFailure", t.toString()); + Toasty.info(getApplicationContext(), getResources().getString(R.string.genericError)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + } + }); - callCreateToken.enqueue(new Callback() { + } - @Override - public void onResponse(@NonNull Call callCreateToken, @NonNull retrofit2.Response responseCreate) { + private void letTheUserIn(final String instanceUrl, final String loginUid, final String loginPass, final int loginOTP) { - if(responseCreate.isSuccessful()) { + final String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8); - if(responseCreate.code() == 201) { + Call> call; + if(loginOTP != 0) { + call = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().getUserTokensWithOTP(credential, loginOTP, loginUid); + } + else { + call = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().getUserTokens(credential, loginUid); + } - UserTokens newToken = responseCreate.body(); - assert newToken != null; - //Log.i("Tokens-NEW", "new:" + newToken.getSha1()); + call.enqueue(new Callback>() { - if (!newToken.getSha1().equals("")) { + @Override + public void onResponse(@NonNull Call> call, @NonNull retrofit2.Response> response) { - tinyDb.remove("loginPass"); - tinyDb.putBoolean("loggedInMode", true); - tinyDb.putString(loginUid + "-token", newToken.getSha1()); - tinyDb.putString(loginUid + "-token-last-eight", appUtil.getLastCharactersOfWord(newToken.getSha1(), 8)); - //Log.i("Tokens", "new:" + newToken.getSha1() + " old:" + tinyDb.getString(loginUid + "-token")); + List userTokens = response.body(); + final TinyDB tinyDb = new TinyDB(getApplicationContext()); + final AppUtil appUtil = new AppUtil(); + //Headers responseHeaders = response.headers(); - startActivity(new Intent(LoginActivity.this, MainActivity.class)); - finish(); + if(response.isSuccessful()) { - } + if(response.code() == 200) { - } + boolean setTokenFlag = false; - } - else if(responseCreate.code() == 500) { + assert userTokens != null; + if(userTokens.size() > 0) { - String toastError = getResources().getString(R.string.genericApiStatusError) + responseCreate.code(); - Toasty.info(getApplicationContext(), toastError); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + if(userTokens.get(0).getToken_last_eight() != null) { - } + for(int i = 0; i < userTokens.size(); i++) { + if(userTokens.get(i).getToken_last_eight().equals(tinyDb.getString(loginUid + "-token-last-eight"))) { + setTokenFlag = true; + break; + } + //Log.i("Tokens: ", userTokens.get(i).getToken_last_eight()); + } - } + } + else { - @Override - public void onFailure(@NonNull Call createUserToken, Throwable t) { + for(int i = 0; i < userTokens.size(); i++) { + if(userTokens.get(i).getSha1().equals(tinyDb.getString(loginUid + "-token"))) { + setTokenFlag = true; + break; + } + //Log.i("Tokens: ", userTokens.get(i).getSha1()); + } - } + } - }); - } - else { + } - //Log.i("Current Token", tinyDb.getString(loginUid + "-token")); - tinyDb.putBoolean("loggedInMode", true); - startActivity(new Intent(LoginActivity.this, MainActivity.class)); - finish(); + if(tinyDb.getString(loginUid + "-token").isEmpty() || !setTokenFlag) { - } + UserTokens createUserToken = new UserTokens("gitnex-app-" + device_id); - } + Call callCreateToken; + if(loginOTP != 0) { + callCreateToken = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().createNewTokenWithOTP(credential, loginOTP, loginUid, createUserToken); + } + else { + callCreateToken = RetrofitClient.getInstance(instanceUrl, getApplicationContext()).getApiInterface().createNewToken(credential, loginUid, createUserToken); + } - } - else if(response.code() == 500) { + callCreateToken.enqueue(new Callback() { - String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); - Toasty.info(getApplicationContext(), toastError); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + @Override + public void onResponse(@NonNull Call callCreateToken, @NonNull retrofit2.Response responseCreate) { - } - else { + if(responseCreate.isSuccessful()) { - String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); - //Log.i("error message else4", String.valueOf(response.code())); + if(responseCreate.code() == 201) { - Toasty.info(getApplicationContext(), toastError); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); + UserTokens newToken = responseCreate.body(); + assert newToken != null; + //Log.i("Tokens-NEW", "new:" + newToken.getSha1()); - } + if(!newToken.getSha1().equals("")) { - } + tinyDb.remove("loginPass"); + tinyDb.putBoolean("loggedInMode", true); + tinyDb.putString(loginUid + "-token", newToken.getSha1()); + tinyDb.putString(loginUid + "-token-last-eight", appUtil.getLastCharactersOfWord(newToken.getSha1(), 8)); + //Log.i("Tokens", "new:" + newToken.getSha1() + " old:" + tinyDb.getString(loginUid + "-token")); - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e("onFailure-login", t.toString()); - Toasty.info(getApplicationContext(), getResources().getString(R.string.malformedJson)); - enableProcessButton(); - loginButton.setText(R.string.btnLogin); - } - }); + startActivity(new Intent(LoginActivity.this, MainActivity.class)); + finish(); - } + } - private void disableProcessButton() { + } - loginButton.setEnabled(false); - GradientDrawable shape = new GradientDrawable(); - shape.setCornerRadius( 8 ); - shape.setColor(getResources().getColor(R.color.hintColor)); - loginButton.setBackground(shape); + } + else if(responseCreate.code() == 500) { - } + String toastError = getResources().getString(R.string.genericApiStatusError) + responseCreate.code(); + Toasty.info(getApplicationContext(), toastError); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); - private void enableProcessButton() { + } - loginButton.setEnabled(true); - GradientDrawable shape = new GradientDrawable(); - shape.setCornerRadius( 8 ); - shape.setColor(getResources().getColor(R.color.btnBackground)); - loginButton.setBackground(shape); + } - } + @Override + public void onFailure(@NonNull Call createUserToken, Throwable t) { + + } + + }); + } + else { + + //Log.i("Current Token", tinyDb.getString(loginUid + "-token")); + tinyDb.putBoolean("loggedInMode", true); + startActivity(new Intent(LoginActivity.this, MainActivity.class)); + finish(); + + } + + } + + } + else if(response.code() == 500) { + + String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); + Toasty.info(getApplicationContext(), toastError); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + + } + else { + + String toastError = getResources().getString(R.string.genericApiStatusError) + response.code(); + //Log.i("error message else4", String.valueOf(response.code())); + + Toasty.info(getApplicationContext(), toastError); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + + } + + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + + Log.e("onFailure-login", t.toString()); + Toasty.info(getApplicationContext(), getResources().getString(R.string.malformedJson)); + enableProcessButton(); + loginButton.setText(R.string.btnLogin); + } + }); + + } + + private void disableProcessButton() { + + loginButton.setEnabled(false); + GradientDrawable shape = new GradientDrawable(); + shape.setCornerRadius(8); + shape.setColor(getResources().getColor(R.color.hintColor)); + loginButton.setBackground(shape); + + } + + private void enableProcessButton() { + + loginButton.setEnabled(true); + GradientDrawable shape = new GradientDrawable(); + shape.setCornerRadius(8); + shape.setColor(getResources().getColor(R.color.btnBackground)); + loginButton.setBackground(shape); + + } } From b4996c0e6de03056b76fe89cdf8f57b2a6b673be Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 21:14:49 +0200 Subject: [PATCH 11/24] Even more reformatting. --- .../mian/gitnex/clients/IssuesService.java | 69 ++--- .../gitnex/clients/PullRequestsService.java | 69 ++--- .../mian/gitnex/clients/RetrofitClient.java | 84 +++-- .../gitnex/helpers/MemorizingActivity.java | 3 + .../helpers/MemorizingTrustManager.java | 286 +++++++++++------- 5 files changed, 290 insertions(+), 221 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index 28d26ac7..999b9ad7 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -26,48 +26,49 @@ import retrofit2.converter.gson.GsonConverterFactory; public class IssuesService { - public static S createService(Class serviceClass, String instanceURL, Context ctx) { + public static S createService(Class serviceClass, String instanceURL, Context ctx) { - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); - int cacheSize = 50 * 1024 * 1024; // 50MB - Cache cache = new Cache(httpCacheDirectory, cacheSize); + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + int cacheSize = 50 * 1024 * 1024; // 50MB + Cache cache = new Cache(httpCacheDirectory, cacheSize); - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - logging.setLevel(HttpLoggingInterceptor.Level.BODY); + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); - sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { - @NonNull - @Override - public Response intercept(@NonNull Chain chain) throws IOException { + @NonNull + @Override + public Response intercept(@NonNull Chain chain) throws IOException { - Request request = chain.request(); - if(connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } - else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } - return chain.proceed(request); - } - }).build(); + Request request = chain.request(); + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); + } + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } + return chain.proceed(request); + } + }).build(); - Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); - Retrofit retrofit = builder.build(); - return retrofit.create(serviceClass); - } - catch(Exception e) { - Log.e("onFailure", e.toString()); - } + Retrofit retrofit = builder.build(); + return retrofit.create(serviceClass); + } + catch(Exception e) { + Log.e("onFailure", e.toString()); + } + + return null; + } - return null; - } } diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index 50731225..bf2beb96 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -26,48 +26,49 @@ import retrofit2.converter.gson.GsonConverterFactory; public class PullRequestsService { - public static S createService(Class serviceClass, String instanceURL, Context ctx) { + public static S createService(Class serviceClass, String instanceURL, Context ctx) { - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); - int cacheSize = 50 * 1024 * 1024; // 50MB - Cache cache = new Cache(httpCacheDirectory, cacheSize); + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + int cacheSize = 50 * 1024 * 1024; // 50MB + Cache cache = new Cache(httpCacheDirectory, cacheSize); - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - logging.setLevel(HttpLoggingInterceptor.Level.BODY); + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); - sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { - @NonNull - @Override - public Response intercept(@NonNull Chain chain) throws IOException { + @NonNull + @Override + public Response intercept(@NonNull Chain chain) throws IOException { - Request request = chain.request(); - if(connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } - else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } - return chain.proceed(request); - } - }).build(); + Request request = chain.request(); + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); + } + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } + return chain.proceed(request); + } + }).build(); - Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); - Retrofit retrofit = builder.build(); - return retrofit.create(serviceClass); - } - catch(Exception e) { - Log.e("onFailure", e.toString()); - } + Retrofit retrofit = builder.build(); + return retrofit.create(serviceClass); + } + catch(Exception e) { + Log.e("onFailure", e.toString()); + } + + return null; + } - return null; - } } diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index a2794ec7..9e677ff9 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -24,60 +24,56 @@ import retrofit2.converter.scalars.ScalarsConverterFactory; public class RetrofitClient { - private Retrofit retrofit; + private Retrofit retrofit; - private RetrofitClient(String instanceUrl, Context ctx) { - final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); - int cacheSize = 50 * 1024 * 1024; // 50MB - File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); - Cache cache = new Cache(httpCacheDirectory, cacheSize); + private RetrofitClient(String instanceUrl, Context ctx) { - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - logging.setLevel(HttpLoggingInterceptor.Level.BODY); + final boolean connToInternet = AppUtil.haveNetworkConnection(ctx); + int cacheSize = 50 * 1024 * 1024; // 50MB + File httpCacheDirectory = new File(ctx.getCacheDir(), "responses"); + Cache cache = new Cache(httpCacheDirectory, cacheSize); - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); - MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); - sslContext.init(null, new X509TrustManager[] { memorizingTrustManager }, new SecureRandom()); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); - OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() - .cache(cache) - //.addInterceptor(logging) - .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) - .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) - .addInterceptor(chain -> { + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - Request request = chain.request(); - if(connToInternet) { - request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); - } - else { - request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); - } - return chain.proceed(request); - }); + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder().cache(cache) + //.addInterceptor(logging) + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(chain -> { - Retrofit.Builder builder = new Retrofit.Builder() - .baseUrl(instanceUrl) - .client(okHttpClient.build()) - .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(GsonConverterFactory.create()); + Request request = chain.request(); + if(connToInternet) { + request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build(); + } + else { + request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build(); + } + return chain.proceed(request); + }); - retrofit = builder.build(); + Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceUrl).client(okHttpClient.build()).addConverterFactory(ScalarsConverterFactory.create()).addConverterFactory(GsonConverterFactory.create()); - } - catch(Exception e) { - Log.e("onFailure", e.toString()); - } - } + retrofit = builder.build(); - public static synchronized RetrofitClient getInstance(String instanceUrl, Context ctx) { - return new RetrofitClient(instanceUrl, ctx); - } + } + catch(Exception e) { + Log.e("onFailure", e.toString()); + } + } - public ApiInterface getApiInterface() { - return retrofit.create(ApiInterface.class); - } + public static synchronized RetrofitClient getInstance(String instanceUrl, Context ctx) { + + return new RetrofitClient(instanceUrl, ctx); + } + + public ApiInterface getApiInterface() { + + return retrofit.create(ApiInterface.class); + } } diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java index 1de5c9fa..a787a007 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java @@ -14,6 +14,7 @@ public class MemorizingActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); Intent intent = getIntent(); @@ -34,7 +35,9 @@ public class MemorizingActivity extends Activity { } private void onSendResult(int decisionId, int decision) { + MemorizingTrustManager.interactResult(decisionId, decision); finish(); } + } \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java index 033cd9b7..bef13f21 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java @@ -46,9 +46,10 @@ import javax.net.ssl.X509TrustManager; */ public class MemorizingTrustManager implements X509TrustManager { + private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; - final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; - final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert"; + final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; + final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert"; private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName()); final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId"; @@ -69,44 +70,49 @@ public class MemorizingTrustManager implements X509TrustManager { private X509TrustManager defaultTrustManager; private X509TrustManager appTrustManager; - /** Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager. - * + /** + * Creates an instance of the MemorizingTrustManager class that falls back to a custom TrustManager. + *

* You need to supply the application context. This has to be one of: - * - Application - * - Activity - * - Service - * + * - Application + * - Activity + * - Service + *

* The context is used for file management, to display the dialog / * notification and for obtaining translated strings. * - * @param m Context for the application. + * @param m Context for the application. * @param defaultTrustManager Delegate trust management to this TM. If null, the user must accept every certificate. */ public MemorizingTrustManager(Context m, X509TrustManager defaultTrustManager) { + init(m); this.appTrustManager = getTrustManager(appKeyStore); this.defaultTrustManager = defaultTrustManager; } - /** Creates an instance of the MemorizingTrustManager class using the system X509TrustManager. - * + /** + * Creates an instance of the MemorizingTrustManager class using the system X509TrustManager. + *

* You need to supply the application context. This has to be one of: - * - Application - * - Activity - * - Service - * + * - Application + * - Activity + * - Service + *

* The context is used for file management, to display the dialog / * notification and for obtaining translated strings. * * @param m Context for the application. */ public MemorizingTrustManager(Context m) { + init(m); this.appTrustManager = getTrustManager(appKeyStore); this.defaultTrustManager = getTrustManager(null); } private void init(Context m) { + context = m; masterHandler = new Handler(m.getMainLooper()); notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); @@ -115,11 +121,11 @@ public class MemorizingTrustManager implements X509TrustManager { appKeyStore = loadAppKeyStore(); } - + /** * Returns a X509TrustManager list containing a new instance of * TrustManagerFactory. - * + *

* This function is meant for convenience only. You can use it * as follows to integrate TrustManagerFactory for HTTPS sockets: * @@ -129,31 +135,34 @@ public class MemorizingTrustManager implements X509TrustManager { * new java.security.SecureRandom()); * HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); * + * * @param c Activity or Service to show the Dialog / Notification */ public static X509TrustManager[] getInstanceList(Context c) { - return new X509TrustManager[] { new MemorizingTrustManager(c) }; + + return new X509TrustManager[]{new MemorizingTrustManager(c)}; } /** * Binds an Activity to the MTM for displaying the query dialog. - * + *

* This is useful if your connection is run from a service that is * triggered by user interaction -- in such cases the activity is * visible and the user tends to ignore the service notification. - * + *

* You should never have a hidden activity bound to MTM! Use this * function in onResume() and @see unbindDisplayActivity in onPause(). * * @param act Activity to be bound */ private void bindDisplayActivity(Activity act) { + foregroundAct = act; } /** * Removes an Activity from the MTM display stack. - * + *

* Always call this function when the Activity added with * {@link #bindDisplayActivity(Activity)} is hidden. * @@ -161,8 +170,9 @@ public class MemorizingTrustManager implements X509TrustManager { */ public void unbindDisplayActivity(Activity act) { // do not remove if it was overridden by a different activity - if (foregroundAct == act) + if(foregroundAct == act) { foregroundAct = null; + } } /** @@ -171,9 +181,11 @@ public class MemorizingTrustManager implements X509TrustManager { * @return an {@link Enumeration} of all certificates */ private Enumeration getCertificates() { + try { return appKeyStore.aliases(); - } catch (KeyStoreException e) { + } + catch(KeyStoreException e) { // this should never happen, however... throw new RuntimeException(e); } @@ -183,13 +195,14 @@ public class MemorizingTrustManager implements X509TrustManager { * Get a certificate for a given alias. * * @param alias the certificate's alias as returned by {@link #getCertificates()}. - * * @return the certificate associated with the alias or null if none found. */ public Certificate getCertificate(String alias) { + try { return appKeyStore.getCertificate(alias); - } catch (KeyStoreException e) { + } + catch(KeyStoreException e) { // this should never happen, however... throw new RuntimeException(e); } @@ -204,11 +217,12 @@ public class MemorizingTrustManager implements X509TrustManager { * (b) new connections are created using TLS renegotiation, without a new cert * check. *

- * @param alias the certificate's alias as returned by {@link #getCertificates()}. * + * @param alias the certificate's alias as returned by {@link #getCertificates()}. * @throws KeyStoreException if the certificate could not be deleted. */ public void deleteCertificate(String alias) throws KeyStoreException { + appKeyStore.deleteEntry(alias); keyStoreUpdated(); } @@ -220,36 +234,39 @@ public class MemorizingTrustManager implements X509TrustManager { * the given instance of {@link MemorizingTrustManager}, and leverages an * existing {@link HostnameVerifier}. The returned verifier performs the * following steps, returning as soon as one of them succeeds: - *

- *
    - *
  1. Success, if the wrapped defaultVerifier accepts the certificate.
  2. - *
  3. Success, if the server certificate is stored in the keystore under the given hostname.
  4. - *
  5. Ask the user and return accordingly.
  6. - *
  7. Failure on exception.
  8. - *
+ * /p> + *
    + *
  1. Success, if the wrapped defaultVerifier accepts the certificate.
  2. + *
  3. Success, if the server certificate is stored in the keystore under the given hostname.
  4. + *
  5. Ask the user and return accordingly.
  6. + *
  7. Failure on exception.
  8. + *
* * @param defaultVerifier the {@link HostnameVerifier} that should perform the actual check * @return a new hostname verifier using the MTM's key store - * * @throws IllegalArgumentException if the defaultVerifier parameter is null */ public HostnameVerifier wrapHostnameVerifier(final HostnameVerifier defaultVerifier) { - if (defaultVerifier == null) + + if(defaultVerifier == null) { throw new IllegalArgumentException("The default verifier may not be null"); - + } + return new MemorizingHostnameVerifier(defaultVerifier); } - + private X509TrustManager getTrustManager(KeyStore ks) { + try { TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); tmf.init(ks); - for (TrustManager t : tmf.getTrustManagers()) { - if (t instanceof X509TrustManager) { - return (X509TrustManager)t; + for(TrustManager t : tmf.getTrustManagers()) { + if(t instanceof X509TrustManager) { + return (X509TrustManager) t; } } - } catch (Exception e) { + } + catch(Exception e) { e.printStackTrace(); } @@ -257,18 +274,21 @@ public class MemorizingTrustManager implements X509TrustManager { } private KeyStore loadAppKeyStore() { + KeyStore keyStore; try { keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - } catch (KeyStoreException e) { + } + catch(KeyStoreException e) { e.printStackTrace(); return null; } try { keyStore.load(null, null); - } catch (NoSuchAlgorithmException | CertificateException | IOException e) { + } + catch(NoSuchAlgorithmException | CertificateException | IOException e) { e.printStackTrace(); } @@ -280,7 +300,8 @@ public class MemorizingTrustManager implements X509TrustManager { try { keyStore.load(inputStream, "MTM".toCharArray()); inputStream.close(); - } catch(Exception e) { + } + catch(Exception e) { e.printStackTrace(); } } @@ -289,17 +310,20 @@ public class MemorizingTrustManager implements X509TrustManager { } private void storeCert(String alias, Certificate cert) { + try { appKeyStore.setCertificateEntry(alias, cert); - } catch (KeyStoreException e) { + } + catch(KeyStoreException e) { e.printStackTrace(); return; } keyStoreUpdated(); } - + private void storeCert(X509Certificate cert) { + storeCert(cert.getSubjectDN().toString(), cert); } @@ -316,69 +340,83 @@ public class MemorizingTrustManager implements X509TrustManager { byteArrayOutputStream.close(); keyStoreStorage.edit().putString(KEYSTORE_KEY, Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT)).apply(); - } catch (Exception e) { + } + catch(Exception e) { e.printStackTrace(); } } // if the certificate is stored in the app key store, it is considered "known" private boolean isCertKnown(X509Certificate cert) { + try { return appKeyStore.getCertificateAlias(cert) != null; - } catch (KeyStoreException e) { + } + catch(KeyStoreException e) { return false; } } private static boolean isExpiredException(Throwable e) { + do { - if (e instanceof CertificateExpiredException) + if(e instanceof CertificateExpiredException) { return true; + } e = e.getCause(); - } while (e != null); + } while(e != null); return false; } private static boolean isPathException(Throwable e) { + do { - if (e instanceof CertPathValidatorException) + if(e instanceof CertPathValidatorException) { return true; + } e = e.getCause(); - } while (e != null); + } while(e != null); return false; } private void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer) throws CertificateException { + LOGGER.log(Level.FINE, "checkCertTrusted(" + Arrays.toString(chain) + ", " + authType + ", " + isServer + ")"); try { LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager"); - if (isServer) + if(isServer) { appTrustManager.checkServerTrusted(chain, authType); - else + } + else { appTrustManager.checkClientTrusted(chain, authType); - } catch (CertificateException ae) { + } + } + catch(CertificateException ae) { LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager did not verify certificate. Will fall back to secondary verification mechanisms (if any).", ae); // if the cert is stored in our appTrustManager, we ignore expiredness - if (isExpiredException(ae)) { + if(isExpiredException(ae)) { LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore"); return; } - if (isCertKnown(chain[0])) { + if(isCertKnown(chain[0])) { LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore"); return; } try { - if (defaultTrustManager == null) { + if(defaultTrustManager == null) { LOGGER.fine("No defaultTrustManager set. Verification failed, throwing " + ae); throw ae; } LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager"); - if (isServer) + if(isServer) { defaultTrustManager.checkServerTrusted(chain, authType); - else + } + else { defaultTrustManager.checkClientTrusted(chain, authType); - } catch (CertificateException e) { + } + } + catch(CertificateException e) { LOGGER.log(Level.FINER, "checkCertTrusted: defaultTrustManager failed", e); interactCert(chain, authType, e); } @@ -386,18 +424,22 @@ public class MemorizingTrustManager implements X509TrustManager { } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + checkCertTrusted(chain, authType, false); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + checkCertTrusted(chain, authType, true); } public X509Certificate[] getAcceptedIssuers() { + return defaultTrustManager.getAcceptedIssuers(); } private static int createDecisionId(MTMDecision d) { + int myId; synchronized(openDecisions) { myId = decisionId; @@ -408,26 +450,31 @@ public class MemorizingTrustManager implements X509TrustManager { } private static String hexString(byte[] data) { + StringBuilder si = new StringBuilder(); - for (int i = 0; i < data.length; i++) { + for(int i = 0; i < data.length; i++) { si.append(String.format("%02x", data[i])); - if (i < data.length - 1) + if(i < data.length - 1) { si.append(":"); + } } return si.toString(); } private static String certHash(final X509Certificate cert, String digest) { + try { MessageDigest md = MessageDigest.getInstance(digest); md.update(cert.getEncoded()); return hexString(md.digest()); - } catch (CertificateEncodingException | NoSuchAlgorithmException e) { + } + catch(CertificateEncodingException | NoSuchAlgorithmException e) { return e.getMessage(); } } private static void certDetails(StringBuilder si, X509Certificate c) { + SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); si.append("\n"); si.append(c.getSubjectDN().toString()); @@ -443,18 +490,22 @@ public class MemorizingTrustManager implements X509TrustManager { si.append(c.getIssuerDN().toString()); si.append("\n"); } - + private String certChainMessage(final X509Certificate[] chain, CertificateException cause) { + Throwable e = cause; StringBuilder si = new StringBuilder(); - if (isPathException(e)) { + if(isPathException(e)) { si.append(context.getString(R.string.mtm_trust_anchor)); - } else if (isExpiredException(e)) { + } + else if(isExpiredException(e)) { si.append(context.getString(R.string.mtm_cert_expired)); - } else { + } + else { // get to the cause - while (e.getCause() != null) e = e.getCause(); + while(e.getCause() != null) + e = e.getCause(); si.append(e.getLocalizedMessage()); } @@ -462,13 +513,14 @@ public class MemorizingTrustManager implements X509TrustManager { si.append(context.getString(R.string.mtm_connect_anyway)); si.append("\n\n"); si.append(context.getString(R.string.mtm_cert_details)); - for (X509Certificate c : chain) { + for(X509Certificate c : chain) { certDetails(si, c); } return si.toString(); } private String hostNameMessage(X509Certificate cert, String hostname) { + StringBuilder si = new StringBuilder(); si.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); @@ -476,20 +528,24 @@ public class MemorizingTrustManager implements X509TrustManager { try { Collection> sans = cert.getSubjectAlternativeNames(); - if (sans == null) { + if(sans == null) { si.append(cert.getSubjectDN()); si.append("\n"); - } else for (List altName : sans) { - Object name = altName.get(1); - if (name instanceof String) { - si.append("["); - si.append(altName.get(0)); - si.append("] "); - si.append(name); - si.append("\n"); + } + else { + for(List altName : sans) { + Object name = altName.get(1); + if(name instanceof String) { + si.append("["); + si.append(altName.get(0)); + si.append("] "); + si.append(name); + si.append("\n"); + } } } - } catch (CertificateParsingException e) { + } + catch(CertificateParsingException e) { e.printStackTrace(); si.append("Notification.setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent) * since it was remove in Android API level 23. - * */ private static void setLatestEventInfoReflective(Notification notification, Context context, CharSequence mtmNotification, CharSequence certName, PendingIntent call) { + Method setLatestEventInfo; try { - setLatestEventInfo = notification.getClass().getMethod( - "setLatestEventInfo", Context.class, CharSequence.class, - CharSequence.class, PendingIntent.class); - } catch (NoSuchMethodException e) { + setLatestEventInfo = notification.getClass().getMethod("setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class); + } + catch(NoSuchMethodException e) { throw new IllegalStateException(e); } try { setLatestEventInfo.invoke(notification, context, mtmNotification, certName, call); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + } + catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalStateException(e); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void startActivityNotification(Intent intent, int decisionId, String certName) { + final PendingIntent call = PendingIntent.getActivity(context, 0, intent, 0); final String mtmNotification = context.getString(R.string.mtm_notification); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl") - .setSmallIcon(android.R.drawable.ic_lock_lock) - .setContentTitle(mtmNotification) - .setContentText(certName) - .setTicker(certName) - .setContentIntent(call) - .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_HIGH); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl").setSmallIcon(android.R.drawable.ic_lock_lock).setContentTitle(mtmNotification).setContentText(certName).setTicker(certName).setContentIntent(call).setAutoCancel(true).setPriority(NotificationCompat.PRIORITY_HIGH); notificationManager.notify(NOTIFICATION_ID + decisionId, builder.build()); } @@ -551,6 +601,7 @@ public class MemorizingTrustManager implements X509TrustManager { * @return the Context of the currently bound UI or the master context if none is bound */ Context getUI() { + return (foregroundAct != null) ? foregroundAct : context; } @@ -560,7 +611,9 @@ public class MemorizingTrustManager implements X509TrustManager { final int myId = createDecisionId(choice); masterHandler.post(new Runnable() { + public void run() { + Intent intent = new Intent(context, MemorizingActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId)); @@ -575,7 +628,8 @@ public class MemorizingTrustManager implements X509TrustManager { // deployed. try { foregroundAct.startActivity(intent); - } catch (Exception e) { + } + catch(Exception e) { startActivityNotification(intent, myId, message); } } @@ -585,15 +639,17 @@ public class MemorizingTrustManager implements X509TrustManager { synchronized(choice) { choice.wait(); } - } catch (InterruptedException e) { + } + catch(InterruptedException e) { e.printStackTrace(); } return choice.state; } - + private void interactCert(final X509Certificate[] chain, String authType, CertificateException cause) throws CertificateException { - switch (interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) { + + switch(interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) { case MTMDecision.DECISION_ALWAYS: storeCert(chain[0]); // only store the server cert, not the whole chain case MTMDecision.DECISION_ONCE: @@ -604,7 +660,8 @@ public class MemorizingTrustManager implements X509TrustManager { } private boolean interactHostname(X509Certificate cert, String hostname) { - switch (interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) { + + switch(interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) { case MTMDecision.DECISION_ALWAYS: storeCert(hostname, cert); case MTMDecision.DECISION_ONCE: @@ -615,46 +672,57 @@ public class MemorizingTrustManager implements X509TrustManager { } static void interactResult(int decisionId, int choice) { + MTMDecision d; synchronized(openDecisions) { - d = openDecisions.get(decisionId); - openDecisions.remove(decisionId); + d = openDecisions.get(decisionId); + openDecisions.remove(decisionId); } - if (d == null) return; + if(d == null) { + return; + } synchronized(d) { d.state = choice; d.notify(); } } - + class MemorizingHostnameVerifier implements HostnameVerifier { + private HostnameVerifier defaultVerifier; - + MemorizingHostnameVerifier(HostnameVerifier wrapped) { + defaultVerifier = wrapped; } @Override public boolean verify(String hostname, SSLSession session) { // if the default verifier accepts the hostname, we are done - if (defaultVerifier.verify(hostname, session)) return true; + if(defaultVerifier.verify(hostname, session)) { + return true; + } // otherwise, we check if the hostname is an alias for this cert in our keystore try { - X509Certificate cert = (X509Certificate)session.getPeerCertificates()[0]; + X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0]; - if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) { + if(cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) { return true; - } else { + } + else { return interactHostname(cert, hostname); } - } catch (Exception e) { + } + catch(Exception e) { e.printStackTrace(); return false; } } + } + } From 5fdbc98d6ee88fe71c1a7727cb37af1801ef56f2 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 21:21:49 +0200 Subject: [PATCH 12/24] Text to resource. --- app/src/main/java/org/mian/gitnex/activities/LoginActivity.java | 2 +- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 71f44827..c900602b 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -464,7 +464,7 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener Log.e("onFailure-version", t.toString()); if(t instanceof NoRouteToHostException) { - Toasty.info(getApplicationContext(), "Could not connect to host. Please check your url or port " + "for issues."); + Toasty.info(getApplicationContext(), getResources().getString(R.string.malformedUrl)); } else { Toasty.info(getApplicationContext(), getResources().getString(R.string.commentError)); diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5c9368d8..1d24ad5e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -63,6 +63,7 @@ In Gitea anmelden 1- Wähle das richtige Protokoll (https oder http) \ N2- Gebe die Gitea-URL ein, z.B. try.gitea.io \n3- Falls du 2FA aktiviert hast, gib den Code in das OTP-Code Feld ein. \n4- Für HTTP basic auth nutze BENUTZERNAME@DOMAIN.COM im URL Feld Falscher Benutzername/Passwort + Verbindung zum Host fehlgeschlagen. Bitte prüfen Sie Ihre URL und den Port auf etwaige Fehler. Es wird nicht empfohlen, das HTTP-Protokoll zu verwenden, es sei denn, du testest in einem lokalen Netzwerk. Fehlerhaftes JSON erhalten. Serveranfrage war nicht erfolgreich. Instanzen-URL ist erforderlich diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f22d81b..1ed2ddd7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,6 +92,7 @@ 1- Choose the correct protocol(https or http). \n2- Enter Gitea url e.g: try.gitea.io. \n3- If you have enabled 2FA for your account, enter the code in the OTP Code field. \n4- For HTTP basic auth use USERNAME@DOMAIN.COM in the URL field. Wrong username/password :// + Could'nt connect to host. Please check your URL or port for any errors. It is not recommended to use HTTP protocol unless you are testing on local network. Malformed JSON was received. Server response was not successful. Instance URL is required From e5a656ce577ded6e59bd24bd4b0f0a2825a13a46 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Tue, 31 Mar 2020 22:09:42 +0200 Subject: [PATCH 13/24] Many improvements and bug fixes. --- .../mian/gitnex/clients/IssuesService.java | 2 +- .../gitnex/clients/PullRequestsService.java | 2 +- .../mian/gitnex/clients/RetrofitClient.java | 2 +- .../gitnex/helpers/MemorizingActivity.java | 6 +- .../helpers/MemorizingTrustManager.java | 194 +++++++----------- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 79 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index 999b9ad7..b132aff0 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -36,7 +36,7 @@ public class IssuesService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index bf2beb96..23364211 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -36,7 +36,7 @@ public class PullRequestsService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index 9e677ff9..85517923 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -36,7 +36,7 @@ public class RetrofitClient { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { + try { // try-catch can be problematic here SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java index a787a007..ee20979e 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java @@ -18,9 +18,9 @@ public class MemorizingActivity extends Activity { super.onCreate(savedInstanceState); Intent intent = getIntent(); - int decisionId = intent.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID); - int titleId = intent.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert); - String cert = intent.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT); + int decisionId = intent.getIntExtra("DECISION_INTENT_ID", MTMDecision.DECISION_INVALID); + int titleId = intent.getIntExtra("DECISION_TITLE_ID", R.string.mtm_accept_cert); + String cert = intent.getStringExtra("DECISION_INTENT_CERT"); AlertDialog.Builder builder = new AlertDialog.Builder(MemorizingActivity.this); builder.setTitle(titleId); diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java index bef13f21..ece64672 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java @@ -46,20 +46,12 @@ import javax.net.ssl.X509TrustManager; */ public class MemorizingTrustManager implements X509TrustManager { - - private final static String DECISION_INTENT = "de.duenndns.ssl.DECISION"; - final static String DECISION_INTENT_ID = DECISION_INTENT + ".decisionId"; - final static String DECISION_INTENT_CERT = DECISION_INTENT + ".cert"; - - private final static Logger LOGGER = Logger.getLogger(MemorizingTrustManager.class.getName()); - final static String DECISION_TITLE_ID = DECISION_INTENT + ".titleId"; private final static int NOTIFICATION_ID = 100509; private final static String KEYSTORE_NAME = "keystore"; private final static String KEYSTORE_KEY = "keystore"; private Context context; - private Activity foregroundAct; private NotificationManager notificationManager; private static int decisionId = 0; private static final SparseArray openDecisions = new SparseArray<>(); @@ -143,38 +135,6 @@ public class MemorizingTrustManager implements X509TrustManager { return new X509TrustManager[]{new MemorizingTrustManager(c)}; } - /** - * Binds an Activity to the MTM for displaying the query dialog. - *

- * This is useful if your connection is run from a service that is - * triggered by user interaction -- in such cases the activity is - * visible and the user tends to ignore the service notification. - *

- * You should never have a hidden activity bound to MTM! Use this - * function in onResume() and @see unbindDisplayActivity in onPause(). - * - * @param act Activity to be bound - */ - private void bindDisplayActivity(Activity act) { - - foregroundAct = act; - } - - /** - * Removes an Activity from the MTM display stack. - *

- * Always call this function when the Activity added with - * {@link #bindDisplayActivity(Activity)} is hidden. - * - * @param act Activity to be unbound - */ - public void unbindDisplayActivity(Activity act) { - // do not remove if it was overridden by a different activity - if(foregroundAct == act) { - foregroundAct = null; - } - } - /** * Get a list of all certificate aliases stored in MTM. * @@ -365,6 +325,7 @@ public class MemorizingTrustManager implements X509TrustManager { } e = e.getCause(); } while(e != null); + return false; } @@ -374,17 +335,16 @@ public class MemorizingTrustManager implements X509TrustManager { if(e instanceof CertPathValidatorException) { return true; } + e = e.getCause(); } while(e != null); + return false; } private void checkCertTrusted(X509Certificate[] chain, String authType, boolean isServer) throws CertificateException { - - LOGGER.log(Level.FINE, "checkCertTrusted(" + Arrays.toString(chain) + ", " + authType + ", " + isServer + ")"); - try { - LOGGER.log(Level.FINE, "checkCertTrusted: trying appTrustManager"); + if(isServer) { appTrustManager.checkServerTrusted(chain, authType); } @@ -393,22 +353,15 @@ public class MemorizingTrustManager implements X509TrustManager { } } catch(CertificateException ae) { - LOGGER.log(Level.FINER, "checkCertTrusted: appTrustManager did not verify certificate. Will fall back to secondary verification mechanisms (if any).", ae); // if the cert is stored in our appTrustManager, we ignore expiredness - if(isExpiredException(ae)) { - LOGGER.log(Level.INFO, "checkCertTrusted: accepting expired certificate from keystore"); - return; - } - if(isCertKnown(chain[0])) { - LOGGER.log(Level.INFO, "checkCertTrusted: accepting cert already stored in keystore"); + if(isExpiredException(ae) || isCertKnown(chain[0])) { return; } + try { if(defaultTrustManager == null) { - LOGGER.fine("No defaultTrustManager set. Verification failed, throwing " + ae); throw ae; } - LOGGER.log(Level.FINE, "checkCertTrusted: trying defaultTrustManager"); if(isServer) { defaultTrustManager.checkServerTrusted(chain, authType); } @@ -417,7 +370,6 @@ public class MemorizingTrustManager implements X509TrustManager { } } catch(CertificateException e) { - LOGGER.log(Level.FINER, "checkCertTrusted: defaultTrustManager failed", e); interactCert(chain, authType, e); } } @@ -452,12 +404,14 @@ public class MemorizingTrustManager implements X509TrustManager { private static String hexString(byte[] data) { StringBuilder si = new StringBuilder(); + for(int i = 0; i < data.length; i++) { si.append(String.format("%02x", data[i])); if(i < data.length - 1) { si.append(":"); } } + return si.toString(); } @@ -473,91 +427,97 @@ public class MemorizingTrustManager implements X509TrustManager { } } - private static void certDetails(StringBuilder si, X509Certificate c) { + private static void certDetails(StringBuilder stringBuilder, X509Certificate c) { SimpleDateFormat validityDateFormater = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - si.append("\n"); - si.append(c.getSubjectDN().toString()); - si.append("\n"); - si.append(validityDateFormater.format(c.getNotBefore())); - si.append(" - "); - si.append(validityDateFormater.format(c.getNotAfter())); - si.append("\nSHA-256: "); - si.append(certHash(c, "SHA-256")); - si.append("\nSHA-1: "); - si.append(certHash(c, "SHA-1")); - si.append("\nSigned by: "); - si.append(c.getIssuerDN().toString()); - si.append("\n"); + + stringBuilder.append("\n") + .append(c.getSubjectDN().toString()) + .append("\n") + .append(validityDateFormater.format(c.getNotBefore())) + .append(" - ") + .append(validityDateFormater.format(c.getNotAfter())) + .append("\nSHA-256: ") + .append(certHash(c, "SHA-256")) + .append("\nSHA-1: ") + .append(certHash(c, "SHA-1")) + .append("\nSigned by: ") + .append(c.getIssuerDN().toString()) + .append("\n"); } private String certChainMessage(final X509Certificate[] chain, CertificateException cause) { Throwable e = cause; - StringBuilder si = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); if(isPathException(e)) { - si.append(context.getString(R.string.mtm_trust_anchor)); + stringBuilder.append(context.getString(R.string.mtm_trust_anchor)); } else if(isExpiredException(e)) { - si.append(context.getString(R.string.mtm_cert_expired)); + stringBuilder.append(context.getString(R.string.mtm_cert_expired)); } else { // get to the cause - while(e.getCause() != null) + while(e.getCause() != null) { e = e.getCause(); - si.append(e.getLocalizedMessage()); + } + + stringBuilder.append(e.getLocalizedMessage()); } - si.append("\n\n"); - si.append(context.getString(R.string.mtm_connect_anyway)); - si.append("\n\n"); - si.append(context.getString(R.string.mtm_cert_details)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_connect_anyway)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_cert_details)); + for(X509Certificate c : chain) { - certDetails(si, c); + certDetails(stringBuilder, c); } - return si.toString(); + + return stringBuilder.toString(); } private String hostNameMessage(X509Certificate cert, String hostname) { - StringBuilder si = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); - si.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); - si.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_hostname_mismatch, hostname)); + stringBuilder.append("\n\n"); try { Collection> sans = cert.getSubjectAlternativeNames(); + if(sans == null) { - si.append(cert.getSubjectDN()); - si.append("\n"); + stringBuilder.append(cert.getSubjectDN()); + stringBuilder.append("\n"); } else { for(List altName : sans) { Object name = altName.get(1); if(name instanceof String) { - si.append("["); - si.append(altName.get(0)); - si.append("] "); - si.append(name); - si.append("\n"); + stringBuilder.append("["); + stringBuilder.append(altName.get(0)); + stringBuilder.append("] "); + stringBuilder.append(name); + stringBuilder.append("\n"); } } } } catch(CertificateParsingException e) { e.printStackTrace(); - si.append("\n"); + stringBuilder.append("\n"); } - si.append("\n"); - si.append(context.getString(R.string.mtm_connect_anyway)); - si.append("\n\n"); - si.append(context.getString(R.string.mtm_cert_details)); - certDetails(si, cert); - return si.toString(); + stringBuilder.append("\n"); + stringBuilder.append(context.getString(R.string.mtm_connect_anyway)); + stringBuilder.append("\n\n"); + stringBuilder.append(context.getString(R.string.mtm_cert_details)); + certDetails(stringBuilder, cert); + return stringBuilder.toString(); } /** @@ -584,29 +544,24 @@ public class MemorizingTrustManager implements X509TrustManager { } } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void startActivityNotification(Intent intent, int decisionId, String certName) { final PendingIntent call = PendingIntent.getActivity(context, 0, intent, 0); final String mtmNotification = context.getString(R.string.mtm_notification); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl").setSmallIcon(android.R.drawable.ic_lock_lock).setContentTitle(mtmNotification).setContentText(certName).setTicker(certName).setContentIntent(call).setAutoCancel(true).setPriority(NotificationCompat.PRIORITY_HIGH); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ssl") + .setSmallIcon(android.R.drawable.ic_lock_lock) + .setContentTitle(mtmNotification) + .setContentText(certName) + .setTicker(certName) + .setContentIntent(call) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_HIGH); notificationManager.notify(NOTIFICATION_ID + decisionId, builder.build()); } - /** - * Returns the top-most entry of the activity stack. - * - * @return the Context of the currently bound UI or the master context if none is bound - */ - Context getUI() { - - return (foregroundAct != null) ? foregroundAct : context; - } - private int interact(final String message, final int titleId) { - /* prepare the MTMDecision blocker object */ MTMDecision choice = new MTMDecision(); final int myId = createDecisionId(choice); @@ -615,19 +570,14 @@ public class MemorizingTrustManager implements X509TrustManager { public void run() { Intent intent = new Intent(context, MemorizingActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setData(Uri.parse(MemorizingTrustManager.class.getName() + "/" + myId)); - intent.putExtra(DECISION_INTENT_ID, myId); - intent.putExtra(DECISION_INTENT_CERT, message); - intent.putExtra(DECISION_TITLE_ID, titleId); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.putExtra("DECISION_INTENT_ID", myId); + intent.putExtra("DECISION_INTENT_CERT", message); + intent.putExtra("DECISION_TITLE_ID", titleId); - // we try to directly start the activity and fall back to - // making a notification. If no foreground activity is set - // (foregroundAct==null) or if the app developer set an - // invalid / expired activity, the catch-all fallback is - // deployed. try { - foregroundAct.startActivity(intent); + context.startActivity(intent); } catch(Exception e) { startActivityNotification(intent, myId, message); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ed2ddd7..e95e0430 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,7 +92,7 @@ 1- Choose the correct protocol(https or http). \n2- Enter Gitea url e.g: try.gitea.io. \n3- If you have enabled 2FA for your account, enter the code in the OTP Code field. \n4- For HTTP basic auth use USERNAME@DOMAIN.COM in the URL field. Wrong username/password :// - Could'nt connect to host. Please check your URL or port for any errors. + Could\'nt connect to host. Please check your URL or port for any errors. It is not recommended to use HTTP protocol unless you are testing on local network. Malformed JSON was received. Server response was not successful. Instance URL is required From 0dda996444b2d2c2ff965a43250aa7964ea2014d Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Wed, 1 Apr 2020 13:26:32 +0200 Subject: [PATCH 14/24] Adding Picasso support for self-signed certificates. --- app/build.gradle | 2 +- .../activities/IssueDetailActivity.java | 5 +- .../mian/gitnex/activities/MainActivity.java | 5 +- .../gitnex/adapters/AdminGetUsersAdapter.java | 3 +- .../gitnex/adapters/ClosedIssuesAdapter.java | 5 +- .../gitnex/adapters/CollaboratorsAdapter.java | 3 +- .../adapters/ExploreRepositoriesAdapter.java | 3 +- .../gitnex/adapters/IssueCommentsAdapter.java | 5 +- .../mian/gitnex/adapters/IssuesAdapter.java | 5 +- .../gitnex/adapters/MembersByOrgAdapter.java | 3 +- .../gitnex/adapters/MyReposListAdapter.java | 3 +- .../adapters/OrganizationsListAdapter.java | 3 +- .../adapters/ProfileFollowersAdapter.java | 3 +- .../adapters/ProfileFollowingAdapter.java | 3 +- .../gitnex/adapters/PullRequestsAdapter.java | 5 +- .../adapters/RepoStargazersAdapter.java | 3 +- .../gitnex/adapters/RepoWatchersAdapter.java | 3 +- .../gitnex/adapters/ReposListAdapter.java | 3 +- .../adapters/RepositoriesByOrgAdapter.java | 3 +- .../adapters/StarredReposListAdapter.java | 3 +- .../adapters/TeamMembersByOrgAdapter.java | 3 +- .../gitnex/adapters/UserSearchAdapter.java | 3 +- .../mian/gitnex/clients/IssuesService.java | 6 +- .../mian/gitnex/clients/PicassoService.java | 65 +++++++++++++++++++ .../gitnex/clients/PullRequestsService.java | 6 +- .../mian/gitnex/clients/RetrofitClient.java | 7 +- .../fragments/OrganizationInfoFragment.java | 3 +- .../gitnex/fragments/ProfileFragment.java | 3 +- 28 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/clients/PicassoService.java diff --git a/app/build.gradle b/app/build.gradle index 63d1808b..331206de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,5 +84,5 @@ dependencies { implementation "com.mikepenz:fastadapter:$fastadapter" implementation "com.mikepenz:fastadapter-commons:$fastadapter" implementation "com.mikepenz:fastadapter-extensions:$fastadapter" - implementation 'org.conscrypt:conscrypt-android:2.2.1' + implementation "org.conscrypt:conscrypt-android:2.2.1" } diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index 5ed1f303..cc8fd2a5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -54,6 +54,7 @@ import com.squareup.picasso.Picasso; import com.vdurmont.emoji.EmojiParser; import org.mian.gitnex.R; import org.mian.gitnex.adapters.IssueCommentsAdapter; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.fragments.BottomSheetSingleIssueFragment; import org.mian.gitnex.helpers.AlertDialogs; @@ -388,7 +389,7 @@ public class IssueDetailActivity extends BaseActivity { tinyDb.putString("issueState", singleIssue.getState()); tinyDb.putString("issueTitle", singleIssue.getTitle()); - Picasso.get().load(singleIssue.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar); + PicassoService.getInstance(ctx).get().load(singleIssue.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar); String issueNumber_ = "" + getApplicationContext().getResources().getString(R.string.hash) + singleIssue.getNumber() + ""; issueTitle.setText(Html.fromHtml(issueNumber_ + " " + singleIssue.getTitle())); String cleanIssueDescription = singleIssue.getBody().trim(); @@ -406,7 +407,7 @@ public class IssueDetailActivity extends BaseActivity { ImageView assigneesView = new ImageView(getApplicationContext()); - Picasso.get().load(singleIssue.getAssignees().get(i).getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(100, 100).centerCrop().into(assigneesView); + PicassoService.getInstance(ctx).get().load(singleIssue.getAssignees().get(i).getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(100, 100).centerCrop().into(assigneesView); assigneesLayout.addView(assigneesView); assigneesView.setLayoutParams(params1); diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 51a2e540..bf8530d1 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -24,6 +24,7 @@ import android.widget.TextView; import com.squareup.picasso.NetworkPolicy; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.fragments.AboutFragment; import org.mian.gitnex.fragments.ExploreRepositoriesFragment; @@ -207,7 +208,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig userAvatar = hView.findViewById(R.id.userAvatar); if (!userAvatarNav.equals("")) { - Picasso.get().load(userAvatarNav).networkPolicy(NetworkPolicy.OFFLINE).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(160, 160).centerCrop().into(userAvatar); + PicassoService.getInstance(ctx).get().load(userAvatarNav).networkPolicy(NetworkPolicy.OFFLINE).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(160, 160).centerCrop().into(userAvatar); } userAvatar.setOnClickListener(new View.OnClickListener() { @@ -488,7 +489,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig userAvatar = hView.findViewById(R.id.userAvatar); if (!Objects.requireNonNull(userDetails).getAvatar().equals("")) { - Picasso.get().load(userDetails.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(160, 160).centerCrop().into(userAvatar); + PicassoService.getInstance(ctx).get().load(userDetails.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(160, 160).centerCrop().into(userAvatar); } else { userAvatar.setImageResource(R.mipmap.app_logo_round); } diff --git a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java index 18ef0171..d4f3af44 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.amulyakhare.textdrawable.TextDrawable; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserInfo; import java.util.ArrayList; @@ -98,7 +99,7 @@ public class AdminGetUsersAdapter extends RecyclerView.Adapter" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + ""; diff --git a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java index ce6ab11f..f83186f3 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java @@ -10,6 +10,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.models.Collaborators; import org.mian.gitnex.helpers.RoundedTransformation; import java.util.List; @@ -77,7 +78,7 @@ public class CollaboratorsAdapter extends BaseAdapter { private void initData(ViewHolder viewHolder, int position) { Collaborators currentItem = collaboratorsList.get(position); - Picasso.get().load(currentItem.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.collaboratorAvatar); + PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.collaboratorAvatar); if(!currentItem.getFull_name().equals("")) { viewHolder.collaboratorName.setText(currentItem.getFull_name()); diff --git a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java index e179e07b..20bdb1f8 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java @@ -20,6 +20,7 @@ import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserRepositories; import org.mian.gitnex.util.TinyDB; @@ -182,7 +183,7 @@ public class ExploreRepositoriesAdapter extends RecyclerView.Adapter } if (issuesModel.getUser().getAvatar_url() != null) { - Picasso.get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); + PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); } else { - Picasso.get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); + PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); } String issueNumber_ = "" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + ""; diff --git a/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java index 345bba32..3ecc3f94 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserInfo; import java.util.ArrayList; @@ -83,7 +84,7 @@ public class MembersByOrgAdapter extends BaseAdapter implements Filterable { private void initData(MembersByOrgAdapter.ViewHolder viewHolder, int position) { UserInfo currentItem = membersList.get(position); - Picasso.get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(viewHolder.memberAvatar); + PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(viewHolder.memberAvatar); if(!currentItem.getFullname().equals("")) { viewHolder.memberName.setText(currentItem.getFullname()); diff --git a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java index 6f2d7833..4dd33204 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java @@ -19,6 +19,7 @@ import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserRepositories; import org.mian.gitnex.util.TinyDB; @@ -183,7 +184,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter" + context.getResources().getString(R.string.hash) + prModel.getNumber() + ""; diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java index 9166e933..dbfefd74 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java @@ -11,6 +11,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserInfo; import org.mian.gitnex.util.TinyDB; @@ -79,7 +80,7 @@ public class RepoStargazersAdapter extends BaseAdapter { private void initData(RepoStargazersAdapter.ViewHolder viewHolder, int position) { UserInfo currentItem = stargazersList.get(position); - Picasso.get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.memberAvatar); + PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.memberAvatar); final TinyDB tinyDb = new TinyDB(mCtx); Typeface myTypeface; diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java index b039399f..bf98ef2e 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java @@ -11,6 +11,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserInfo; import org.mian.gitnex.util.TinyDB; @@ -79,7 +80,7 @@ public class RepoWatchersAdapter extends BaseAdapter { private void initData(RepoWatchersAdapter.ViewHolder viewHolder, int position) { UserInfo currentItem = watchersList.get(position); - Picasso.get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.memberAvatar); + PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(viewHolder.memberAvatar); final TinyDB tinyDb = new TinyDB(mCtx); Typeface myTypeface; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java index 4eb33545..7dece23b 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java @@ -22,6 +22,7 @@ import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.models.UserRepositories; import org.mian.gitnex.util.TinyDB; @@ -187,7 +188,7 @@ public class ReposListAdapter extends RecyclerView.Adapter 0) { diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index b132aff0..5e8d5215 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -42,7 +42,11 @@ public class IssuesService { MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .cache(cache) + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) + .addInterceptor(new Interceptor() { @NonNull @Override diff --git a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java new file mode 100644 index 00000000..c4333327 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java @@ -0,0 +1,65 @@ +package org.mian.gitnex.clients; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; +import com.squareup.picasso.OkHttp3Downloader; +import com.squareup.picasso.Picasso; +import org.mian.gitnex.helpers.MemorizingTrustManager; +import java.security.SecureRandom; +import java.util.Objects; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; +import okhttp3.OkHttpClient; + +public class PicassoService { + + private static PicassoService picassoService; + private Picasso picasso; + + private PicassoService(Context context) { + + Picasso.Builder builder = new Picasso.Builder(context); + + try { + + SSLContext sslContext = SSLContext.getInstance("TLS"); + + MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); + sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); + + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())); + + builder.downloader(new OkHttp3Downloader(okHttpClient.build())); + builder.listener((picasso, uri, exception) -> { + + Log.e("PicassoService", Objects.requireNonNull(uri.toString())); // important!! + Log.e("PicassoService", exception.toString()); + }); + + picasso = builder.build(); + } + catch(Exception e) { + + e.printStackTrace(); + } + } + + public Picasso get() { + + return picasso; + } + + public static synchronized PicassoService getInstance(Context context) { + + if(picassoService == null) { + picassoService = new PicassoService(context); + } + + return picassoService; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index 23364211..5d2493cf 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -42,7 +42,11 @@ public class PullRequestsService { MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(new Interceptor() { + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .cache(cache) + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) + .addInterceptor(new Interceptor() { @NonNull @Override diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index 85517923..dcc15c02 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -42,9 +42,12 @@ public class RetrofitClient { MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); sslContext.init(null, new X509TrustManager[]{memorizingTrustManager}, new SecureRandom()); - OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder().cache(cache) + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() + .cache(cache) //.addInterceptor(logging) - .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager).hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())).addInterceptor(chain -> { + .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) + .addInterceptor(chain -> { Request request = chain.request(); if(connToInternet) { diff --git a/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java b/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java index bb0b9758..3d6820b6 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java @@ -16,6 +16,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.RoundedTransformation; @@ -104,7 +105,7 @@ public class OrganizationInfoFragment extends Fragment { if (response.code() == 200) { assert orgInfo != null; - Picasso.get().load(orgInfo.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(orgAvatar); + PicassoService.getInstance(ctx).get().load(orgInfo.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(orgAvatar); orgDescInfo.setText(orgInfo.getDescription()); orgWebsiteInfo.setText(orgInfo.getWebsite()); orgLocationInfo.setText(orgInfo.getLocation()); diff --git a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java index 43dda832..e2ea5bd0 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java @@ -21,6 +21,7 @@ import com.google.android.material.tabs.TabLayout; import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.MainActivity; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.util.TinyDB; import java.util.Objects; @@ -48,7 +49,7 @@ public class ProfileFragment extends Fragment { TextView userEmail = v.findViewById(R.id.userEmail); userFullName.setText(tinyDb.getString("userFullname")); - Picasso.get().load(tinyDb.getString("userAvatar")).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(userAvatar); + PicassoService.getInstance(ctx).get().load(tinyDb.getString("userAvatar")).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(userAvatar); userLogin.setText(getString(R.string.usernameWithAt, tinyDb.getString("userLogin"))); userEmail.setText(tinyDb.getString("userEmail")); From 9f6d8239f6184b42aa6d44e34d4f4e72f3ceae52 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Wed, 1 Apr 2020 14:38:34 +0200 Subject: [PATCH 15/24] Adding author --- .../main/java/org/mian/gitnex/clients/PicassoService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java index c4333327..7072ca77 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java @@ -1,7 +1,6 @@ package org.mian.gitnex.clients; import android.content.Context; -import android.net.Uri; import android.util.Log; import com.squareup.picasso.OkHttp3Downloader; import com.squareup.picasso.Picasso; @@ -13,6 +12,10 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; import okhttp3.OkHttpClient; +/** + * Author anonTree1417 + */ + public class PicassoService { private static PicassoService picassoService; From d2f77cfcd02e5f6fb52cde0b3b7130420ef2d441 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Wed, 1 Apr 2020 20:28:14 +0500 Subject: [PATCH 16/24] Fix minor formattings, other improvements --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../activities/IssueDetailActivity.java | 1 - .../mian/gitnex/activities/MainActivity.java | 1 - .../gitnex/adapters/AdminGetUsersAdapter.java | 1 - .../gitnex/adapters/ClosedIssuesAdapter.java | 1 - .../gitnex/adapters/CollaboratorsAdapter.java | 1 - .../adapters/ExploreRepositoriesAdapter.java | 1 - .../gitnex/adapters/IssueCommentsAdapter.java | 1 - .../mian/gitnex/adapters/IssuesAdapter.java | 1 - .../gitnex/adapters/MembersByOrgAdapter.java | 1 - .../gitnex/adapters/MyReposListAdapter.java | 1 - .../adapters/OrganizationsListAdapter.java | 1 - .../adapters/ProfileFollowersAdapter.java | 1 - .../adapters/ProfileFollowingAdapter.java | 1 - .../gitnex/adapters/PullRequestsAdapter.java | 1 - .../adapters/RepoStargazersAdapter.java | 1 - .../gitnex/adapters/RepoWatchersAdapter.java | 1 - .../gitnex/adapters/ReposListAdapter.java | 1 - .../adapters/RepositoriesByOrgAdapter.java | 1 - .../adapters/StarredReposListAdapter.java | 1 - .../adapters/TeamMembersByOrgAdapter.java | 1 - .../gitnex/adapters/UserSearchAdapter.java | 1 - .../mian/gitnex/clients/IssuesService.java | 12 ++++++++--- .../mian/gitnex/clients/PicassoService.java | 7 +++++-- .../gitnex/clients/PullRequestsService.java | 11 +++++++--- .../mian/gitnex/clients/RetrofitClient.java | 14 ++++++++----- .../fragments/OrganizationInfoFragment.java | 1 - .../gitnex/fragments/ProfileFragment.java | 1 - .../gitnex/helpers/{ => ssl}/MTMDecision.java | 4 +++- .../helpers/{ => ssl}/MemorizingActivity.java | 4 +++- .../{ => ssl}/MemorizingTrustManager.java | 21 ++++++++----------- app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values/strings.xml | 4 ++-- 34 files changed, 50 insertions(+), 55 deletions(-) rename app/src/main/java/org/mian/gitnex/helpers/{ => ssl}/MTMDecision.java (87%) rename app/src/main/java/org/mian/gitnex/helpers/{ => ssl}/MemorizingActivity.java (97%) rename app/src/main/java/org/mian/gitnex/helpers/{ => ssl}/MemorizingTrustManager.java (97%) diff --git a/app/build.gradle b/app/build.gradle index 331206de..7e5e0ae5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,5 +84,5 @@ dependencies { implementation "com.mikepenz:fastadapter:$fastadapter" implementation "com.mikepenz:fastadapter-commons:$fastadapter" implementation "com.mikepenz:fastadapter-extensions:$fastadapter" - implementation "org.conscrypt:conscrypt-android:2.2.1" + //implementation "org.conscrypt:conscrypt-android:2.2.1" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4db4c839..36a1c064 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -74,7 +74,7 @@ - + \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index cc8fd2a5..2bada8f0 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -50,7 +50,6 @@ import android.widget.RelativeLayout; import android.widget.ScrollView; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; -import com.squareup.picasso.Picasso; import com.vdurmont.emoji.EmojiParser; import org.mian.gitnex.R; import org.mian.gitnex.adapters.IssueCommentsAdapter; diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index bf8530d1..3347c715 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -22,7 +22,6 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.NetworkPolicy; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; diff --git a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java index d4f3af44..bde801c6 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java @@ -11,7 +11,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.amulyakhare.textdrawable.TextDrawable; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java index fedbaf79..98d8aadd 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.IssueDetailActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java index f83186f3..29111d51 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java @@ -8,7 +8,6 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.models.Collaborators; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java index 20bdb1f8..5720a3dc 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java @@ -14,7 +14,6 @@ import androidx.appcompat.widget.PopupMenu; import androidx.recyclerview.widget.RecyclerView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java index ee4570b2..173b7eb7 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java @@ -12,7 +12,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import com.vdurmont.emoji.EmojiParser; import org.mian.gitnex.R; import org.mian.gitnex.activities.ReplyToIssueActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java index 9a05b82d..1bf481a9 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.IssueDetailActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java index 3ecc3f94..5428f4aa 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java @@ -10,7 +10,6 @@ import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java index 4dd33204..075b47ff 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java index b3deb57d..899bcb65 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java @@ -12,7 +12,6 @@ import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OrganizationDetailActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowersAdapter.java index 0b2c63be..cf20b379 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowersAdapter.java @@ -6,7 +6,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowingAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowingAdapter.java index e6ecbcc2..ff96c2ff 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowingAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ProfileFollowingAdapter.java @@ -6,7 +6,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/PullRequestsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/PullRequestsAdapter.java index 0ee7b421..29c2bcf0 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/PullRequestsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/PullRequestsAdapter.java @@ -14,7 +14,6 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.IssueDetailActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java index dbfefd74..9b4d6e72 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoStargazersAdapter.java @@ -9,7 +9,6 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java index bf98ef2e..0e46e049 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoWatchersAdapter.java @@ -9,7 +9,6 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java index 7dece23b..00f2bccc 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java @@ -16,7 +16,6 @@ import android.widget.ImageView; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepositoriesByOrgAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepositoriesByOrgAdapter.java index 518fd7c9..6520fc51 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepositoriesByOrgAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepositoriesByOrgAdapter.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java index 9e0e689b..651e29a0 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java @@ -13,7 +13,6 @@ import android.widget.ImageView; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.OpenRepoInBrowserActivity; import org.mian.gitnex.activities.RepoDetailActivity; diff --git a/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgAdapter.java index d6139cb1..7e356bff 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgAdapter.java @@ -9,7 +9,6 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.RoundedTransformation; diff --git a/app/src/main/java/org/mian/gitnex/adapters/UserSearchAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/UserSearchAdapter.java index 6be8b38d..daa7bf08 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/UserSearchAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/UserSearchAdapter.java @@ -9,7 +9,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.actions.CollaboratorActions; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java index 5e8d5215..98ccf570 100644 --- a/app/src/main/java/org/mian/gitnex/clients/IssuesService.java +++ b/app/src/main/java/org/mian/gitnex/clients/IssuesService.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; -import org.mian.gitnex.helpers.MemorizingTrustManager; +import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; @@ -36,7 +36,8 @@ public class IssuesService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { // try-catch can be problematic here + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); @@ -44,6 +45,7 @@ public class IssuesService { OkHttpClient okHttpClient = new OkHttpClient.Builder() .cache(cache) + //.addInterceptor(logging) .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) .addInterceptor(new Interceptor() { @@ -63,10 +65,14 @@ public class IssuesService { } }).build(); - Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder() + .baseUrl(instanceURL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()); Retrofit retrofit = builder.build(); return retrofit.create(serviceClass); + } catch(Exception e) { Log.e("onFailure", e.toString()); diff --git a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java index 7072ca77..aceceb3f 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java @@ -4,7 +4,7 @@ import android.content.Context; import android.util.Log; import com.squareup.picasso.OkHttp3Downloader; import com.squareup.picasso.Picasso; -import org.mian.gitnex.helpers.MemorizingTrustManager; +import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import java.security.SecureRandom; import java.util.Objects; import javax.net.ssl.HttpsURLConnection; @@ -41,14 +41,17 @@ public class PicassoService { Log.e("PicassoService", Objects.requireNonNull(uri.toString())); // important!! Log.e("PicassoService", exception.toString()); + }); picasso = builder.build(); + } catch(Exception e) { - e.printStackTrace(); + Log.e("PicassoService", e.toString()); } + } public Picasso get() { diff --git a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java index 5d2493cf..b6bbd632 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PullRequestsService.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; -import org.mian.gitnex.helpers.MemorizingTrustManager; +import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.io.IOException; @@ -36,7 +36,8 @@ public class PullRequestsService { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { // try-catch can be problematic here + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); @@ -44,6 +45,7 @@ public class PullRequestsService { OkHttpClient okHttpClient = new OkHttpClient.Builder() .cache(cache) + //.addInterceptor(logging) .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) .addInterceptor(new Interceptor() { @@ -63,7 +65,10 @@ public class PullRequestsService { } }).build(); - Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceURL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder() + .baseUrl(instanceURL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()); Retrofit retrofit = builder.build(); return retrofit.create(serviceClass); diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index dcc15c02..0150fb4f 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -3,7 +3,7 @@ package org.mian.gitnex.clients; import android.content.Context; import android.util.Log; import org.mian.gitnex.interfaces.ApiInterface; -import org.mian.gitnex.helpers.MemorizingTrustManager; +import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import org.mian.gitnex.util.AppUtil; import java.io.File; import java.security.SecureRandom; @@ -36,7 +36,8 @@ public class RetrofitClient { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); - try { // try-catch can be problematic here + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(ctx); @@ -59,7 +60,11 @@ public class RetrofitClient { return chain.proceed(request); }); - Retrofit.Builder builder = new Retrofit.Builder().baseUrl(instanceUrl).client(okHttpClient.build()).addConverterFactory(ScalarsConverterFactory.create()).addConverterFactory(GsonConverterFactory.create()); + Retrofit.Builder builder = new Retrofit.Builder() + .baseUrl(instanceUrl) + .client(okHttpClient.build()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()); retrofit = builder.build(); @@ -67,15 +72,14 @@ public class RetrofitClient { catch(Exception e) { Log.e("onFailure", e.toString()); } + } public static synchronized RetrofitClient getInstance(String instanceUrl, Context ctx) { - return new RetrofitClient(instanceUrl, ctx); } public ApiInterface getApiInterface() { - return retrofit.create(ApiInterface.class); } diff --git a/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java b/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java index 3d6820b6..71cda317 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java @@ -14,7 +14,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; diff --git a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java index e2ea5bd0..f8907a8e 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java @@ -18,7 +18,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.google.android.material.tabs.TabLayout; -import com.squareup.picasso.Picasso; import org.mian.gitnex.R; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.clients.PicassoService; diff --git a/app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java similarity index 87% rename from app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java rename to app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java index 86280426..5fc77e06 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MTMDecision.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java @@ -1,14 +1,16 @@ -package org.mian.gitnex.helpers; +package org.mian.gitnex.helpers.ssl; /** * Author Georg Lukas, modified by anonTree1417 */ class MTMDecision { + final static int DECISION_INVALID = 0; final static int DECISION_ABORT = 1; final static int DECISION_ONCE = 2; final static int DECISION_ALWAYS = 3; int state = DECISION_INVALID; + } diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java similarity index 97% rename from app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java rename to app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java index ee20979e..0003e7fc 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java @@ -1,4 +1,4 @@ -package org.mian.gitnex.helpers; +package org.mian.gitnex.helpers.ssl; import android.app.Activity; import android.app.AlertDialog; @@ -32,12 +32,14 @@ public class MemorizingActivity extends Activity { builder.setOnCancelListener(dialog -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); builder.create().show(); + } private void onSendResult(int decisionId, int decision) { MemorizingTrustManager.interactResult(decisionId, decision); finish(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java similarity index 97% rename from app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java rename to app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java index ece64672..292b5379 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java @@ -1,19 +1,14 @@ -package org.mian.gitnex.helpers; +package org.mian.gitnex.helpers.ssl; -import android.annotation.TargetApi; -import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.net.Uri; +import android.os.Handler; import android.util.Base64; import android.util.SparseArray; -import android.os.Build; -import android.os.Handler; - import androidx.core.app.NotificationCompat; import org.mian.gitnex.R; import java.io.ByteArrayInputStream; @@ -21,20 +16,22 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.security.cert.*; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Locale; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1d24ad5e..5c9368d8 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -63,7 +63,6 @@ In Gitea anmelden 1- Wähle das richtige Protokoll (https oder http) \ N2- Gebe die Gitea-URL ein, z.B. try.gitea.io \n3- Falls du 2FA aktiviert hast, gib den Code in das OTP-Code Feld ein. \n4- Für HTTP basic auth nutze BENUTZERNAME@DOMAIN.COM im URL Feld Falscher Benutzername/Passwort - Verbindung zum Host fehlgeschlagen. Bitte prüfen Sie Ihre URL und den Port auf etwaige Fehler. Es wird nicht empfohlen, das HTTP-Protokoll zu verwenden, es sei denn, du testest in einem lokalen Netzwerk. Fehlerhaftes JSON erhalten. Serveranfrage war nicht erfolgreich. Instanzen-URL ist erforderlich diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e95e0430..c70314e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,7 +92,7 @@ 1- Choose the correct protocol(https or http). \n2- Enter Gitea url e.g: try.gitea.io. \n3- If you have enabled 2FA for your account, enter the code in the OTP Code field. \n4- For HTTP basic auth use USERNAME@DOMAIN.COM in the URL field. Wrong username/password :// - Could\'nt connect to host. Please check your URL or port for any errors. + Couldn\'t connect to host. Please check your URL or port for any errors. It is not recommended to use HTTP protocol unless you are testing on local network. Malformed JSON was received. Server response was not successful. Instance URL is required @@ -580,7 +580,7 @@ Server could not authenticate as \"%s\". The certificate is only valid for: Do you want to connect anyway? Certificate details: - Always + Trust Once Abort From 4217db3e31e0d770da6509dd5d7369454036bfbb Mon Sep 17 00:00:00 2001 From: M M Arif Date: Wed, 1 Apr 2020 21:16:59 +0500 Subject: [PATCH 17/24] Add author to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 17c374c0..7b3c1db0 100644 --- a/README.md +++ b/README.md @@ -92,5 +92,6 @@ Open source libraries - Droidsonroids.gif/android-gif-drawable - Barteksc/AndroidPdfViewer - Mikepenz/fastadapter +- Ge0rg/MemorizingTrustManager [Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif) From faaa4db88b03f6c1d74f83600b51411025056362 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Wed, 1 Apr 2020 18:39:39 +0200 Subject: [PATCH 18/24] Removing option "ONCE" --- .../mian/gitnex/clients/PicassoService.java | 2 +- .../mian/gitnex/helpers/ssl/MTMDecision.java | 1 - .../helpers/ssl/MemorizingActivity.java | 3 +-- .../helpers/ssl/MemorizingTrustManager.java | 22 +++++++------------ 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java index aceceb3f..f13e4d2f 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java @@ -39,7 +39,7 @@ public class PicassoService { builder.downloader(new OkHttp3Downloader(okHttpClient.build())); builder.listener((picasso, uri, exception) -> { - Log.e("PicassoService", Objects.requireNonNull(uri.toString())); // important!! + //Log.e("PicassoService", Objects.requireNonNull(uri.toString())); Log.e("PicassoService", exception.toString()); }); diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java index 5fc77e06..aab9ce96 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java @@ -8,7 +8,6 @@ class MTMDecision { final static int DECISION_INVALID = 0; final static int DECISION_ABORT = 1; - final static int DECISION_ONCE = 2; final static int DECISION_ALWAYS = 3; int state = DECISION_INVALID; diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java index 0003e7fc..472a9954 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingActivity.java @@ -27,8 +27,7 @@ public class MemorizingActivity extends Activity { builder.setMessage(cert); builder.setPositiveButton(R.string.mtm_decision_always, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ALWAYS)); - builder.setNeutralButton(R.string.mtm_decision_once, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ONCE)); - builder.setNegativeButton(R.string.mtm_decision_abort, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); + builder.setNeutralButton(R.string.mtm_decision_abort, (dialog, which) -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); builder.setOnCancelListener(dialog -> onSendResult(decisionId, MTMDecision.DECISION_ABORT)); builder.create().show(); diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java index 292b5379..49ea6f6e 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java @@ -596,26 +596,20 @@ public class MemorizingTrustManager implements X509TrustManager { private void interactCert(final X509Certificate[] chain, String authType, CertificateException cause) throws CertificateException { - switch(interact(certChainMessage(chain, cause), R.string.mtm_accept_cert)) { - case MTMDecision.DECISION_ALWAYS: - storeCert(chain[0]); // only store the server cert, not the whole chain - case MTMDecision.DECISION_ONCE: - break; - default: - throw (cause); + if(interact(certChainMessage(chain, cause), R.string.mtm_accept_cert) == MTMDecision.DECISION_ALWAYS) { + storeCert(chain[0]); // only store the server cert, not the whole chain } + + throw (cause); } private boolean interactHostname(X509Certificate cert, String hostname) { - switch(interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername)) { - case MTMDecision.DECISION_ALWAYS: - storeCert(hostname, cert); - case MTMDecision.DECISION_ONCE: - return true; - default: - return false; + if(interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername) == MTMDecision.DECISION_ALWAYS) { + storeCert(hostname, cert); } + + return false; } static void interactResult(int decisionId, int choice) { From b2f34f819c4958130b056c1f9535b76175d9e4f3 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Wed, 1 Apr 2020 18:58:52 +0200 Subject: [PATCH 19/24] Removing library and commenting out logger. --- app/build.gradle | 1 - app/src/main/java/org/mian/gitnex/clients/PicassoService.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7e5e0ae5..e2460f4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,5 +84,4 @@ dependencies { implementation "com.mikepenz:fastadapter:$fastadapter" implementation "com.mikepenz:fastadapter-commons:$fastadapter" implementation "com.mikepenz:fastadapter-extensions:$fastadapter" - //implementation "org.conscrypt:conscrypt-android:2.2.1" } diff --git a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java index f13e4d2f..e759d773 100644 --- a/app/src/main/java/org/mian/gitnex/clients/PicassoService.java +++ b/app/src/main/java/org/mian/gitnex/clients/PicassoService.java @@ -40,7 +40,7 @@ public class PicassoService { builder.listener((picasso, uri, exception) -> { //Log.e("PicassoService", Objects.requireNonNull(uri.toString())); - Log.e("PicassoService", exception.toString()); + //Log.e("PicassoService", exception.toString()); }); From ee0392e208ee0ac15fd5d5d05b0da1add05b5afc Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Wed, 1 Apr 2020 21:58:42 +0200 Subject: [PATCH 20/24] Very small improvements --- app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java index aab9ce96..a0264f39 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MTMDecision.java @@ -8,7 +8,7 @@ class MTMDecision { final static int DECISION_INVALID = 0; final static int DECISION_ABORT = 1; - final static int DECISION_ALWAYS = 3; + final static int DECISION_ALWAYS = 2; int state = DECISION_INVALID; From abdfe8d9d1d46f0c5216a941c9061eeee8dd874a Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Thu, 2 Apr 2020 02:59:00 +0200 Subject: [PATCH 21/24] Adding option to delete certificates. --- .../mian/gitnex/activities/MainActivity.java | 13 +++--- .../gitnex/fragments/SettingsFragment.java | 29 +++++++++++++ .../helpers/ssl/MemorizingTrustManager.java | 4 +- app/src/main/res/layout/fragment_settings.xml | 16 ++++++- .../main/res/layout/layout_settings_certs.xml | 42 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 app/src/main/res/layout/layout_settings_certs.xml diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 3347c715..7156d378 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -1,5 +1,6 @@ package org.mian.gitnex.activities; +import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -101,7 +102,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig boolean connToInternet = AppUtil.haveNetworkConnection(getApplicationContext()); if(!tinyDb.getBoolean("loggedInMode")) { - logout(); + logout(this, ctx); return; } @@ -354,7 +355,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig new SettingsFragment()).commit(); break; case R.id.nav_logout: - logout(); + logout(this, ctx); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); break; case R.id.nav_about: @@ -392,15 +393,15 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig } } - public void logout() { + public static void logout(Activity activity, Context ctx) { - TinyDB tinyDb = new TinyDB(getApplicationContext()); + TinyDB tinyDb = new TinyDB(ctx.getApplicationContext()); tinyDb.putBoolean("loggedInMode", false); tinyDb.remove("basicAuthPassword"); tinyDb.putBoolean("basicAuthFlag", false); //tinyDb.clear(); - finish(); - startActivity(new Intent(MainActivity.this, LoginActivity.class)); + activity.finish(); + ctx.startActivity(new Intent(ctx, LoginActivity.class)); } diff --git a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java index 492af7c4..98fc3441 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java @@ -13,7 +13,9 @@ import android.widget.LinearLayout; import android.widget.Switch; import android.widget.TextView; import org.mian.gitnex.R; +import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import org.mian.gitnex.util.TinyDB; import java.util.Objects; import androidx.annotation.NonNull; @@ -67,6 +69,7 @@ public class SettingsFragment extends Fragment { LinearLayout homeScreenFrame = v.findViewById(R.id.homeScreenFrame); LinearLayout customFontFrame = v.findViewById(R.id.customFontFrame); LinearLayout themeFrame = v.findViewById(R.id.themeSelectionFrame); + LinearLayout certsFrame = v.findViewById(R.id.certsFrame); Switch issuesSwitch = v.findViewById(R.id.switchIssuesBadge); Switch pdfModeSwitch = v.findViewById(R.id.switchPdfMode); @@ -144,6 +147,32 @@ public class SettingsFragment extends Fragment { pdfModeSwitch.setChecked(false); } + // certs deletion + certsFrame.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + AlertDialog.Builder builder = new AlertDialog.Builder(ctx); + builder.setTitle("Delete trusted certificates?"); + builder.setMessage("Are you really sure to delete any manually trusted certificate?\n\nNotice: Your will be logged out too."); + builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + ctx.getSharedPreferences(MemorizingTrustManager.KEYSTORE_NAME, Context.MODE_PRIVATE) + .edit() + .remove(MemorizingTrustManager.KEYSTORE_KEY) + .apply(); + + MainActivity.logout(Objects.requireNonNull(getActivity()), ctx); + } + }); + + builder.setNeutralButton("NO", (dialog, which) -> dialog.dismiss()); + builder.create().show(); + } + }); + // issues badge switcher issuesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java index 49ea6f6e..d79f1d6c 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java @@ -45,8 +45,8 @@ import javax.net.ssl.X509TrustManager; public class MemorizingTrustManager implements X509TrustManager { private final static int NOTIFICATION_ID = 100509; - private final static String KEYSTORE_NAME = "keystore"; - private final static String KEYSTORE_KEY = "keystore"; + public final static String KEYSTORE_NAME = "keystore"; + public final static String KEYSTORE_KEY = "keystore"; private Context context; private NotificationManager notificationManager; diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 5f6eb145..09aec96b 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -33,7 +33,7 @@ layout="@layout/layout_settings_fileview"/> + + + + diff --git a/app/src/main/res/layout/layout_settings_certs.xml b/app/src/main/res/layout/layout_settings_certs.xml new file mode 100644 index 00000000..52b4a67b --- /dev/null +++ b/app/src/main/res/layout/layout_settings_certs.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c70314e0..711cf468 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,6 +246,8 @@ Translation + Certs + Delete certificates Date & Time Settings saved Language From b9eee16294fc383403d57b999586736b45cd0297 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Thu, 2 Apr 2020 03:12:44 +0200 Subject: [PATCH 22/24] Adding resources. --- .../main/java/org/mian/gitnex/fragments/SettingsFragment.java | 4 ++-- app/src/main/res/values/strings.xml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java index 98fc3441..6d681ea5 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java @@ -153,8 +153,8 @@ public class SettingsFragment extends Fragment { @Override public void onClick(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(ctx); - builder.setTitle("Delete trusted certificates?"); - builder.setMessage("Are you really sure to delete any manually trusted certificate?\n\nNotice: Your will be logged out too."); + builder.setTitle(getResources().getString(R.string.settingsCertsPopupTitle)); + builder.setMessage(getResources().getString(R.string.settingsCertsPopupMessage)); builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 711cf468..c4ef3a1b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,8 +246,10 @@ Translation - Certs + Certificates Delete certificates + Delete trusted certificates? + Are you sure to delete any manually trusted certificate or hostname? You will also be logged out. Date & Time Settings saved Language From 70581cd330ad6630648bde1c8806cb2b2873d394 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Thu, 2 Apr 2020 20:49:26 +0500 Subject: [PATCH 23/24] Updates to settings screen --- .../gitnex/fragments/SettingsFragment.java | 34 ++++++++----------- .../main/res/drawable/ic_security_24dp.xml | 5 +++ app/src/main/res/layout/fragment_settings.xml | 8 ++--- ...certs.xml => layout_settings_security.xml} | 8 ++--- app/src/main/res/values/strings.xml | 8 ++--- 5 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 app/src/main/res/drawable/ic_security_24dp.xml rename app/src/main/res/layout/{layout_settings_certs.xml => layout_settings_security.xml} (86%) diff --git a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java index 6d681ea5..0c87a21f 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java @@ -148,29 +148,25 @@ public class SettingsFragment extends Fragment { } // certs deletion - certsFrame.setOnClickListener(new View.OnClickListener() { + certsFrame.setOnClickListener(v1 -> { - @Override - public void onClick(View v) { - AlertDialog.Builder builder = new AlertDialog.Builder(ctx); - builder.setTitle(getResources().getString(R.string.settingsCertsPopupTitle)); - builder.setMessage(getResources().getString(R.string.settingsCertsPopupMessage)); - builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { + AlertDialog.Builder builder = new AlertDialog.Builder(ctx); + builder.setTitle(getResources().getString(R.string.settingsCertsPopupTitle)); + builder.setMessage(getResources().getString(R.string.settingsCertsPopupMessage)); + builder.setPositiveButton(R.string.menuDeleteText, (dialog, which) -> { - @Override - public void onClick(DialogInterface dialog, int which) { - ctx.getSharedPreferences(MemorizingTrustManager.KEYSTORE_NAME, Context.MODE_PRIVATE) - .edit() - .remove(MemorizingTrustManager.KEYSTORE_KEY) - .apply(); + ctx.getSharedPreferences(MemorizingTrustManager.KEYSTORE_NAME, Context.MODE_PRIVATE) + .edit() + .remove(MemorizingTrustManager.KEYSTORE_KEY) + .apply(); - MainActivity.logout(Objects.requireNonNull(getActivity()), ctx); - } - }); + MainActivity.logout(Objects.requireNonNull(getActivity()), ctx); + + }); + + builder.setNeutralButton(R.string.cancelButton, (dialog, which) -> dialog.dismiss()); + builder.create().show(); - builder.setNeutralButton("NO", (dialog, which) -> dialog.dismiss()); - builder.create().show(); - } }); // issues badge switcher diff --git a/app/src/main/res/drawable/ic_security_24dp.xml b/app/src/main/res/drawable/ic_security_24dp.xml new file mode 100644 index 00000000..6ffc522d --- /dev/null +++ b/app/src/main/res/drawable/ic_security_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 09aec96b..274ee8a1 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -33,7 +33,7 @@ layout="@layout/layout_settings_fileview"/> + android:id="@+id/securityLayout" + layout="@layout/layout_settings_security" /> + android:layout_below="@id/securityLayout" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4ef3a1b..4a173dc5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,10 +246,10 @@ Translation - Certificates - Delete certificates - Delete trusted certificates? - Are you sure to delete any manually trusted certificate or hostname? You will also be logged out. + Security + Delete Trusted Certificates + Delete Trusted Certificates? + Are you sure to delete any manually trusted certificate or hostname? \n\nYou will also be logged out. Date & Time Settings saved Language From 59e8f6f94a9206315f158baf7ca6a1eb42fbc5a6 Mon Sep 17 00:00:00 2001 From: anonTree1417 Date: Thu, 2 Apr 2020 19:11:23 +0200 Subject: [PATCH 24/24] Fixing popup bug. --- .../main/java/org/mian/gitnex/activities/LoginActivity.java | 2 +- .../org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index c900602b..5f640399 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -467,7 +467,7 @@ public class LoginActivity extends BaseActivity implements View.OnClickListener Toasty.info(getApplicationContext(), getResources().getString(R.string.malformedUrl)); } else { - Toasty.info(getApplicationContext(), getResources().getString(R.string.commentError)); + Toasty.info(getApplicationContext(), getResources().getString(R.string.genericError)); } enableProcessButton(); diff --git a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java index d79f1d6c..3ee8b045 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ssl/MemorizingTrustManager.java @@ -598,15 +598,16 @@ public class MemorizingTrustManager implements X509TrustManager { if(interact(certChainMessage(chain, cause), R.string.mtm_accept_cert) == MTMDecision.DECISION_ALWAYS) { storeCert(chain[0]); // only store the server cert, not the whole chain + } else { + throw (cause); } - - throw (cause); } private boolean interactHostname(X509Certificate cert, String hostname) { if(interact(hostNameMessage(cert, hostname), R.string.mtm_accept_servername) == MTMDecision.DECISION_ALWAYS) { storeCert(hostname, cert); + return true; } return false;