diff --git a/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java index ac9d592..bda20a0 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java @@ -35,6 +35,10 @@ public final class CustomRepoData extends RepoData { JSONObject jsonObject = new JSONObject( new String(Http.doHttpGet(this.getUrl(), false), StandardCharsets.UTF_8)); + // make sure there's at least a name and a modules or data object + if (!jsonObject.has("name") || (!jsonObject.has("modules") && !jsonObject.has("data"))) { + throw new IllegalArgumentException("Invalid repo: " + this.getUrl()); + } this.name = jsonObject.getString("name").trim(); this.website = jsonObject.optString("website"); this.support = jsonObject.optString("support"); diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index 7402683..518ce03 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -71,8 +71,6 @@ import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.mikepenz.aboutlibraries.LibsBuilder; import com.topjohnwu.superuser.internal.UiThreadHandler; -import org.json.JSONException; - import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -666,8 +664,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { public static class RepoFragment extends PreferenceFragmentCompat { private static final int CUSTOM_REPO_ENTRIES = 5; - // *says proudly* I stole it - // namely, from https://github.com/NeoApplications/Neo-Wellbeing/blob/9fca4136263780c022f9ec6433c0b43d159166db/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/SettingsActivity.java#L101 + /** + * proudly I stole it + *

+ * namely, from neo wellbeing + */ public static void applyMaterial3(Preference p) { if (p instanceof PreferenceGroup) { PreferenceGroup pg = (PreferenceGroup) p; @@ -816,6 +817,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // Get the dummy pref_androidacy_repo_api_token preference with id pref_androidacy_repo_api_token // we have to use the id because the key is different EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_repo_api_token")); + // add validation to the EditTextPreference + // string must be 64 characters long, and only allows alphanumeric characters prefAndroidacyRepoApiKey.setTitle(R.string.api_key); prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary); prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key); @@ -834,6 +837,14 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); prefAndroidacyRepoApiKey.setPositiveButtonText(R.string.save_api_key); prefAndroidacyRepoApiKey.setOnPreferenceChangeListener((preference, newValue) -> { + // validate the api key client side first. should be 64 characters long, and only allow alphanumeric characters + if (!newValue.toString().matches("[a-zA-Z0-9]{64}")) { + // Show snack bar with error + Snackbar.make(requireView(), R.string.api_key_mismatch, Snackbar.LENGTH_LONG).show(); + // Restore the original api key + prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); + return false; + } // Make sure originalApiKeyRef is not null if (originalApiKeyRef[0].equals(newValue)) return true; @@ -990,13 +1001,18 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { public void run() { try { customRepoData.quickPrePopulate(); + UiThreadHandler.handler.post(() -> updateCustomRepoList(false)); } catch ( - IOException | - JSONException | - NoSuchAlgorithmException e) { + Exception e) { Timber.e(e); + // show new dialog + new Handler(Looper.getMainLooper()).post(() -> new MaterialAlertDialogBuilder(context) + .setTitle(R.string.error_adding) + .setMessage(e.getMessage()) + .setPositiveButton(android.R.string.ok, (dialog1, which1) -> { + }) + .show()); } - UiThreadHandler.handler.post(() -> updateCustomRepoList(false)); } }.start(); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java b/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java index 73f7c0d..24c8540 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java @@ -8,6 +8,7 @@ import android.content.Context; import androidx.annotation.NonNull; +import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainApplication; import java.io.IOException; @@ -16,6 +17,7 @@ import java.util.HashSet; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; +import timber.log.Timber; /** * This interceptor put all the Cookies in Preferences in the Request. @@ -42,14 +44,20 @@ public class AddCookiesInterceptor implements Interceptor { // Some APIs die if you do it differently. StringBuilder cookiestring = new StringBuilder(); for (String cookie : preferences) { - String[] parser = cookie.split(";"); - cookiestring.append(parser[0]).append("; "); + // if cookie doesn't end in a semicolon, add one. + if (!cookie.endsWith(";")) { + cookie = cookie + ";"; + } + cookiestring.append(cookie).append(" "); } - builder.addHeader("Cookie", cookiestring.toString()); - - for (String cookie : preferences) { - builder.addHeader("Cookie", cookie); + // if ccokiestring doesn't have is_foxmmm cookie, add a never expiring one for the current domain. + if (!cookiestring.toString().contains("is_foxmmm")) { + cookiestring.append("is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=").append(chain.request().url().host()).append("; SameSite=None; Secure;"); } + if (BuildConfig.DEBUG_HTTP) { + Timber.d("Sending cookies: %s", cookiestring.toString()); + } + builder.addHeader("Cookie", cookiestring.toString()); return chain.proceed(builder.build()); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java index 3739894..5e2306c 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java @@ -234,7 +234,13 @@ public class Http { // Log, but set all query parameters values to "****" while keeping the keys Timber.d("doHttpGet: %s", url.replaceAll("=[^&]*", "=****")); } - Response response = (allowCache ? getHttpClientWithCache() : getHttpClient()).newCall(new Request.Builder().url(url).get().build()).execute(); + Response response; + try { + response = (allowCache ? getHttpClientWithCache() : getHttpClient()).newCall(new Request.Builder().url(url).get().build()).execute(); + } catch (IOException e) { + Timber.e(e, "Failed to fetch %s", url.replaceAll("=[^&]*", "=****")); + throw new HttpException(e.getMessage(), 0); + } if (BuildConfig.DEBUG_HTTP) { Timber.d("doHttpGet: request executed"); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/ReceivedCookiesInterceptor.java b/app/src/main/java/com/fox2code/mmm/utils/io/ReceivedCookiesInterceptor.java index 834b2db..a962d66 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/ReceivedCookiesInterceptor.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/ReceivedCookiesInterceptor.java @@ -10,6 +10,7 @@ import android.content.SharedPreferences; import androidx.annotation.NonNull; +import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainApplication; import java.io.IOException; @@ -17,6 +18,7 @@ import java.util.HashSet; import okhttp3.Interceptor; import okhttp3.Response; +import timber.log.Timber; public class ReceivedCookiesInterceptor implements Interceptor { private final Context context; @@ -33,8 +35,14 @@ public class ReceivedCookiesInterceptor implements Interceptor { HashSet cookies = (HashSet) MainApplication.getSharedPreferences().getStringSet("PREF_COOKIES", new HashSet<>()); cookies.addAll(originalResponse.headers("Set-Cookie")); + if (!cookies.toString().contains("is_foxmmm")) { + cookies.add("is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=" + chain.request().url().host() + "; SameSite=None; Secure;"); + } SharedPreferences.Editor memes = MainApplication.getSharedPreferences().edit(); + if (BuildConfig.DEBUG_HTTP) { + Timber.d("Received cookies: %s", cookies); + } memes.putStringSet("PREF_COOKIES", cookies).apply(); memes.commit(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c1b6df1..2c453d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -316,5 +316,5 @@ Reset the app You\'re pretty awesome! Looks like you\'ve already upgraded your subscription and are supporting Androidacy. Premium active - If you keep seeing this screen, resetting the app might help. This will clear app data but will not effect installed modules.This will completely remove all app data and close the app. Modules will not be uninstalled.ResetThis is going to completely wipe app data, but will not effect modules. + If you keep seeing this screen, resetting the app might help. This will clear app data but will not effect installed modules.This will completely remove all app data and close the app. Modules will not be uninstalled.ResetThis is going to completely wipe app data, but will not effect modules.Failed to add custom repoAPI key is in an invalid format diff --git a/app/src/main/res/xml/repo_preferences.xml b/app/src/main/res/xml/repo_preferences.xml index a6168be..590cfc5 100644 --- a/app/src/main/res/xml/repo_preferences.xml +++ b/app/src/main/res/xml/repo_preferences.xml @@ -1,5 +1,5 @@ - + @@ -13,6 +13,7 @@