1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
25 #include <string_view>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
30 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/uno/Any.hxx>
33 #include <com/sun/star/uno/Exception.hpp>
34 #include <com/sun/star/uno/Reference.hxx>
35 #include <com/sun/star/uno/RuntimeException.hpp>
36 #include <com/sun/star/uno/Sequence.hxx>
37 #include <com/sun/star/uno/XComponentContext.hpp>
38 #include <com/sun/star/uno/XInterface.hpp>
39 #include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp>
40 #include <com/sun/star/uri/XUriReference.hpp>
41 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
42 #include <com/sun/star/uri/XUriSchemeParser.hpp>
43 #include <cppuhelper/exc_hlp.hxx>
44 #include <cppuhelper/implbase.hxx>
45 #include <cppuhelper/supportsservice.hxx>
46 #include <cppuhelper/weak.hxx>
47 #include <rtl/character.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <rtl/ustring.hxx>
50 #include <sal/types.h>
52 #include "UriReference.hxx"
56 bool equalIgnoreEscapeCase(std::u16string_view s1
, std::u16string_view s2
) {
57 if (s1
.size() == s2
.size()) {
58 for (size_t i
= 0; i
< s1
.size();) {
59 if (s1
[i
] == '%' && s2
[i
] == '%' && s1
.size() - i
> 2
60 && rtl::isAsciiHexDigit(s1
[i
+ 1])
61 && rtl::isAsciiHexDigit(s1
[i
+ 2])
62 && rtl::isAsciiHexDigit(s2
[i
+ 1])
63 && rtl::isAsciiHexDigit(s2
[i
+ 2])
64 && rtl::compareIgnoreAsciiCase(s1
[i
+ 1], s2
[i
+ 1]) == 0
65 && rtl::compareIgnoreAsciiCase(s1
[i
+ 2], s2
[i
+ 2]) == 0)
68 } else if (s1
[i
] != s2
[i
]) {
80 sal_Int32
parseScheme(std::u16string_view uriReference
) {
81 if (uriReference
.size() >= 2 && rtl::isAsciiAlpha(uriReference
[0])) {
82 for (size_t i
= 0; i
< uriReference
.size(); ++i
) {
83 sal_Unicode c
= uriReference
[i
];
86 } else if (!rtl::isAsciiAlpha(c
) && !rtl::isAsciiDigit(c
)
87 && c
!= '+' && c
!= '-' && c
!= '.')
97 public cppu::WeakImplHelper
<css::uri::XUriReference
>
101 OUString
const & scheme
, bool bHasAuthority
,
102 OUString
const & authority
, OUString
const & path
,
103 bool bHasQuery
, OUString
const & query
):
105 scheme
, bHasAuthority
, authority
, path
, bHasQuery
,
109 UriReference(const UriReference
&) = delete;
110 UriReference
& operator=(const UriReference
&) = delete;
112 virtual OUString SAL_CALL
getUriReference() override
113 { return m_base
.getUriReference(); }
115 virtual sal_Bool SAL_CALL
isAbsolute() override
116 { return m_base
.isAbsolute(); }
118 virtual OUString SAL_CALL
getScheme() override
119 { return m_base
.getScheme(); }
121 virtual OUString SAL_CALL
getSchemeSpecificPart() override
122 { return m_base
.getSchemeSpecificPart(); }
124 virtual sal_Bool SAL_CALL
isHierarchical() override
125 { return m_base
.isHierarchical(); }
127 virtual sal_Bool SAL_CALL
hasAuthority() override
128 { return m_base
.hasAuthority(); }
130 virtual OUString SAL_CALL
getAuthority() override
131 { return m_base
.getAuthority(); }
133 virtual OUString SAL_CALL
getPath() override
134 { return m_base
.getPath(); }
136 virtual sal_Bool SAL_CALL
hasRelativePath() override
137 { return m_base
.hasRelativePath(); }
139 virtual sal_Int32 SAL_CALL
getPathSegmentCount() override
140 { return m_base
.getPathSegmentCount(); }
142 virtual OUString SAL_CALL
getPathSegment(sal_Int32 index
) override
143 { return m_base
.getPathSegment(index
); }
145 virtual sal_Bool SAL_CALL
hasQuery() override
146 { return m_base
.hasQuery(); }
148 virtual OUString SAL_CALL
getQuery() override
149 { return m_base
.getQuery(); }
151 virtual sal_Bool SAL_CALL
hasFragment() override
152 { return m_base
.hasFragment(); }
154 virtual OUString SAL_CALL
getFragment() override
155 { return m_base
.getFragment(); }
157 virtual void SAL_CALL
setFragment(OUString
const & fragment
) override
158 { m_base
.setFragment(fragment
); }
160 virtual void SAL_CALL
clearFragment() override
161 { m_base
.clearFragment(); }
164 virtual ~UriReference() override
{}
166 stoc::uriproc::UriReference m_base
;
169 css::uno::Reference
< css::uri::XUriReference
> parseGeneric(
170 OUString
const & scheme
, std::u16string_view schemeSpecificPart
)
172 size_t len
= schemeSpecificPart
.size();
174 bool hasAuthority
= false;
176 if (len
- i
>= 2 && schemeSpecificPart
[i
] == '/'
177 && schemeSpecificPart
[i
+ 1] == '/')
181 while (i
< len
&& schemeSpecificPart
[i
] != '/'
182 && schemeSpecificPart
[i
] != '?') {
186 authority
= schemeSpecificPart
.substr(n
, i
- n
);
189 i
= schemeSpecificPart
.find('?', i
);
190 if (i
== std::u16string_view::npos
) {
193 OUString
path( schemeSpecificPart
.substr(n
, i
- n
) );
194 bool hasQuery
= false;
198 query
= schemeSpecificPart
.substr(i
+ 1);
200 return new UriReference(
201 scheme
, hasAuthority
, authority
, path
, hasQuery
, query
);
207 std::u16string_view segment
;
209 Segment(bool theLeadingSlash
, bool theExcessParent
, std::u16string_view theSegment
):
210 leadingSlash(theLeadingSlash
), excessParent(theExcessParent
), segment(theSegment
) {}
213 std::pair
<std::vector
<Segment
>, bool> processSegments(
214 std::u16string_view first
, std::u16string_view second
, bool processSpecialSegments
)
216 std::vector
<Segment
> segments
;
217 bool processed
= false;
218 std::u16string_view
const * half
= &first
;
219 // later checks for `half == &first` and `half == &second` rely on the fact that `first` and
220 // `second` are passed by value, in case a caller passes the same object for both arguments
221 std::size_t index
= 0;
223 if (index
== half
->length()) {
227 if (index
!= half
->length()) {
228 if ((*half
)[index
] == u
'/') {
233 if (index
== half
->length() && half
== &first
) {
237 if (index
== half
->length()) {
239 segments
.emplace_back(true, false, std::u16string_view());
243 auto const n
= std::min(half
->find(u
'/', index
), half
->length());
244 auto const leadingSlash
= slash
;
245 auto const segment
= half
->substr(index
, n
- index
);
246 auto const process
= processSpecialSegments
|| half
== &second
;
249 if (index
== half
->length() && half
== &first
) {
253 if (index
!= half
->length() && (*half
)[index
] == u
'/') {
258 if (segment
== u
".") {
259 slash
= leadingSlash
;
262 } else if (segment
== u
"..") {
263 if (segments
.empty() || segments
.back().excessParent
) {
264 segments
.emplace_back(leadingSlash
, true, segment
);
269 slash
= leadingSlash
;
275 segments
.emplace_back(leadingSlash
, false, segment
);
278 return {segments
, processed
};
282 public cppu::WeakImplHelper
<
283 css::lang::XServiceInfo
, css::uri::XUriReferenceFactory
>
287 css::uno::Reference
< css::uno::XComponentContext
> context
):
288 m_context(std::move(context
)) {}
290 Factory(const Factory
&) = delete;
291 Factory
& operator=(const Factory
&) = delete;
293 virtual OUString SAL_CALL
getImplementationName() override
;
295 virtual sal_Bool SAL_CALL
supportsService(OUString
const & serviceName
) override
;
297 virtual css::uno::Sequence
< OUString
> SAL_CALL
298 getSupportedServiceNames() override
;
300 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
301 parse(OUString
const & uriReference
) override
;
303 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
305 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
306 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
307 sal_Bool processAdditionalSpecialSegments
,
308 css::uri::RelativeUriExcessParentSegments excessParentSegments
) override
;
310 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
312 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
313 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
314 sal_Bool preferAuthorityOverRelativePath
,
315 sal_Bool preferAbsoluteOverRelativePath
,
316 sal_Bool encodeRetainedSpecialSegments
) override
;
319 virtual ~Factory() override
{}
321 css::uno::Reference
< css::uri::XUriReference
> clone(
322 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
)
323 { return parse(uriReference
->getUriReference()); }
325 css::uno::Reference
< css::uno::XComponentContext
> m_context
;
328 OUString
Factory::getImplementationName()
330 return u
"com.sun.star.comp.uri.UriReferenceFactory"_ustr
;
333 sal_Bool
Factory::supportsService(OUString
const & serviceName
)
335 return cppu::supportsService(this, serviceName
);
338 css::uno::Sequence
< OUString
> Factory::getSupportedServiceNames()
340 css::uno::Sequence
< OUString
> s
{ u
"com.sun.star.uri.UriReferenceFactory"_ustr
};
344 css::uno::Reference
< css::uri::XUriReference
> Factory::parse(
345 OUString
const & uriReference
)
347 sal_Int32 fragment
= uriReference
.indexOf('#');
348 if (fragment
== -1) {
349 fragment
= uriReference
.getLength();
352 OUString schemeSpecificPart
;
353 OUString serviceName
;
354 sal_Int32 n
= parseScheme(uriReference
);
355 assert(n
< fragment
);
357 scheme
= uriReference
.copy(0, n
);
358 schemeSpecificPart
= uriReference
.copy(n
+ 1, fragment
- (n
+ 1));
359 OUStringBuffer
buf(128);
360 buf
.append("com.sun.star.uri.UriSchemeParser_");
361 for (sal_Int32 i
= 0; i
< scheme
.getLength(); ++i
) {
362 sal_Unicode c
= scheme
[i
];
363 if (rtl::isAsciiUpperCase(c
)) {
364 buf
.append(static_cast<sal_Unicode
>(rtl::toAsciiLowerCase(c
)));
365 } else if (c
== '+') {
367 } else if (c
== '-') {
368 buf
.append("HYPHEN");
369 } else if (c
== '.') {
372 assert(rtl::isAsciiLowerCase(c
) || rtl::isAsciiDigit(c
));
376 serviceName
= buf
.makeStringAndClear();
378 schemeSpecificPart
= uriReference
.copy(0, fragment
);
380 css::uno::Reference
< css::uri::XUriSchemeParser
> parser
;
381 if (!serviceName
.isEmpty()) {
382 css::uno::Reference
< css::lang::XMultiComponentFactory
> factory(
383 m_context
->getServiceManager());
385 css::uno::Reference
< css::uno::XInterface
> service
;
387 service
= factory
->createInstanceWithContext(
388 serviceName
, m_context
);
389 } catch (css::uno::RuntimeException
&) {
391 } catch (const css::uno::Exception
&) {
392 css::uno::Any anyEx
= cppu::getCaughtException();
393 throw css::lang::WrappedTargetRuntimeException(
394 "creating service " + serviceName
,
399 parser
.set( service
, css::uno::UNO_QUERY_THROW
);
403 css::uno::Reference
< css::uri::XUriReference
> uriRef(
405 ? parser
->parse(scheme
, schemeSpecificPart
)
406 : parseGeneric(scheme
, schemeSpecificPart
));
407 if (uriRef
.is() && fragment
!= uriReference
.getLength()) {
408 uriRef
->setFragment(uriReference
.copy(fragment
+ 1));
413 css::uno::Reference
< css::uri::XUriReference
> Factory::makeAbsolute(
414 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
415 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
416 sal_Bool processAdditionalSpecialSegments
,
417 css::uri::RelativeUriExcessParentSegments excessParentSegments
)
419 if (!baseUriReference
.is() || !baseUriReference
->isAbsolute()
420 || !uriReference
.is()) {
422 } else if (uriReference
->isAbsolute()) {
423 if (processAdditionalSpecialSegments
) {
424 auto const path
= uriReference
->getPath();
425 auto [segments
, proc
] = processSegments(path
, {}, true);
427 OUStringBuffer
abs(uriReference
->getScheme() + ":");
428 if (uriReference
->hasAuthority()) {
429 abs
.append("//" + uriReference
->getAuthority());
431 for (auto const & i
: segments
)
433 if (i
.excessParent
) {
434 switch (excessParentSegments
) {
435 case css::uri::RelativeUriExcessParentSegments_ERROR
:
438 case css::uri::RelativeUriExcessParentSegments_RETAIN
:
439 assert(i
.segment
== u
"..");
442 case css::uri::RelativeUriExcessParentSegments_REMOVE
:
450 if (i
.leadingSlash
) {
453 abs
.append(i
.segment
);
455 if (uriReference
->hasQuery()) {
456 abs
.append("?" + uriReference
->getQuery());
458 if (uriReference
->hasFragment()) {
459 abs
.append("#" + uriReference
->getFragment());
461 return parse(abs
.makeStringAndClear());
464 return clone(uriReference
);
465 } else if (!uriReference
->hasAuthority()
466 && uriReference
->getPath().isEmpty()) {
467 OUStringBuffer
abs(baseUriReference
->getScheme() + ":");
468 if (baseUriReference
->hasAuthority()) {
469 abs
.append("//" + baseUriReference
->getAuthority());
471 abs
.append(baseUriReference
->getPath());
472 if (uriReference
->hasQuery()) {
473 abs
.append("?" + uriReference
->getQuery());
474 } else if (baseUriReference
->hasQuery()) {
475 abs
.append("?" + baseUriReference
->getQuery());
477 if (uriReference
->hasFragment()) {
478 abs
.append("#" + uriReference
->getFragment());
480 return parse(abs
.makeStringAndClear());
482 OUStringBuffer
abs(128);
483 abs
.append(baseUriReference
->getScheme() + ":");
484 if (uriReference
->hasAuthority()) {
485 abs
.append("//" + uriReference
->getAuthority());
486 } else if (baseUriReference
->hasAuthority()) {
487 abs
.append("//" + baseUriReference
->getAuthority());
489 if (uriReference
->hasRelativePath()) {
490 auto path1
= baseUriReference
->getPath();
491 if (path1
.isEmpty()) {
492 if (baseUriReference
->hasAuthority()) {
496 path1
= path1
.copy(0, path1
.lastIndexOf('/') + 1);
498 auto const path2
= uriReference
->getPath();
499 auto [segments
, _
] = processSegments(path1
, path2
, processAdditionalSpecialSegments
);
501 for (auto const & i
: segments
)
503 if (i
.excessParent
) {
504 switch (excessParentSegments
) {
505 case css::uri::RelativeUriExcessParentSegments_ERROR
:
508 case css::uri::RelativeUriExcessParentSegments_RETAIN
:
509 assert(i
.segment
== u
"..");
512 case css::uri::RelativeUriExcessParentSegments_REMOVE
:
520 if (i
.leadingSlash
) {
523 abs
.append(i
.segment
);
526 bool processed
= false;
527 if (processAdditionalSpecialSegments
) {
528 auto const path
= uriReference
->getPath();
529 auto [segments
, proc
] = processSegments(path
, {}, true);
531 for (auto const & i
: segments
)
533 if (i
.excessParent
) {
534 switch (excessParentSegments
) {
535 case css::uri::RelativeUriExcessParentSegments_ERROR
:
538 case css::uri::RelativeUriExcessParentSegments_RETAIN
:
539 assert(i
.segment
== u
"..");
542 case css::uri::RelativeUriExcessParentSegments_REMOVE
:
550 if (i
.leadingSlash
) {
553 abs
.append(i
.segment
);
559 abs
.append(uriReference
->getPath());
562 if (uriReference
->hasQuery()) {
563 abs
.append("?" + uriReference
->getQuery());
565 if (uriReference
->hasFragment()) {
566 abs
.append("#" + uriReference
->getFragment());
568 return parse(abs
.makeStringAndClear());
572 css::uno::Reference
< css::uri::XUriReference
> Factory::makeRelative(
573 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
574 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
575 sal_Bool preferAuthorityOverRelativePath
,
576 sal_Bool preferAbsoluteOverRelativePath
,
577 sal_Bool encodeRetainedSpecialSegments
)
579 if (!baseUriReference
.is() || !baseUriReference
->isAbsolute()
580 || !uriReference
.is()) {
582 } else if (!uriReference
->isAbsolute() || uriReference
->hasRelativePath()
583 || !baseUriReference
->getScheme().equalsIgnoreAsciiCase(
584 uriReference
->getScheme())) {
585 return clone(uriReference
);
587 OUStringBuffer
rel(128);
588 bool omitQuery
= false;
589 if ((baseUriReference
->hasAuthority() != uriReference
->hasAuthority())
590 || !equalIgnoreEscapeCase(
591 baseUriReference
->getAuthority(),
592 uriReference
->getAuthority()))
594 if (uriReference
->hasAuthority()) {
595 rel
.append("//" + uriReference
->getAuthority());
597 rel
.append(uriReference
->getPath());
598 } else if ((equalIgnoreEscapeCase(
599 baseUriReference
->getPath(), uriReference
->getPath())
600 || (baseUriReference
->getPath() == "/"
601 && uriReference
->getPath().isEmpty()))
602 && baseUriReference
->hasQuery() == uriReference
->hasQuery()
603 && equalIgnoreEscapeCase(
604 baseUriReference
->getQuery(), uriReference
->getQuery()))
608 sal_Int32 count1
= std::max
< sal_Int32
>(
609 baseUriReference
->getPathSegmentCount(), 1);
610 sal_Int32 count2
= std::max
< sal_Int32
>(
611 uriReference
->getPathSegmentCount(), 1);
613 for (; i
< std::min(count1
, count2
) - 1; ++i
) {
614 if (!equalIgnoreEscapeCase(
615 baseUriReference
->getPathSegment(i
),
616 uriReference
->getPathSegment(i
)))
622 && (preferAbsoluteOverRelativePath
|| uriReference
->hasQuery())
623 && (preferAuthorityOverRelativePath
624 || !uriReference
->getPath().startsWith("//")))
626 if (uriReference
->getPath().isEmpty()) {
627 if (!baseUriReference
->getPath().isEmpty()
628 && baseUriReference
->getPath() != "/")
632 } else if (uriReference
->getPath() == "/") {
633 if (baseUriReference
->getPath().isEmpty()
634 || baseUriReference
->getPath() != "/")
639 if (uriReference
->getPath().startsWith("//")) {
640 assert(uriReference
->hasAuthority());
641 rel
.append("//" + uriReference
->getAuthority());
643 rel
.append(uriReference
->getPath());
646 bool segments
= false;
647 for (sal_Int32 j
= i
; j
< count1
- 1; ++j
) {
655 || (!uriReference
->getPathSegment(count2
- 1).isEmpty()))
658 && (uriReference
->getPathSegment(i
).isEmpty()
659 || (parseScheme(uriReference
->getPathSegment(i
))
665 for (; i
< count2
; ++i
) {
669 OUString
s(uriReference
->getPathSegment(i
));
670 if (encodeRetainedSpecialSegments
&& s
== ".") {
672 } else if (encodeRetainedSpecialSegments
&& s
== "..") {
673 rel
.append("%2E%2E");
682 if (!omitQuery
&& uriReference
->hasQuery()) {
683 rel
.append("?" + uriReference
->getQuery());
685 if (uriReference
->hasFragment()) {
686 rel
.append("#" + uriReference
->getFragment());
688 return parse(rel
.makeStringAndClear());
694 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
695 com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext
* rxContext
,
696 css::uno::Sequence
<css::uno::Any
> const &)
698 return ::cppu::acquire(new Factory(rxContext
));
701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */