This is an automated email from the ASF dual-hosted git repository.

ardovm pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit 06d7e6fcc50631716e4dd8909f5d630a5b1f52c5
Author: Peter <[email protected]>
AuthorDate: Sat Sep 26 22:16:29 2026 +0200

    Accept local files
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    (cherry picked from commit 14eaf708515ab11319faef612741acb86321c914)
---
 .../main/java/com/sun/star/comp/sdbc/Tools.java    | 71 +++++++++++++++---
 main/jvmaccess/source/classpath.cxx                | 87 +++++++++++++++++++---
 2 files changed, 136 insertions(+), 22 deletions(-)

diff --git 
a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java
 
b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java
index 9725949b32..a9e23d6618 100644
--- 
a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java
+++ 
b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java
@@ -153,23 +153,26 @@ public class Tools {
     /**
      * Appends a class path entry to the list of URLs used to build a class 
loader.
      *
-     * <p>Only local entries or a jar: wrapping a local entry are added.
-     * A malformed or non-local entry is logged and skipped.</p>
+     * <p>Only local entries or a jar: wrapping a local entry are added; a 
file: entry must
+     * in addition name a path on this machine. A malformed or non-local entry 
is logged and
+     * skipped.</p>
      *
      * @param urls the list of class path URLs to append to
      * @param url  the class path entry to parse and validate
      */
     public static void addClassPathURL(Collection<URL> urls, String url) {
         URL javaURL;
-        String protocol;
+        URL effectiveURL;
         try {
             javaURL = new URL(url);
-            protocol = getEffectiveProtocol(javaURL);
+            effectiveURL = getEffectiveURL(javaURL);
         } catch (MalformedURLException e) {
             LOGGER.log(Level.WARNING, e, () -> "Skipping malformed class path 
entry: " + url);
             return;
         }
-        if (LOCAL_PROTOCOLS.contains(protocol)) {
+        String protocol = effectiveURL.getProtocol();
+        if (LOCAL_PROTOCOLS.contains(protocol)
+                && (!"file".equals(protocol) || 
isLocalFileLocation(effectiveURL))) {
             LOGGER.fine(() -> "Adding class path entry: " + url);
             urls.add(javaURL);
         } else {
@@ -178,21 +181,65 @@ public class Tools {
     }
 
     /**
-     * Returns the scheme that actually locates the resource.
+     * Returns the URL that actually locates the resource.
      *
-     * <p>Since {@code jar:} only wraps another URL, the scheme of that 
wrapped URL is returned.
-     * For any other URL its own scheme is returned.</p>
+     * <p>Since {@code jar:} only wraps another URL, that wrapped URL is 
returned.
+     * For any other URL the URL itself is returned.</p>
      *
      * @param url the class path URL to inspect
-     * @return the effective URL scheme
+     * @return the effective URL
      * @throws MalformedURLException if the wrapped jar: URL cannot be parsed
      */
-    private static String getEffectiveProtocol(URL url) throws 
MalformedURLException {
+    private static URL getEffectiveURL(URL url) throws MalformedURLException {
         if (!"jar".equals(url.getProtocol())) {
-            return url.getProtocol();
+            return url;
         }
         String path = url.getPath();
         int separator = path.lastIndexOf("!/");
-        return new URL(separator == -1 ? path : path.substring(0, 
separator)).getProtocol();
+        return new URL(separator == -1 ? path : path.substring(0, separator));
+    }
+
+    /**
+     * Tells whether a file: URL names a path on this machine.
+     *
+     * <p>The host must be empty or {@code localhost}, and the path must not 
name another
+     * machine by itself. The path is judged decoded, because the file: handler
+     * percent-decodes it and, on Windows, turns slashes into backslashes 
before opening
+     * it; a leading escaped slash or backslash would otherwise reach the file 
system as a
+     * reference to a share.</p>
+     *
+     * @param url a URL with the file: scheme
+     * @return whether the URL names a local path
+     */
+    private static boolean isLocalFileLocation(URL url) {
+        String host = url.getHost();
+        if (host != null && !host.isEmpty() && 
!"localhost".equalsIgnoreCase(host)) {
+            return false;
+        }
+        String path = percentDecoded(url.getPath());
+        return path.length() >= 2 && path.charAt(0) == '/' && path.charAt(1) 
!= '/'
+                && path.indexOf('\\') == -1;
+    }
+
+    /**
+     * Undoes one level of %HH escapes, as the file: handler does. Only the 
ASCII
+     * separators matter to the caller, so each escape becomes the character 
of its byte
+     * value; a malformed escape is kept.
+     */
+    private static String percentDecoded(String s) {
+        StringBuilder buf = new StringBuilder(s.length());
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '%' && s.length() - i > 2) {
+                int hi = Character.digit(s.charAt(i + 1), 16);
+                int lo = Character.digit(s.charAt(i + 2), 16);
+                if (hi != -1 && lo != -1) {
+                    c = (char) (hi * 16 + lo);
+                    i += 2;
+                }
+            }
+            buf.append(c);
+        }
+        return buf.toString();
     }
 }
diff --git a/main/jvmaccess/source/classpath.cxx 
b/main/jvmaccess/source/classpath.cxx
index f241cb9ff6..100f509590 100644
--- a/main/jvmaccess/source/classpath.cxx
+++ b/main/jvmaccess/source/classpath.cxx
@@ -37,6 +37,7 @@
 #include "com/sun/star/uri/XVndSunStarExpandUrlReference.hpp"
 #include "com/sun/star/util/XMacroExpander.hpp"
 #include "osl/diagnose.h"
+#include "rtl/ustrbuf.hxx"
 #include "rtl/ustring.hxx"
 #include "sal/types.h"
 
@@ -49,22 +50,88 @@ namespace {
 namespace css = ::com::sun::star;
 
 #if defined SOLAR_JAVA
+int hexDigitValue(sal_Unicode c)
+{
+    if (c >= '0' && c <= '9') {
+        return c - '0';
+    }
+    if (c >= 'A' && c <= 'F') {
+        return c - 'A' + 10;
+    }
+    if (c >= 'a' && c <= 'f') {
+        return c - 'a' + 10;
+    }
+    return -1;
+}
+
+// Undoes one level of %HH escapes, as the JDK's file: handler does before it
+// opens a path.  Only the ASCII separators matter to the caller, so each 
escape
+// simply becomes the code unit of its byte value; a malformed escape is kept.
+::rtl::OUString percentDecoded(::rtl::OUString const & s)
+{
+    sal_Int32 const n = s.getLength();
+    ::rtl::OUStringBuffer buf(n);
+    for (sal_Int32 i = 0; i != n; ++i) {
+        sal_Unicode c = s[i];
+        if (c == '%' && n - i > 2) {
+            int const hi = hexDigitValue(s[i + 1]);
+            int const lo = hexDigitValue(s[i + 2]);
+            if (hi != -1 && lo != -1) {
+                c = static_cast< sal_Unicode >(hi * 16 + lo);
+                i += 2;
+            }
+        }
+        buf.append(c);
+    }
+    return buf.makeStringAndClear();
+}
+
+// Whether the part of a file: URL after the scheme names a path on this
+// machine: the authority, if any, must be empty or localhost, and the path 
must
+// not name another machine by itself.  The path is judged decoded, because the
+// JDK's file: handler percent-decodes it and, on Windows, turns slashes into
+// backslashes before opening it -- a leading escaped slash or backslash would
+// otherwise reach the file system as a reference to a share.
+bool isLocalFileLocation(::rtl::OUString const & afterScheme)
+{
+    ::rtl::OUString rest(afterScheme);
+    if (rest.indexOf('\\') != -1) {
+        return false;
+    }
+    if (rest.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("//"))) {
+        sal_Int32 const end = rest.indexOf('/', 2);
+        ::rtl::OUString const authority(
+            end == -1 ? rest.copy(2) : rest.copy(2, end - 2));
+        if (authority.getLength() != 0
+            && !authority.equalsIgnoreAsciiCaseAsciiL(
+                RTL_CONSTASCII_STRINGPARAM("localhost")))
+        {
+            return false;
+        }
+        rest = end == -1 ? ::rtl::OUString() : rest.copy(end);
+    }
+    rest = percentDecoded(rest);
+    return rest.getLength() >= 2 && rest[0] == '/' && rest[1] != '/'
+        && rest.indexOf('\\') == -1;
+}
+
 // URL schemes that resolve to the local file system or the running JVM image,
-// optionally wrapped in a jar: URL.
+// optionally wrapped in a jar: URL; a file: URL must in addition name a path 
on
+// this machine.
 //
 // com.sun.star.comp.sdbc.Tools enforces the same allow-list on the Java side;
 // keep the two in sync.
 bool isLocalClassPathUrl(::rtl::OUString const & url)
 {
-    return url.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("file:"))
-        || url.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("jrt:"))
-        || url.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("jmod:"))
-        || url.matchIgnoreAsciiCaseAsciiL(
-               RTL_CONSTASCII_STRINGPARAM("jar:file:"))
-        || url.matchIgnoreAsciiCaseAsciiL(
-               RTL_CONSTASCII_STRINGPARAM("jar:jrt:"))
-        || url.matchIgnoreAsciiCaseAsciiL(
-               RTL_CONSTASCII_STRINGPARAM("jar:jmod:"));
+    ::rtl::OUString rest(url);
+    if (rest.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("jar:"))) {
+        rest = rest.copy(RTL_CONSTASCII_LENGTH("jar:"));
+    }
+    if (rest.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("file:"))) {
+        return isLocalFileLocation(rest.copy(RTL_CONSTASCII_LENGTH("file:")));
+    }
+    return rest.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("jrt:"))
+        || 
rest.matchIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("jmod:"));
 }
 #endif
 

Reply via email to