Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit d294c64d authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #6546 from thundernest/uri_matcher_update

Add `GenericUriParser`
parents 49ac2228 c444be48
Loading
Loading
Loading
Loading
+0 −29
Original line number Diff line number Diff line
package com.fsck.k9.message.html;


import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;


class BitcoinUriParser implements UriParser {
    private static final Pattern BITCOIN_URI_PATTERN =
            Pattern.compile("bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?");

    @Nullable
    @Override
    public UriMatch parseUri(@NotNull CharSequence text, int startPos) {
        Matcher matcher = BITCOIN_URI_PATTERN.matcher(text);

        if (!matcher.find(startPos) || matcher.start() != startPos) {
            return null;
        }

        int startIndex = matcher.start();
        int endIndex = matcher.end();
        CharSequence uri = text.subSequence(startIndex, endIndex);
        return new UriMatch(startIndex, endIndex, uri);
    }
}
+0 −33
Original line number Diff line number Diff line
package com.fsck.k9.message.html;


import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;


/**
 * Parses ERC-67 URIs
 * https://github.com/ethereum/EIPs/issues/67
 */
class EthereumUriParser implements UriParser {
    private static final Pattern ETHEREUM_URI_PATTERN =
            Pattern.compile("ethereum:0x[0-9a-fA-F]*(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?");

    @Nullable
    @Override
    public UriMatch parseUri(@NotNull CharSequence text, int startPos) {
        Matcher matcher = ETHEREUM_URI_PATTERN.matcher(text);

        if (!matcher.find(startPos) || matcher.start() != startPos) {
            return null;
        }

        int startIndex = matcher.start();
        int endIndex = matcher.end();
        CharSequence uri = text.subSequence(startIndex, endIndex);
        return new UriMatch(startIndex, endIndex, uri);
    }
}
+34 −0
Original line number Diff line number Diff line
package com.fsck.k9.message.html

import java.util.regex.Pattern

/**
 * Matches the URI generic syntax.
 *
 * See [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986).
 */
class GenericUriParser : UriParser {
    override fun parseUri(text: CharSequence, startPos: Int): UriMatch? {
        val matcher = PATTERN.matcher(text)
        if (!matcher.find(startPos) || matcher.start() != startPos) return null

        val startIndex = matcher.start()
        val endIndex = matcher.end()
        val uri = text.subSequence(startIndex, endIndex)

        return UriMatch(startIndex, endIndex, uri)
    }

    companion object {
        private const val SCHEME = "[a-zA-Z][a-zA-Z0-9+.\\-]*"
        private const val AUTHORITY = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:\\[\\]@]*"
        private const val PATH = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/]*"
        private const val QUERY = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/?]*"
        private const val FRAGMENT = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/?]*"

        // This regular expression matches more than allowed by the generic URI syntax. So we might end up linkifying
        // text that is not a proper URI. We leave apps actually handling the URI when the user clicks on such a link
        // to deal with this case.
        private val PATTERN = Pattern.compile("$SCHEME:(?://$AUTHORITY)?(?:$PATH)?(?:\\?$QUERY)?(?:#$FRAGMENT)?")
    }
}
+5 −3
Original line number Diff line number Diff line
@@ -3,12 +3,14 @@ package com.fsck.k9.message.html
object UriMatcher {
    private val SUPPORTED_URIS = run {
        val httpUriParser = HttpUriParser()
        val genericUriParser = GenericUriParser()
        mapOf(
            "ethereum:" to EthereumUriParser(),
            "bitcoin:" to BitcoinUriParser(),
            "http:" to httpUriParser,
            "https:" to httpUriParser,
            "rtsp:" to httpUriParser
            "mailto:" to genericUriParser,
            "matrix:" to genericUriParser,
            "rtsp:" to httpUriParser,
            "xmpp:" to genericUriParser,
        )
    }

+0 −73
Original line number Diff line number Diff line
package com.fsck.k9.message.html;


import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;


public class BitcoinUriParserTest {
    BitcoinUriParser parser = new BitcoinUriParser();


    @Test
    public void basicBitcoinUri() throws Exception {
        assertValidUri("bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
    }

    @Test
    public void bitcoinUriWithAmount() throws Exception {
        assertValidUri("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2");
    }

    @Test
    public void bitcoinUriWithQueryParameters() throws Exception {
        assertValidUri("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2" +
                "&message=Payment&label=Satoshi&extra=other-param");
    }

    @Test
    public void uriInMiddleOfInput() throws Exception {
        String prefix = "prefix ";
        String uri = "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2";
        String text = prefix + uri;

        UriMatch uriMatch = parser.parseUri(text, prefix.length());

        assertUriMatch(uri, uriMatch, prefix.length());
    }

    @Test
    public void invalidScheme() throws Exception {
        assertInvalidUri("bitcion:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
    }

    @Test
    public void invalidAddress() throws Exception {
        assertInvalidUri("bitcoin:[invalid]");
    }


    private void assertValidUri(String uri) {
        UriMatch uriMatch = parser.parseUri(uri, 0);
        assertUriMatch(uri, uriMatch);
    }

    private void assertUriMatch(String uri, UriMatch uriMatch) {
        assertUriMatch(uri, uriMatch, 0);
    }

    private void assertUriMatch(String uri, UriMatch uriMatch, int offset) {
        assertNotNull(uriMatch);
        assertEquals(offset, uriMatch.getStartIndex());
        assertEquals(uri.length() + offset, uriMatch.getEndIndex());
        assertEquals(uri, uriMatch.getUri().toString());
    }

    private void assertInvalidUri(String text) {
        UriMatch uriMatch = parser.parseUri(text, 0);
        assertNull(uriMatch);
    }
}
Loading