diff options
Diffstat (limited to 'stoc/source/uriproc/UriReferenceFactory.cxx')
-rw-r--r-- | stoc/source/uriproc/UriReferenceFactory.cxx | 375 |
1 files changed, 240 insertions, 135 deletions
diff --git a/stoc/source/uriproc/UriReferenceFactory.cxx b/stoc/source/uriproc/UriReferenceFactory.cxx index 779bb36b627b..3a5f30b850de 100644 --- a/stoc/source/uriproc/UriReferenceFactory.cxx +++ b/stoc/source/uriproc/UriReferenceFactory.cxx @@ -21,7 +21,9 @@ #include <algorithm> #include <cassert> -#include <cstdlib> +#include <cstddef> +#include <string_view> +#include <utility> #include <vector> #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> @@ -96,11 +98,11 @@ class UriReference: { public: UriReference( - OUString const & scheme, bool bIsHierarchical, bool bHasAuthority, + OUString const & scheme, bool bHasAuthority, OUString const & authority, OUString const & path, bool bHasQuery, OUString const & query): m_base( - scheme, bIsHierarchical, bHasAuthority, authority, path, bHasQuery, + scheme, bHasAuthority, authority, path, bHasQuery, query) {} @@ -167,75 +169,113 @@ private: css::uno::Reference< css::uri::XUriReference > parseGeneric( OUString const & scheme, OUString const & schemeSpecificPart) { - bool isAbsolute = !scheme.isEmpty(); - bool isHierarchical = !isAbsolute || schemeSpecificPart.startsWith("/"); + sal_Int32 len = schemeSpecificPart.getLength(); + sal_Int32 i = 0; bool hasAuthority = false; OUString authority; - OUString path; - bool hasQuery = false; - OUString query; - if (isHierarchical) { - sal_Int32 len = schemeSpecificPart.getLength(); - sal_Int32 i = 0; - if (len - i >= 2 && schemeSpecificPart[i] == '/' - && schemeSpecificPart[i + 1] == '/') - { - i += 2; - sal_Int32 n = i; - while (i < len && schemeSpecificPart[i] != '/' - && schemeSpecificPart[i] != '?') { - ++i; - } - hasAuthority = true; - authority = schemeSpecificPart.copy(n, i - n); - } + if (len - i >= 2 && schemeSpecificPart[i] == '/' + && schemeSpecificPart[i + 1] == '/') + { + i += 2; sal_Int32 n = i; - i = schemeSpecificPart.indexOf('?', i); - if (i == -1) { - i = len; + while (i < len && schemeSpecificPart[i] != '/' + && schemeSpecificPart[i] != '?') { + ++i; } - path = schemeSpecificPart.copy(n, i - n); - if (i != len) { - hasQuery = true; - query = schemeSpecificPart.copy(i + 1); - } - } else { - if (schemeSpecificPart.isEmpty()) { - // The scheme-specific part of an opaque URI must not be empty: - return nullptr; - } - path = schemeSpecificPart; + hasAuthority = true; + authority = schemeSpecificPart.copy(n, i - n); + } + sal_Int32 n = i; + i = schemeSpecificPart.indexOf('?', i); + if (i == -1) { + i = len; + } + OUString path = schemeSpecificPart.copy(n, i - n); + bool hasQuery = false; + OUString query; + if (i != len) { + hasQuery = true; + query = schemeSpecificPart.copy(i + 1); } return new UriReference( - scheme, isHierarchical, hasAuthority, authority, path, hasQuery, query); + scheme, hasAuthority, authority, path, hasQuery, query); } -void processSegments( - std::vector<sal_Int32> & segments, - css::uno::Reference< css::uri::XUriReference > const & uriReference, - bool base, bool processSpecialSegments) +struct Segment { + bool leadingSlash; + bool excessParent; + std::u16string_view segment; + + Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment): + leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {} +}; + +std::pair<std::vector<Segment>, bool> processSegments( + std::u16string_view first, std::u16string_view second, bool processSpecialSegments) { - sal_Int32 count = uriReference->getPathSegmentCount() - (base ? 1 : 0); - assert(count <= SAL_MAX_INT32 - 1 && -count >= SAL_MIN_INT32 + 1); - for (sal_Int32 i = 0; i < count; ++i) { - if (processSpecialSegments) { - OUString segment(uriReference->getPathSegment(i)); - if ( segment == "." ) { - if (!base && i == count - 1) { - segments.push_back(0); + std::vector<Segment> segments; + bool processed = false; + std::u16string_view const * half = &first; + // later checks for `half == &first` and `half == &second` rely on the fact that `first` and + // `second` are passed by value, in case a caller passes the same object for both arguments + std::size_t index = 0; + bool slash = false; + if (index == half->length()) { + half = &second; + index = 0; + } + if (index != half->length()) { + if ((*half)[index] == u'/') { + slash = true; + ++index; + } + for (;;) { + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index == half->length()) { + if (slash) { + segments.emplace_back(true, false, std::u16string_view()); } - continue; - } else if ( segment == ".." ) { - if (segments.empty() || std::abs(segments.back()) == 1) { - segments.push_back(base ? -1 : 1); - } else { - segments.pop_back(); + break; + } + auto const n = std::min(half->find(u'/', index), half->length()); + auto const leadingSlash = slash; + auto const segment = half->substr(index, n - index); + auto const process = processSpecialSegments || half == &second; + index = n; + slash = false; + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index != half->length() && (*half)[index] == u'/') { + slash = true; + ++index; + } + if (process) { + if (segment == u".") { + slash = leadingSlash; + processed = true; + continue; + } else if (segment == u"..") { + if (segments.empty() || segments.back().excessParent) { + segments.emplace_back(leadingSlash, true, segment); + } else { + if (leadingSlash) { + segments.pop_back(); + } + slash = leadingSlash; + } + processed = true; + continue; } - continue; } + segments.emplace_back(leadingSlash, false, segment); } - segments.push_back(base ? -(i + 2) : i + 2); } + return {segments, processed}; } class Factory: @@ -264,7 +304,7 @@ public: makeAbsolute( css::uno::Reference< css::uri::XUriReference > const & baseUriReference, css::uno::Reference< css::uri::XUriReference > const & uriReference, - sal_Bool processSpecialBaseSegments, + sal_Bool processAdditionalSpecialSegments, css::uri::RelativeUriExcessParentSegments excessParentSegments) override; virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL @@ -373,25 +413,80 @@ css::uno::Reference< css::uri::XUriReference > Factory::parse( css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute( css::uno::Reference< css::uri::XUriReference > const & baseUriReference, css::uno::Reference< css::uri::XUriReference > const & uriReference, - sal_Bool processSpecialBaseSegments, + sal_Bool processAdditionalSpecialSegments, css::uri::RelativeUriExcessParentSegments excessParentSegments) { if (!baseUriReference.is() || !baseUriReference->isAbsolute() - || !baseUriReference->isHierarchical() || !uriReference.is()) { + || !uriReference.is()) { return nullptr; } else if (uriReference->isAbsolute()) { + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + OUStringBuffer abs(uriReference->getScheme()); + abs.append(':'); + if (uriReference->hasAuthority()) { + abs.append("//"); + abs.append(uriReference->getAuthority()); + } + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + if (uriReference->hasQuery()) { + abs.append('?'); + abs.append(uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append('#'); + abs.append(uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } + } return clone(uriReference); } else if (!uriReference->hasAuthority() - && uriReference->getPath().isEmpty() - && !uriReference->hasQuery()) { - css::uno::Reference< css::uri::XUriReference > abs( - clone(baseUriReference)); + && uriReference->getPath().isEmpty()) { + OUStringBuffer abs(baseUriReference->getScheme()); + abs.append(':'); + if (baseUriReference->hasAuthority()) { + abs.append("//"); + abs.append(baseUriReference->getAuthority()); + } + abs.append(baseUriReference->getPath()); + if (uriReference->hasQuery()) { + abs.append('?'); + abs.append(uriReference->getQuery()); + } else if (baseUriReference->hasQuery()) { + abs.append('?'); + abs.append(baseUriReference->getQuery()); + } if (uriReference->hasFragment()) { - abs->setFragment(uriReference->getFragment()); - } else { - abs->clearFragment(); + abs.append('#'); + abs.append(uriReference->getFragment()); } - return abs; + return parse(abs.makeStringAndClear()); } else { OUStringBuffer abs(baseUriReference->getScheme()); abs.append(':'); @@ -403,73 +498,77 @@ css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute( abs.append(baseUriReference->getAuthority()); } if (uriReference->hasRelativePath()) { - std::vector<sal_Int32> segments; - processSegments( - segments, baseUriReference, true, processSpecialBaseSegments); - processSegments(segments, uriReference, false, true); - // If the path component of the base URI reference is empty (which - // implies that the base URI reference denotes a "root entity"), and - // the resulting URI reference denotes the same root entity, make - // sure the path component of the resulting URI reference is also - // empty (and not "/"). RFC 2396 is unclear about this, and I chose - // these rules for consistent results. - bool slash = !baseUriReference->getPath().isEmpty(); - if (slash) { - abs.append('/'); + auto path1 = baseUriReference->getPath(); + if (path1.isEmpty()) { + if (baseUriReference->hasAuthority()) { + path1 = "/"; + } + } else { + path1 = path1.copy(0, path1.lastIndexOf('/') + 1); } - for (const auto& i : segments) + auto const path2 = uriReference->getPath(); + auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments); + (void)_; + for (auto const & i : segments) { - if (i < -1) { - OUString segment( - baseUriReference->getPathSegment(-(i + 2))); - if (!segment.isEmpty() || segments.size() > 1) { - if (!slash) { - abs.append('/'); - } - abs.append(segment); - slash = true; - abs.append('/'); - } - } else if (i > 1) { - OUString segment(uriReference->getPathSegment(i - 2)); - if (!segment.isEmpty() || segments.size() > 1) { - if (!slash) { - abs.append('/'); - } - abs.append(segment); - slash = false; - } - } else if (i == 0) { - if (segments.size() > 1 && !slash) { - abs.append('/'); - } - } else { + if (i.excessParent) { switch (excessParentSegments) { case css::uri::RelativeUriExcessParentSegments_ERROR: return nullptr; case css::uri::RelativeUriExcessParentSegments_RETAIN: - if (!slash) { - abs.append('/'); - } - abs.append(".."); - slash = i < 0; - if (slash) { - abs.append('/'); - } + assert(i.segment == u".."); break; case css::uri::RelativeUriExcessParentSegments_REMOVE: - break; + continue; default: assert(false); break; } } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); } } else { - abs.append(uriReference->getPath()); + bool processed = false; + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + processed = true; + } + } + if (!processed) { + abs.append(uriReference->getPath()); + } } if (uriReference->hasQuery()) { abs.append('?'); @@ -491,9 +590,9 @@ css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( sal_Bool encodeRetainedSpecialSegments) { if (!baseUriReference.is() || !baseUriReference->isAbsolute() - || !baseUriReference->isHierarchical() || !uriReference.is()) { + || !uriReference.is()) { return nullptr; - } else if (!uriReference->isAbsolute() || !uriReference->isHierarchical() + } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath() || !baseUriReference->getScheme().equalsIgnoreAsciiCase( uriReference->getScheme())) { return clone(uriReference); @@ -512,8 +611,8 @@ css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( rel.append(uriReference->getPath()); } else if ((equalIgnoreEscapeCase( baseUriReference->getPath(), uriReference->getPath()) - || (baseUriReference->getPath().getLength() <= 1 - && uriReference->getPath().getLength() <= 1)) + || (baseUriReference->getPath() == "/" + && uriReference->getPath().isEmpty())) && baseUriReference->hasQuery() == uriReference->hasQuery() && equalIgnoreEscapeCase( baseUriReference->getQuery(), uriReference->getQuery())) @@ -533,24 +632,30 @@ css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( break; } } - if (i == 0 && preferAbsoluteOverRelativePath + if (i == 0 + && (preferAbsoluteOverRelativePath || uriReference->hasQuery()) && (preferAuthorityOverRelativePath || !uriReference->getPath().startsWith("//"))) { - if (baseUriReference->getPath().getLength() > 1 - || uriReference->getPath().getLength() > 1) - { - if (uriReference->getPath().isEmpty()) { + if (uriReference->getPath().isEmpty()) { + if (!baseUriReference->getPath().isEmpty() + && baseUriReference->getPath() != "/") + { rel.append('/'); - } else { - assert(uriReference->getPath()[0] == '/'); - if (uriReference->getPath().startsWith("//")) { - assert(uriReference->hasAuthority()); - rel.append("//"); - rel.append(uriReference->getAuthority()); - } - rel.append(uriReference->getPath()); } + } else if (uriReference->getPath() == "/") { + if (baseUriReference->getPath().isEmpty() + || baseUriReference->getPath() != "/") + { + rel.append('/'); + } + } else { + if (uriReference->getPath().startsWith("//")) { + assert(uriReference->hasAuthority()); + rel.append("//"); + rel.append(uriReference->getAuthority()); + } + rel.append(uriReference->getPath()); } } else { bool segments = false; |