From 88f3ebcb3a7e8859e18c44da3e2afdfe9ab2df34 Mon Sep 17 00:00:00 2001
From: Matthew Leeds <matthew.leeds@endlessm.com>
Date: Mon, 22 Jul 2019 18:31:11 -0700
Subject: [PATCH] dir: Ignore trailing slash in remote URIs

Currently if you have a remote configured with the URL
"https://dl.flathub.org/repo/" (as you would if you use the flatpakrepo
file) and you use a flatpakref file which specifies the URL
"https://dl.flathub.org/repo", Flatpak tries to add a duplicate remote
because it doesn't see those URLs as equal. So ignore the trailing slash
when comparing remote URLs. OSTree works equally well with both kinds
(it uses g_build_filename()).

Flathub served flatpakref files with URLs missing a trailing slash until
this commit:
https://github.com/flathub/ansible-playbook/commit/b20694f09

Also, add a unit test that fails without this patch.

Fixes https://github.com/flatpak/flatpak/issues/2979

Closes: #3065
Approved by: alexlarsson

(cherry picked from commit 866ba643d2d80cd1c8983aab47b774fc6de1920a)

Closes: #3115
Approved by: alexlarsson
---
 common/flatpak-dir.c | 38 ++++++++++++++++++++++++++++++++++++--
 tests/test-repo.sh   | 40 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index d64775d85..40a2e5865 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -18,6 +18,7 @@
  * Authors:
  *       Alexander Larsson <alexl@redhat.com>
  *       Philip Withnall <withnall@endlessm.com>
+ *       Matthew Leeds <matthew.leeds@endlessm.com>
  */
 
 #include "config.h"
@@ -11283,6 +11284,34 @@ flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
   return TRUE;
 }
 
+static gboolean
+_flatpak_uri_equal (const char *uri1,
+                    const char *uri2)
+{
+  g_autofree char *uri1_norm = NULL;
+  g_autofree char *uri2_norm = NULL;
+  gsize uri1_len = strlen (uri1);
+  gsize uri2_len = strlen (uri2);
+
+  /* URIs handled by libostree are equivalent with or without a trailing slash,
+   * but this isn't otherwise guaranteed to be the case.
+   */
+  if (g_str_has_prefix (uri1, "oci+") || g_str_has_prefix (uri2, "oci+"))
+    return g_strcmp0 (uri1, uri2) == 0;
+
+  if (g_str_has_suffix (uri1, "/"))
+    uri1_norm = g_strndup (uri1, uri1_len - 1);
+  else
+    uri1_norm = g_strdup (uri1);
+
+  if (g_str_has_suffix (uri2, "/"))
+    uri2_norm = g_strndup (uri2, uri2_len - 1);
+  else
+    uri2_norm = g_strdup (uri2);
+
+  return g_strcmp0 (uri1_norm, uri2_norm) == 0;
+}
+
 /* This tries to find a pre-configured remote for the specified uri
  * and (optionally) collection id. This is a bit more complex than it
  * sounds, because a local remote could be configured in different
@@ -11297,6 +11326,8 @@ flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
  *  If the collection id is the same (and specified), its going to be
  *  the same remote, even if the url is different (because it could be
  *  some other mirror of the same repo).
+ *
+ *  We also consider non-OCI URLs equal even if one lacks a trailing slash.
  */
 char *
 flatpak_dir_find_remote_by_uri (FlatpakDir *self,
@@ -11305,6 +11336,9 @@ flatpak_dir_find_remote_by_uri (FlatpakDir *self,
 {
   g_auto(GStrv) remotes = NULL;
 
+  g_return_val_if_fail (self != NULL, NULL);
+  g_return_val_if_fail (uri != NULL, NULL);
+
   if (!flatpak_dir_ensure_repo (self, NULL, NULL))
     return NULL;
 
@@ -11333,9 +11367,9 @@ flatpak_dir_find_remote_by_uri (FlatpakDir *self,
               strcmp (collection_id, remote_collection_id) == 0)
             return g_strdup (remote);
 
-          /* Same repo if uris matches, unless both have collection-id
+          /* Same repo if uris match, unless both have collection-id
              specified but different */
-          if (strcmp (uri, remote_uri) == 0 &&
+          if (_flatpak_uri_equal (uri, remote_uri) &&
               !(collection_id != NULL &&
                 remote_collection_id != NULL &&
                 strcmp (collection_id, remote_collection_id) != 0))
diff --git a/tests/test-repo.sh b/tests/test-repo.sh
index 4a3097606..d0b0d3271 100755
--- a/tests/test-repo.sh
+++ b/tests/test-repo.sh
@@ -23,7 +23,7 @@ set -euo pipefail
 
 skip_without_bwrap
 
-echo "1..27"
+echo "1..28"
 
 #Regular repo
 setup_repo
@@ -169,6 +169,44 @@ fi
 
 echo "ok missing remote name auto-corrects for install"
 
+# Use a new remote so we can be sure it doesn't match any existing one's URL
+setup_repo_no_add flatpakref org.test.Collection.Flatpakref
+
+cat << EOF > repos/flatpakref/flatpakref-repo.flatpakrepo
+[Flatpak Repo]
+Version=1
+Url=http://127.0.0.1:$(cat httpd-port-main)/flatpakref/
+Title=The Title
+GPGKey=${FL_GPG_BASE64}
+EOF
+
+if [ x${USE_COLLECTIONS_IN_CLIENT-} == xyes ]; then
+    echo "DeployCollectionID=org.test.Collection.Flatpakref" >> repos/flatpakref/flatpakref-repo.flatpakrepo
+fi
+
+cat << EOF > org.test.Hello.flatpakref
+[Flatpak Ref]
+Name=org.test.Hello
+Branch=master
+Url=http://127.0.0.1:$(cat httpd-port-main)/flatpakref
+GPGKey=${FL_GPG_BASE64}
+RuntimeRepo=http://127.0.0.1:$(cat httpd-port-main)/flatpakref/flatpakref-repo.flatpakrepo
+EOF
+
+${FLATPAK} ${U} uninstall -y org.test.Platform org.test.Hello
+
+# Ensure that only one remote is added even though the URL in the flatpakref
+# does not have a trailing slash and the URL in the flatpakrepo file does
+NUM_REMOTES_BEFORE=$(flatpak remotes | wc -l)
+${FLATPAK} ${U} install -y org.test.Hello.flatpakref
+NUM_REMOTES_AFTER=$(flatpak remotes | wc -l)
+
+if [ $NUM_REMOTES_AFTER -ne $((NUM_REMOTES_BEFORE + 1)) ]; then
+    assert_not_reached "install of flatpakref should only add one remote"
+fi
+
+echo "ok install flatpakref normalizes remote URL trailing slash"
+
 ${FLATPAK} ${U} uninstall -y org.test.Platform org.test.Hello
 
 if ${FLATPAK} ${U} install -y test-missing-gpg-repo org.test.Platform 2> install-error-log; then
