cui/source/dialogs/hlinettp.cxx               |    8 ------
 framework/qa/cppunit/services.cxx             |   24 ++++++++++++++++++++
 sw/qa/uitest/writer_tests3/hyperlinkdialog.py |   21 ++++++++++++++++++
 tools/qa/cppunit/test_urlobj.cxx              |   27 +++++++++++++++++++++++
 tools/source/fsys/urlobj.cxx                  |   30 ++++++++++++++++++++++++--
 5 files changed, 101 insertions(+), 9 deletions(-)

New commits:
commit 6b973753d407d66dfa5fda86547246c486ab7087
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Jan 15 11:33:10 2022 +0300
Commit:     Mike Kaganski <[email protected]>
CommitDate: Sat Jan 15 10:52:56 2022 +0100

    tdf#146754: consider xyz:123 as host:port when parsing URLs smart
    
    ... rather than scheme: and path.
    
    Change-Id: I9a48310b585b8fa3e31635f877a91f1560b065f0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128457
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/cui/source/dialogs/hlinettp.cxx b/cui/source/dialogs/hlinettp.cxx
index c26420d5703a..3a0f431acc17 100644
--- a/cui/source/dialogs/hlinettp.cxx
+++ b/cui/source/dialogs/hlinettp.cxx
@@ -158,13 +158,7 @@ OUString SvxHyperlinkInternetTp::CreateAbsoluteURL() const
     // erase leading and trailing whitespaces
     OUString aStrURL(m_xCbbTarget->get_active_text().trim());
 
-    INetURLObject aURL(aStrURL);
-
-    if( aURL.GetProtocol() == INetProtocol::NotValid )
-    {
-        aURL.SetSmartProtocol( GetSmartProtocolFromButtons() );
-        aURL.SetSmartURL(aStrURL);
-    }
+    INetURLObject aURL(aStrURL, GetSmartProtocolFromButtons());
 
     // username and password for ftp-url
     if( aURL.GetProtocol() == INetProtocol::Ftp && 
!m_xEdLogin->get_text().isEmpty() )
diff --git a/framework/qa/cppunit/services.cxx 
b/framework/qa/cppunit/services.cxx
index c75ce9f65633..873ea5938e4c 100644
--- a/framework/qa/cppunit/services.cxx
+++ b/framework/qa/cppunit/services.cxx
@@ -14,6 +14,7 @@
 #include <com/sun/star/frame/XFrame.hpp>
 #include <com/sun/star/frame/XComponentLoader.hpp>
 #include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
 
 #include <comphelper/propertyvalue.hxx>
 #include <salhelper/thread.hxx>
@@ -126,6 +127,29 @@ CPPUNIT_TEST_FIXTURE(Test, testLoadComponentFromURL)
         xThread->join();
     }
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testURLTransformer_parseSmart)
+{
+    // Without the accompanying fix in place, this test would have failed with
+    // "www.example.com:" treated as scheme, "/8080/foo/" as path, "bar?q=baz"
+    // as name, and "F" as fragment.
+
+    css::util::URL aURL;
+    aURL.Complete = "www.example.com:8080/foo/bar?q=baz#F";
+    css::uno::Reference 
xParser(css::util::URLTransformer::create(mxComponentContext));
+    CPPUNIT_ASSERT(xParser->parseSmart(aURL, "http:"));
+    
CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar?q=baz#F";), 
aURL.Complete);
+    CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar";), 
aURL.Main);
+    CPPUNIT_ASSERT_EQUAL(OUString("http://";), aURL.Protocol);
+    CPPUNIT_ASSERT(aURL.User.isEmpty());
+    CPPUNIT_ASSERT(aURL.Password.isEmpty());
+    CPPUNIT_ASSERT_EQUAL(OUString("www.example.com"), aURL.Server);
+    CPPUNIT_ASSERT_EQUAL(sal_Int16(8080), aURL.Port);
+    CPPUNIT_ASSERT_EQUAL(OUString("/foo/"), aURL.Path);
+    CPPUNIT_ASSERT_EQUAL(OUString("bar"), aURL.Name);
+    CPPUNIT_ASSERT_EQUAL(OUString("q=baz"), aURL.Arguments);
+    CPPUNIT_ASSERT_EQUAL(OUString("F"), aURL.Mark);
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/qa/uitest/writer_tests3/hyperlinkdialog.py 
b/sw/qa/uitest/writer_tests3/hyperlinkdialog.py
index c20a39b48f2a..c737f33ad1b8 100644
--- a/sw/qa/uitest/writer_tests3/hyperlinkdialog.py
+++ b/sw/qa/uitest/writer_tests3/hyperlinkdialog.py
@@ -72,6 +72,27 @@ class HyperlinkDialog(UITestCase):
             self.assertEqual(get_state_as_dict(xedit)["SelectedText"], "link")
 
 
+    def test_insert_hyperlink_without_scheme(self):
+
+        with self.ui_test.create_doc_in_start_center("writer"):
+            xMainWindow = self.xUITest.getTopFocusWindow()
+
+            with 
self.ui_test.execute_dialog_through_command(".uno:HyperlinkDialog") as xDialog:
+
+                # insert link
+                xtab=xDialog.getChild("tabcontrol")
+                select_pos(xtab, "0")
+
+                xtarget = xDialog.getChild("target")
+                xtarget.executeAction("TYPE", mkPropertyValues({"TEXT": 
"www.libreoffice.org:80"}))
+
+            # Check that the link is added with http scheme
+            xMainWindow = self.xUITest.getTopFocusWindow()
+            xedit = xMainWindow.getChild("writer_edit")
+            xedit.executeAction("SELECT", mkPropertyValues({"START_POS": "0", 
"END_POS": "29"}))
+            self.assertEqual(get_state_as_dict(xedit)["SelectedText"], 
"http://www.libreoffice.org:80";)
+
+
     def test_tdf141166(self):
         # Skip this test for --with-help=html and --with-help=online, as that 
would fail with a
         # DialogNotExecutedException("did not execute a dialog for a blocking 
action") thrown from
diff --git a/tools/qa/cppunit/test_urlobj.cxx b/tools/qa/cppunit/test_urlobj.cxx
index ec64b5d66777..abcd2fe1417b 100644
--- a/tools/qa/cppunit/test_urlobj.cxx
+++ b/tools/qa/cppunit/test_urlobj.cxx
@@ -319,6 +319,32 @@ namespace tools_urlobj
                 obj.GetMainURL(INetURLObject::DecodeMechanism::NONE));
         }
 
+        void testParseSmart()
+        {
+            {
+                // host:port must not be misinterpreted as scheme:path
+                INetURLObject obj("example.com:8080/foo", INetProtocol::Http);
+                CPPUNIT_ASSERT(!obj.HasError());
+                CPPUNIT_ASSERT_EQUAL(OUString("http://example.com:8080/foo";),
+                    obj.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+                CPPUNIT_ASSERT_EQUAL(INetProtocol::Http, obj.GetProtocol());
+                CPPUNIT_ASSERT_EQUAL(OUString("example.com"), obj.GetHost());
+                CPPUNIT_ASSERT_EQUAL(sal_uInt32(8080), obj.GetPort());
+                CPPUNIT_ASSERT_EQUAL(OUString("/foo"), obj.GetURLPath());
+            }
+            {
+                // port may only contain decimal digits, so this must be 
treated as unknown scheme
+                INetURLObject obj("example.com:80a0/foo", INetProtocol::Http);
+                CPPUNIT_ASSERT(!obj.HasError());
+                CPPUNIT_ASSERT_EQUAL(OUString("example.com:80a0/foo"),
+                    obj.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+                CPPUNIT_ASSERT_EQUAL(INetProtocol::Generic, obj.GetProtocol());
+                CPPUNIT_ASSERT(obj.isSchemeEqualTo(u"example.com"));
+                CPPUNIT_ASSERT_EQUAL(OUString(""), obj.GetHost());
+                CPPUNIT_ASSERT_EQUAL(OUString("80a0/foo"), obj.GetURLPath());
+            }
+        }
+
         // Change the following lines only, if you add, remove or rename
         // member functions of the current class,
         // because these macros are need by auto register mechanism.
@@ -335,6 +361,7 @@ namespace tools_urlobj
         CPPUNIT_TEST( testSetExtension );
         CPPUNIT_TEST( testChangeScheme );
         CPPUNIT_TEST( testTd146382 );
+        CPPUNIT_TEST( testParseSmart );
         CPPUNIT_TEST_SUITE_END(  );
     };                          // class createPool
 
diff --git a/tools/source/fsys/urlobj.cxx b/tools/source/fsys/urlobj.cxx
index 49d0500cabb0..1b171ad2ed8e 100644
--- a/tools/source/fsys/urlobj.cxx
+++ b/tools/source/fsys/urlobj.cxx
@@ -869,8 +869,34 @@ bool INetURLObject::setAbsURIRef(OUString const & 
rTheAbsURIRef,
             aSynScheme = parseScheme(&p1, pEnd, nFragmentDelimiter);
             if (!aSynScheme.isEmpty())
             {
-                m_eScheme = INetProtocol::Generic;
-                pPos = p1;
+                if (bSmart && m_eSmartScheme != m_eScheme && p1 != pEnd && 
rtl::isAsciiDigit(*p1))
+                {
+                    // rTheAbsURIRef doesn't define a known scheme (handled by 
the "if (pPrefix)"
+                    // branch above); but a known scheme is defined in 
m_eSmartScheme. If this
+                    // scheme may have a port in authority component, then 
avoid misinterpreting
+                    // URLs like www.foo.bar:123/baz as using unknown 
"www.foo.bar" scheme with
+                    // 123/baz rootless path. For now, do not try to handle 
possible colons in
+                    // user information, require such ambiguous URLs to have 
explicit scheme part.
+                    // Also ignore possibility of empty port.
+                    const SchemeInfo& rInfo = getSchemeInfo(m_eSmartScheme);
+                    if (rInfo.m_bAuthority && rInfo.m_bPort)
+                    {
+                        // Make sure that all characters from colon to [/?#] 
or to EOL are digits.
+                        // Or maybe make it simple, and just assume that 
"xyz:1..." is more likely
+                        // to be host "xyz" and port "1...", than scheme "xyz" 
and path "1..."?
+                        sal_Unicode const* p2 = p1 + 1;
+                        while (p2 != pEnd && rtl::isAsciiDigit(*p2))
+                            ++p2;
+                        if (p2 == pEnd || *p2 == '/' || *p2 == '?' || *p2 == 
'#')
+                            m_eScheme = m_eSmartScheme;
+                    }
+                }
+
+                if (m_eScheme == INetProtocol::NotValid)
+                {
+                    m_eScheme = INetProtocol::Generic;
+                    pPos = p1;
+                }
             }
         }
 

Reply via email to