summaryrefslogtreecommitdiff
path: root/svl
diff options
context:
space:
mode:
Diffstat (limited to 'svl')
-rw-r--r--svl/Library_svl.mk2
-rw-r--r--svl/qa/unit/test_URIHelper.cxx63
-rw-r--r--svl/source/misc/urihelper.cxx68
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: */