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