From ac1a5e0ad8bed1c658885c8f6142367a3812815d Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 17 Dec 2018 23:00:38 +0100 Subject: [PATCH] ensure the canonical repo URL is always included in mirrors list The mirror logic assumes that it has a mirrors list with at least once valid entry in it. In the index format as defined by `fdroid update`, there is always at least one valid URL: the canonical URL. That also means if there is only one item in the mirrors list, there are no other URLs to try. The initial state of the repos in the database also include the canonical URL in the mirrors list so the mirror logic works on the first index update. That makes it possible to do the first index update via SD Card or USB OTG drive. --- .../org/fdroid/fdroid/IndexV1Updater.java | 18 ++++++--- .../java/org/fdroid/fdroid/data/DBHelper.java | 9 +++++ .../java/org/fdroid/fdroid/data/Repo.java | 39 +++++++++++++------ 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java index 08f16430b..63ee68901 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java @@ -62,8 +62,10 @@ import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; @@ -284,7 +286,14 @@ public class IndexV1Updater extends IndexUpdater { repo.name = getStringRepoValue(repoMap, "name"); repo.icon = getStringRepoValue(repoMap, "icon"); repo.description = getStringRepoValue(repoMap, "description"); - repo.mirrors = getStringArrayRepoValue(repoMap, "mirrors"); + + // ensure the canonical URL is included in the "mirrors" list + List mirrorsList = getStringListRepoValue(repoMap, "mirrors"); + HashSet mirrors = new HashSet<>(mirrorsList.size() + 1); + mirrors.addAll(mirrorsList); + mirrors.add(repo.address); + repo.mirrors = mirrors.toArray(new String[mirrors.size()]); + // below are optional, can be default value repo.maxage = getIntRepoValue(repoMap, "maxage"); repo.version = getIntRepoValue(repoMap, "version"); @@ -372,13 +381,12 @@ public class IndexV1Updater extends IndexUpdater { } @SuppressWarnings("unchecked") - private String[] getStringArrayRepoValue(Map repoMap, String key) { + private List getStringListRepoValue(Map repoMap, String key) { Object value = repoMap.get(key); if (value != null && value instanceof ArrayList) { - ArrayList list = (ArrayList) value; - return list.toArray(new String[list.size()]); + return (List) value; } - return null; + return Collections.emptyList(); } private HashMap parseRepo(ObjectMapper mapper, JsonParser parser) throws IOException { diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 9eaad2d44..e56a0d139 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -1394,6 +1394,12 @@ public class DBHelper extends SQLiteOpenHelper { return exists; } + /** + * Insert a new repo into the database. This also initializes the list of + * "mirror" URLs. There should always be at least one URL there, since the + * logic in {@link org.fdroid.fdroid.FDroidApp#getMirror(String, Repo)} + * expects at least one entry in the mirrors list. + */ private void insertRepo(SQLiteDatabase db, String name, String address, String description, String version, String enabled, String priority, String pushRequests, String pubKey) { @@ -1410,6 +1416,9 @@ public class DBHelper extends SQLiteOpenHelper { values.put(RepoTable.Cols.LAST_ETAG, (String) null); values.put(RepoTable.Cols.TIMESTAMP, 0); + String[] initializeMirrors = {address}; + values.put(Schema.RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(initializeMirrors)); + switch (pushRequests) { case "ignore": values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_IGNORE); diff --git a/app/src/main/java/org/fdroid/fdroid/data/Repo.java b/app/src/main/java/org/fdroid/fdroid/data/Repo.java index b3b35f4b7..1a9929789 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Repo.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Repo.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; @@ -338,20 +339,29 @@ public class Repo extends ValueObject { } } + /** + * The main repo URL is included in the mirror list, so it only makes + * sense to activate this logic if there are more than one entry in the + * mirror list. + */ public boolean hasMirrors() { return (mirrors != null && mirrors.length > 1) || (userMirrors != null && userMirrors.length > 0); } + /** + * @return {@link List} of valid URLs to reach this repo, including the canonical URL + */ public List getMirrorList() { - final ArrayList allMirrors = new ArrayList(); + final HashSet allMirrors = new HashSet<>(); if (userMirrors != null) { allMirrors.addAll(Arrays.asList(userMirrors)); } if (mirrors != null) { allMirrors.addAll(Arrays.asList(mirrors)); } - return allMirrors; + allMirrors.add(address); + return new ArrayList<>(allMirrors); } /** @@ -360,19 +370,26 @@ public class Repo extends ValueObject { public int getMirrorCount() { int count = 0; for (String m : getMirrorList()) { - if (!m.equals(address)) { - if (FDroidApp.isUsingTor()) { - count++; - } else { - if (!m.contains(".onion")) { - count++; - } - } + if (FDroidApp.isUsingTor()) { + count++; + } else if (!m.contains(".onion")) { + count++; } } return count; } + /** + * The mirror logic assumes that it has a mirrors list with at least once + * valid entry in it. In the index format as defined by {@code fdroid update}, + * there is always at least one valid URL: the canonical URL. That also means + * if there is only one item in the mirrors list, there are no other URLs to try. + *

+ * The initial state of the repos in the database also include the canonical + * URL in the mirrors list so the mirror logic works on the first index + * update. That makes it possible to do the first index update via SD Card + * or USB OTG drive. + */ public String getMirror(String lastWorkingMirror) { if (TextUtils.isEmpty(lastWorkingMirror)) { lastWorkingMirror = address; @@ -382,7 +399,7 @@ public class Repo extends ValueObject { if (shuffledMirrors.size() > 1) { for (String m : shuffledMirrors) { // Return a non default, and not last used mirror - if (!m.equals(address) && !m.equals(lastWorkingMirror)) { + if (!m.equals(lastWorkingMirror)) { if (FDroidApp.isUsingTor()) { return m; } else {