diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2022-09-23 16:08:05 +0200 |
---|---|---|
committer | Stephan Bergmann <sbergman@redhat.com> | 2022-09-23 19:59:36 +0200 |
commit | 8cadfec386d0fb906dbe9b8ea8f918e61927e622 (patch) | |
tree | f9084ba87f2ac22e0e44bd1060ae260378275c96 | |
parent | 4689f7579ac3db998bb0de07b0adbe4092d5be14 (diff) |
Fix utl::splitLastFromConfigurationPath
* Fix regressions introduced with 5edefc801fb48559c8064003f23d22d838710ee4 "use
more string_view in unotools". (Notably, misuses of two-argument std
string_view rfind are something to watch out for, see the commit message of
93e234c45c62af9d57041de676d888f7695ac0e8 "Fix a misuse of two-argument std
string_view rfind" for details.)
* Bring the implementation some more in accordance with the documentation, by
being stricter about handling invalid paths, and making sure to really assign
all of the input _sInPath to the output _rsLocalName in case of an invalid
path.
* Only &...;-decode the names of set elements in ['...'] and ["..."], not
anything else.
Change-Id: If01f4b34af42b0a594994b732d54f26695329286
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140493
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
-rw-r--r-- | unotools/qa/unit/configpaths.cxx | 21 | ||||
-rw-r--r-- | unotools/source/config/configpaths.cxx | 86 |
2 files changed, 56 insertions, 51 deletions
diff --git a/unotools/qa/unit/configpaths.cxx b/unotools/qa/unit/configpaths.cxx index e0a92f37c894..7d9907d9e34d 100644 --- a/unotools/qa/unit/configpaths.cxx +++ b/unotools/qa/unit/configpaths.cxx @@ -47,9 +47,6 @@ public: CPPUNIT_ASSERT_EQUAL(OUString(""), path); CPPUNIT_ASSERT_EQUAL(OUString("foo"), last); } - // TODO: Broken since 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in - // unotools" (expected "" vs. actual "/foo"): - if ((false)) { // Already prior to 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in // unotools", and in discordance with the documentation, this returned true (but @@ -65,9 +62,6 @@ public: CPPUNIT_ASSERT_EQUAL(OUString("/foo/bar"), path); CPPUNIT_ASSERT_EQUAL(OUString("baz"), last); } - // TODO: Broken since 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in - // unotools" (expected "/foo/bar" vs. actual "/foo/bar/baz"): - if ((false)) { // Trailing slash accepted for backwards compatibility (cf // . "for backwards compatibility, ignore a final slash" comment in @@ -77,9 +71,6 @@ public: CPPUNIT_ASSERT_EQUAL(OUString("/foo/bar"), path); CPPUNIT_ASSERT_EQUAL(OUString("baz"), last); } - // TODO: Broken since 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in - // unotools" (expected true vs. actual false return): - if ((false)) { OUString path, last; CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath( @@ -93,18 +84,14 @@ public: CPPUNIT_ASSERT_EQUAL(OUString(""), path); CPPUNIT_ASSERT_EQUAL(OUString("foo"), last); } - // TODO: Broken since 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in - // unotools" (expected false vs. actual true return): - if ((false)) { - // Already prior to 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in - // unotools", and in discordance with the documentation, this set last to "foo" rather - // than "foo/" (but "If <var>_sInPath</var> could not be parsed as a valid configuration - // path, this is set to <var>_sInPath</var>"): + // In accordance with the documentation, this sets last to "foo/" ("If + // <var>_sInPath</var> could not be parsed as a valid configuration path, this is set to + // <var>_sInPath</var>"): OUString path, last; CPPUNIT_ASSERT(!utl::splitLastFromConfigurationPath(u"foo/", path, last)); CPPUNIT_ASSERT_EQUAL(OUString(""), path); - CPPUNIT_ASSERT_EQUAL(OUString("foo"), last); + CPPUNIT_ASSERT_EQUAL(OUString("foo/"), last); } { // Some broken input missing a leading slash happens to be considered OK: diff --git a/unotools/source/config/configpaths.cxx b/unotools/source/config/configpaths.cxx index 4e5002238d93..4c626d96b4e0 100644 --- a/unotools/source/config/configpaths.cxx +++ b/unotools/source/config/configpaths.cxx @@ -19,6 +19,7 @@ #include <sal/config.h> +#include <cassert> #include <string_view> #include <unotools/configpaths.hxx> @@ -78,70 +79,87 @@ bool splitLastFromConfigurationPath(std::u16string_view _sInPath, { size_t nStart,nEnd; - size_t nPos = _sInPath.size()-1; + size_t nPos = _sInPath.size(); - // strip trailing slash - if (nPos != std::u16string_view::npos && nPos > 0 && _sInPath[ nPos ] == '/') + // for backwards compatibility, strip trailing slash + if (nPos > 1 && _sInPath[ nPos - 1 ] == '/') { - OSL_FAIL("Invalid config path: trailing '/' is not allowed"); --nPos; } - // check for predicate ['xxx'] or ["yyy"] - if (nPos != std::u16string_view::npos && nPos > 0 && _sInPath[ nPos ] == ']') + // check for set element ['xxx'] or ["yyy"] + bool decode; + if (nPos > 0 && _sInPath[ nPos - 1 ] == ']') { - sal_Unicode chQuote = _sInPath[--nPos]; + decode = true; + if (nPos < 3) { // expect at least chQuote + chQuote + ']' at _sInPath[nPos-3..nPos-1] + goto invalid; + } + nPos -= 2; + sal_Unicode chQuote = _sInPath[nPos]; if (chQuote == '\'' || chQuote == '\"') { nEnd = nPos; - nPos = _sInPath.find(chQuote,nEnd); + nPos = _sInPath.rfind(chQuote,nEnd - 1); + if (nPos == std::u16string_view::npos) { + goto invalid; + } nStart = nPos + 1; - if (nPos != std::u16string_view::npos) - --nPos; // nPos = rInPath.lastIndexOf('[',nPos); } - else // allow [xxx] + else { - nEnd = nPos + 1; - nPos = _sInPath.rfind('[',nEnd); - nStart = nPos + 1; + goto invalid; } - OSL_ENSURE(nPos != std::u16string_view::npos && _sInPath[nPos] == '[', "Invalid config path: unmatched quotes or brackets"); - if (nPos != std::u16string_view::npos && _sInPath[nPos] == '[') + OSL_ENSURE(nPos > 0 && _sInPath[nPos - 1] == '[', "Invalid config path: unmatched quotes or brackets"); + if (nPos > 1 && _sInPath[nPos - 1] == '[') + // expect at least '/' + '[' at _sInPath[nPos-2..nPos-1] { - nPos = _sInPath.rfind('/',nPos); + nPos = _sInPath.rfind('/',nPos - 2); + if (nPos == std::u16string_view::npos) { + goto invalid; + } } - else // defined behavior for invalid paths + else { - nStart = 0; - nEnd = _sInPath.size(); - nPos = std::u16string_view::npos; + goto invalid; } } else { - nEnd = nPos+1; - nPos = _sInPath.rfind('/',nEnd); + decode = false; + nEnd = nPos; + if (nEnd == 0) { + goto invalid; + } + nPos = _sInPath.rfind('/',nEnd - 1); + if (nPos == std::u16string_view::npos) { + goto invalid; + } nStart = nPos + 1; } - OSL_ASSERT( (nPos == std::u16string_view::npos || - nPos < nStart) && - nStart < nEnd && - nEnd <= _sInPath.size() ); + assert( nPos != std::u16string_view::npos && + nPos < nStart && + nStart <= nEnd && + nEnd <= _sInPath.size() ); - OSL_ASSERT(nPos == std::u16string_view::npos || _sInPath[nPos] == '/'); + assert(_sInPath[nPos] == '/'); OSL_ENSURE(nPos != 0 , "Invalid config child path: immediate child of root"); _rsLocalName = _sInPath.substr(nStart, nEnd-nStart); - if (nPos > 0 && nPos != std::u16string_view::npos) - _rsOutPath = _sInPath.substr(0,nPos); - else - _rsOutPath.clear(); - lcl_resolveCharEntities(_rsLocalName); + _rsOutPath = (nPos > 0) ? OUString(_sInPath.substr(0,nPos)) : OUString(); + if (decode) { + lcl_resolveCharEntities(_rsLocalName); + } + + return true; - return nPos != std::u16string_view::npos; +invalid: + _rsOutPath.clear(); + _rsLocalName = _sInPath; + return false; } OUString extractFirstFromConfigurationPath(OUString const& _sInPath, OUString* _sOutPath) |