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
