diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml index c362c228e..a5269a228 100644 --- a/F-Droid/res/values/strings.xml +++ b/F-Droid/res/values/strings.xml @@ -97,6 +97,7 @@ This repo is already setup, confirm that you want to re-enable it. The incoming repo is already setup and enabled. You must first delete this repo before you can add one with a different key. + This is not a valid URL. Ignoring malformed repo URI: %s The list of used repositories has diff --git a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java index 15912d2d1..166fbee5c 100644 --- a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java +++ b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java @@ -78,6 +78,8 @@ import org.fdroid.fdroid.views.fragments.RepoDetailsFragment; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import java.util.Locale; @@ -98,7 +100,7 @@ public class ManageReposActivity extends ActionBarActivity { private enum AddRepoState { DOESNT_EXIST, EXISTS_FINGERPRINT_MISMATCH, EXISTS_FINGERPRINT_MATCH, - EXISTS_DISABLED, EXISTS_ENABLED, EXISTS_UPGRADABLE_TO_SIGNED + EXISTS_DISABLED, EXISTS_ENABLED, EXISTS_UPGRADABLE_TO_SIGNED, INVALID_URL } private UpdateService.UpdateReceiver updateHandler = null; @@ -390,6 +392,13 @@ public class ManageReposActivity extends ActionBarActivity { String fp = fingerprintEditText.getText().toString(); String url = uriEditText.getText().toString(); + try { + url = normalizeUrl(url); + } catch (URISyntaxException e) { + invalidUrl(); + return; + } + switch(addRepoState) { case DOESNT_EXIST: prepareToCreateNewRepo(url, fp); @@ -449,7 +458,15 @@ public class ManageReposActivity extends ActionBarActivity { * Compare the repo and the fingerprint against existing repositories, to see if this * repo matches and display a relevant message to the user if that is the case. */ - private void validateRepoDetails(@NonNull final String uri, @NonNull String fingerprint) { + private void validateRepoDetails(@NonNull String uri, @NonNull String fingerprint) { + + try { + uri = normalizeUrl(uri); + } catch (URISyntaxException e) { + // Don't bother dealing with this exception yet, as this is called every time + // a letter is added to the repo URL text input. We don't want to display a message + // to the user until they try to save the repo. + } final Repo repo = uri.length() > 0 ? RepoProvider.Helper.findByAddress(context, uri) : null; @@ -485,6 +502,11 @@ public class ManageReposActivity extends ActionBarActivity { true, R.string.overwrite, false); } + private void invalidUrl() { + updateUi(AddRepoState.INVALID_URL, R.string.invalid_url, true, + R.string.repo_add_add, false); + } + private void repoExistsAndDisabled() { updateUi(AddRepoState.EXISTS_DISABLED, R.string.repo_exists_enable, false, R.string.enable, true); @@ -606,6 +628,31 @@ public class ManageReposActivity extends ActionBarActivity { checker.execute(originalAddress); } + /** + * Some basic sanitization of URLs, so that two URLs which have the same semantic meaning + * are represented by the exact same string by F-Droid. This will help to make sure that, + * e.g. "http://10.0.1.50" and "http://10.0.1.50/" are not two different repositories. + * + * Currently it normalizes the path so that "/./" are removed and "test/../" is collapsed. + * This is done using {@link URI#normalize()}. It also removes multiple consecutive forward + * slashes in the path and replaces them with one. Finally, it removes trailing slashes. + */ + private String normalizeUrl(String urlString) throws URISyntaxException { + URI uri = new URI(urlString); + if (!uri.isAbsolute()) { + throw new URISyntaxException(urlString, "Must provide an absolute URI for repositories"); + } + + uri = uri.normalize(); + String path = uri.getPath().replaceAll("//*/", "/"); // Collapse multiple forward slashes into 1. + if (path.length() > 0 && path.charAt(path.length() - 1) == '/') { + path = path.substring(0, path.length() - 1); + } + + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), + path, uri.getQuery(), uri.getFragment()).toString(); + } + private void createNewRepo(String address, String fingerprint) { ContentValues values = new ContentValues(2); values.put(RepoProvider.DataColumns.ADDRESS, address);