diff options
Diffstat (limited to 'svl')
-rw-r--r-- | svl/Library_svl.mk | 2 | ||||
-rw-r--r-- | svl/qa/unit/test_URIHelper.cxx | 63 | ||||
-rw-r--r-- | svl/source/misc/urihelper.cxx | 68 |
3 files changed, 133 insertions, 0 deletions
diff --git a/svl/Library_svl.mk b/svl/Library_svl.mk index db08af5f4b3a..3a7b00d1aaa0 100644 --- a/svl/Library_svl.mk +++ b/svl/Library_svl.mk @@ -21,6 +21,8 @@ $(eval $(call gb_Library_Library,svl)) $(eval $(call gb_Library_use_externals,svl,\ boost_headers \ + icu_headers \ + icuuc \ mdds_headers \ libxml2 \ )) diff --git a/svl/qa/unit/test_URIHelper.cxx b/svl/qa/unit/test_URIHelper.cxx index f27149b57a01..37d53e90ec6c 100644 --- a/svl/qa/unit/test_URIHelper.cxx +++ b/svl/qa/unit/test_URIHelper.cxx @@ -198,9 +198,12 @@ public: void testFindFirstURLInText(); + void testResolveIdnaHost(); + CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(testNormalizedMakeRelative); CPPUNIT_TEST(testFindFirstURLInText); + CPPUNIT_TEST(testResolveIdnaHost); CPPUNIT_TEST(finish); CPPUNIT_TEST_SUITE_END(); @@ -423,6 +426,66 @@ void Test::testFindFirstURLInText() { } } +void Test::testResolveIdnaHost() { + OUString input; + + input.clear(); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("Foo.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://Muenchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://-M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://M\xC3\xBCnchen-.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://xn--M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://xy--M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://-bar.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://bar-.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://xn--bar.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + input = OUString::fromUtf8("foo://xy--bar.M\xC3\xBCnchen.de"); + CPPUNIT_ASSERT_EQUAL(input, URIHelper::resolveIdnaHost(input)); + + CPPUNIT_ASSERT_EQUAL( + OUString::fromUtf8("foo://M\xC3\xBCnchen@xn--mnchen-3ya.de"), + URIHelper::resolveIdnaHost( + OUString::fromUtf8("foo://M\xC3\xBCnchen@M\xC3\xBCnchen.de"))); + + CPPUNIT_ASSERT_EQUAL( + OUString::fromUtf8("foo://xn--mnchen-3ya.de."), + URIHelper::resolveIdnaHost( + OUString::fromUtf8("foo://M\xC3\xBCnchen.de."))); + + CPPUNIT_ASSERT_EQUAL( + OUString::fromUtf8("Foo://bar@xn--mnchen-3ya.de:123/?bar#baz"), + URIHelper::resolveIdnaHost( + OUString::fromUtf8("Foo://bar@M\xC3\xBCnchen.de:123/?bar#baz"))); + + CPPUNIT_ASSERT_EQUAL( + OUString::fromUtf8("foo://xn--mnchen-3ya.de"), + URIHelper::resolveIdnaHost( + OUString::fromUtf8("foo://Mu\xCC\x88nchen.de"))); +} + css::uno::Reference< css::uno::XComponentContext > Test::m_context; CPPUNIT_TEST_SUITE_REGISTRATION(Test); diff --git a/svl/source/misc/urihelper.cxx b/svl/source/misc/urihelper.cxx index ab47bb6de3b6..bb5678a9291f 100644 --- a/svl/source/misc/urihelper.cxx +++ b/svl/source/misc/urihelper.cxx @@ -17,6 +17,10 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <sal/config.h> + +#include <unicode/idna.h> + #include <svl/urihelper.hxx> #include <com/sun/star/ucb/Command.hpp> #include <com/sun/star/ucb/IllegalIdentifierException.hpp> @@ -725,4 +729,68 @@ OUString URIHelper::removePassword(OUString const & rURI, aObj.GetURLNoPass(eDecodeMechanism, eCharset); } +OUString URIHelper::resolveIdnaHost(OUString const & url) { + css::uno::Reference<css::uri::XUriReference> uri( + css::uri::UriReferenceFactory::create( + comphelper::getProcessComponentContext()) + ->parse(url)); + if (!(uri.is() && uri->hasAuthority())) { + return url; + } + auto auth(uri->getAuthority()); + sal_Int32 hostStart = auth.indexOf('@') + 1; + sal_Int32 hostEnd = auth.getLength() - 1; + while (hostEnd > hostStart && rtl::isAsciiDigit(auth[hostEnd])) { + --hostEnd; + } + if (!(hostEnd > hostStart && auth[hostEnd] == ':')) { + hostEnd = auth.getLength() - 1; + } + auto asciiOnly = true; + for (auto i = hostStart; i != hostEnd; ++i) { + if (!rtl::isAscii(auth[i])) { + asciiOnly = false; + break; + } + } + if (asciiOnly) { + // Avoid icu::IDNA case normalization in purely non-IDNA domain names: + return url; + } + UErrorCode e = U_ZERO_ERROR; + std::unique_ptr<icu::IDNA> idna( + icu::IDNA::createUTS46Instance( + (UIDNA_USE_STD3_RULES | UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ + | UIDNA_CHECK_CONTEXTO), + e)); + if (U_FAILURE(e)) { + SAL_WARN("vcl.gdi", "icu::IDNA::createUTS46Instance " << e); + return url; + } + icu::UnicodeString ascii; + icu::IDNAInfo info; + idna->nameToASCII( + icu::UnicodeString( + reinterpret_cast<UChar const *>(auth.getStr() + hostStart), + hostEnd - hostStart), + ascii, info, e); + if (U_FAILURE(e) || info.hasErrors()) { + return url; + } + OUStringBuffer buf(uri->getScheme()); + buf.append("://").append(auth.getStr(), hostStart); + buf.append( + reinterpret_cast<sal_Unicode const *>(ascii.getBuffer()), + ascii.length()); + buf.append(auth.getStr() + hostEnd, auth.getLength() - hostEnd) + .append(uri->getPath()); + if (uri->hasQuery()) { + buf.append('?').append(uri->getQuery()); + } + if (uri->hasFragment()) { + buf.append('#').append(uri->getFragment()); + } + return buf.makeStringAndClear(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |