summaryrefslogtreecommitdiff
path: root/stoc/source/uriproc/UriReferenceFactory.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'stoc/source/uriproc/UriReferenceFactory.cxx')
-rw-r--r--stoc/source/uriproc/UriReferenceFactory.cxx375
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;