dont linkify invalid URIs

Phillip Davis created

`Message.getLinks` wasn't broken, but I thought it was at first, and
then I had already written the test, so here it is.

Change summary

src/main/java/de/gultsch/common/Linkify.java                   |  7 
src/test/java/de/gultsch/common/LinkifyTest.java               | 27 +++
src/test/java/eu/siacs/conversations/entities/MessageTest.java | 32 ++++
3 files changed, 66 insertions(+)

Detailed changes

src/main/java/de/gultsch/common/Linkify.java 🔗

@@ -79,6 +79,13 @@ public class Linkify {
 
     public static void addLinks(final Spannable body) {
         android.text.util.Linkify.addLinks(body, Patterns.URI_GENERIC, null, MATCH_FILTER, null);
+        for (final URLSpan span : body.getSpans(0, body.length(), URLSpan.class)) {
+            try {
+                new java.net.URI(span.getURL());
+            } catch (final java.net.URISyntaxException e) {
+                body.removeSpan(span);
+            }
+        }
     }
 
     public static void addLinks(final Editable body, final Account account, final Jid context) {

src/test/java/de/gultsch/common/LinkifyTest.java 🔗

@@ -0,0 +1,27 @@
+package de.gultsch.common;
+
+import android.os.Build;
+import android.text.SpannableStringBuilder;
+import android.text.style.URLSpan;
+
+import eu.siacs.conversations.Conversations;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.ConscryptMode;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Build.VERSION_CODES.TIRAMISU, application = Conversations.class)
+@ConscryptMode(ConscryptMode.Mode.OFF)
+public class LinkifyTest {
+
+    @Test
+    public void addLinksDoesNotLinkifyInvalidUris() {
+        final var text = new SpannableStringBuilder("https://example.com?q=%s");
+        Linkify.addLinks(text);
+        Assert.assertEquals(0, text.getSpans(0, text.length(), URLSpan.class).length);
+    }
+}

src/test/java/eu/siacs/conversations/entities/MessageTest.java 🔗

@@ -0,0 +1,32 @@
+package eu.siacs.conversations.entities;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.Build;
+
+import eu.siacs.conversations.Conversations;
+import eu.siacs.conversations.xmpp.Jid;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.ConscryptMode;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Build.VERSION_CODES.TIRAMISU, application = Conversations.class)
+@ConscryptMode(ConscryptMode.Mode.OFF)
+public class MessageTest {
+
+    @Test
+    public void extractLinksIgnoresInvalidUris() {
+        final var conversation = mock(Conversational.class);
+        when(conversation.getUuid()).thenReturn("test-uuid");
+        when(conversation.getJid()).thenReturn(Jid.ofLocalAndDomain("test", "example.com"));
+
+        final var message = new Message(conversation, "https://example.com?q=%s", Message.ENCRYPTION_NONE);
+        Assert.assertEquals(0, message.getLinks().size());
+    }
+}