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>
28 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
29 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
30 #include <com/sun/star/lang/XServiceInfo.hpp>
31 #include <com/sun/star/uno/Any.hxx>
32 #include <com/sun/star/uno/Exception.hpp>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/uno/RuntimeException.hpp>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <com/sun/star/uno/XComponentContext.hpp>
37 #include <com/sun/star/uno/XInterface.hpp>
38 #include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp>
39 #include <com/sun/star/uri/XUriReference.hpp>
40 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
41 #include <com/sun/star/uri/XUriSchemeParser.hpp>
42 #include <cppuhelper/implbase.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <cppuhelper/weak.hxx>
45 #include <rtl/character.hxx>
46 #include <rtl/ustrbuf.hxx>
47 #include <rtl/ustring.hxx>
48 #include <sal/types.h>
50 #include "UriReference.hxx"
54 bool equalIgnoreEscapeCase(OUString
const & s1
, OUString
const & s2
) {
55 if (s1
.getLength() == s2
.getLength()) {
56 for (sal_Int32 i
= 0; i
< s1
.getLength();) {
57 if (s1
[i
] == '%' && s2
[i
] == '%' && s1
.getLength() - i
> 2
58 && rtl::isAsciiHexDigit(s1
[i
+ 1])
59 && rtl::isAsciiHexDigit(s1
[i
+ 2])
60 && rtl::isAsciiHexDigit(s2
[i
+ 1])
61 && rtl::isAsciiHexDigit(s2
[i
+ 2])
62 && rtl::compareIgnoreAsciiCase(s1
[i
+ 1], s2
[i
+ 1]) == 0
63 && rtl::compareIgnoreAsciiCase(s1
[i
+ 2], s2
[i
+ 2]) == 0)
66 } else if (s1
[i
] != s2
[i
]) {
78 sal_Int32
parseScheme(OUString
const & uriReference
) {
79 if (uriReference
.getLength() >= 2 && rtl::isAsciiAlpha(uriReference
[0])) {
80 for (sal_Int32 i
= 0; i
< uriReference
.getLength(); ++i
) {
81 sal_Unicode c
= uriReference
[i
];
84 } else if (!rtl::isAsciiAlpha(c
) && !rtl::isAsciiDigit(c
)
85 && c
!= '+' && c
!= '-' && c
!= '.')
95 public cppu::WeakImplHelper
<css::uri::XUriReference
>
99 OUString
const & scheme
, bool bIsHierarchical
, bool bHasAuthority
,
100 OUString
const & authority
, OUString
const & path
,
101 bool bHasQuery
, OUString
const & query
):
103 scheme
, bIsHierarchical
, bHasAuthority
, authority
, path
, bHasQuery
,
107 UriReference(const UriReference
&) = delete;
108 UriReference
& operator=(const UriReference
&) = delete;
110 virtual OUString SAL_CALL
getUriReference() override
111 { return m_base
.getUriReference(); }
113 virtual sal_Bool SAL_CALL
isAbsolute() override
114 { return m_base
.isAbsolute(); }
116 virtual OUString SAL_CALL
getScheme() override
117 { return m_base
.getScheme(); }
119 virtual OUString SAL_CALL
getSchemeSpecificPart() override
120 { return m_base
.getSchemeSpecificPart(); }
122 virtual sal_Bool SAL_CALL
isHierarchical() override
123 { return m_base
.isHierarchical(); }
125 virtual sal_Bool SAL_CALL
hasAuthority() override
126 { return m_base
.hasAuthority(); }
128 virtual OUString SAL_CALL
getAuthority() override
129 { return m_base
.getAuthority(); }
131 virtual OUString SAL_CALL
getPath() override
132 { return m_base
.getPath(); }
134 virtual sal_Bool SAL_CALL
hasRelativePath() override
135 { return m_base
.hasRelativePath(); }
137 virtual sal_Int32 SAL_CALL
getPathSegmentCount() override
138 { return m_base
.getPathSegmentCount(); }
140 virtual OUString SAL_CALL
getPathSegment(sal_Int32 index
) override
141 { return m_base
.getPathSegment(index
); }
143 virtual sal_Bool SAL_CALL
hasQuery() override
144 { return m_base
.hasQuery(); }
146 virtual OUString SAL_CALL
getQuery() override
147 { return m_base
.getQuery(); }
149 virtual sal_Bool SAL_CALL
hasFragment() override
150 { return m_base
.hasFragment(); }
152 virtual OUString SAL_CALL
getFragment() override
153 { return m_base
.getFragment(); }
155 virtual void SAL_CALL
setFragment(OUString
const & fragment
) override
156 { m_base
.setFragment(fragment
); }
158 virtual void SAL_CALL
clearFragment() override
159 { m_base
.clearFragment(); }
162 virtual ~UriReference() override
{}
164 stoc::uriproc::UriReference m_base
;
167 css::uno::Reference
< css::uri::XUriReference
> parseGeneric(
168 OUString
const & scheme
, OUString
const & schemeSpecificPart
)
170 bool isAbsolute
= !scheme
.isEmpty();
171 bool isHierarchical
= !isAbsolute
|| schemeSpecificPart
.startsWith("/");
172 bool hasAuthority
= false;
175 bool hasQuery
= false;
177 if (isHierarchical
) {
178 sal_Int32 len
= schemeSpecificPart
.getLength();
180 if (len
- i
>= 2 && schemeSpecificPart
[i
] == '/'
181 && schemeSpecificPart
[i
+ 1] == '/')
185 while (i
< len
&& schemeSpecificPart
[i
] != '/'
186 && schemeSpecificPart
[i
] != '?') {
190 authority
= schemeSpecificPart
.copy(n
, i
- n
);
193 i
= schemeSpecificPart
.indexOf('?', i
);
197 path
= schemeSpecificPart
.copy(n
, i
- n
);
200 query
= schemeSpecificPart
.copy(i
+ 1);
203 if (schemeSpecificPart
.isEmpty()) {
204 // The scheme-specific part of an opaque URI must not be empty:
207 path
= schemeSpecificPart
;
209 return new UriReference(
210 scheme
, isHierarchical
, hasAuthority
, authority
, path
, hasQuery
, query
);
213 typedef std::vector
< sal_Int32
> Segments
;
215 void processSegments(
217 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
218 bool base
, bool processSpecialSegments
)
220 sal_Int32 count
= uriReference
->getPathSegmentCount() - (base
? 1 : 0);
221 assert(count
<= SAL_MAX_INT32
- 1 && -count
>= SAL_MIN_INT32
+ 1);
222 for (sal_Int32 i
= 0; i
< count
; ++i
) {
223 if (processSpecialSegments
) {
224 OUString
segment(uriReference
->getPathSegment(i
));
225 if ( segment
== "." ) {
226 if (!base
&& i
== count
- 1) {
227 segments
.push_back(0);
230 } else if ( segment
== ".." ) {
231 if (segments
.empty() || std::abs(segments
.back()) == 1) {
232 segments
.push_back(base
? -1 : 1);
239 segments
.push_back(base
? -(i
+ 2) : i
+ 2);
244 public cppu::WeakImplHelper
<
245 css::lang::XServiceInfo
, css::uri::XUriReferenceFactory
>
249 css::uno::Reference
< css::uno::XComponentContext
> const & context
):
250 m_context(context
) {}
252 Factory(const Factory
&) = delete;
253 Factory
& operator=(const Factory
&) = delete;
255 virtual OUString SAL_CALL
getImplementationName() override
;
257 virtual sal_Bool SAL_CALL
supportsService(OUString
const & serviceName
) override
;
259 virtual css::uno::Sequence
< OUString
> SAL_CALL
260 getSupportedServiceNames() override
;
262 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
263 parse(OUString
const & uriReference
) override
;
265 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
267 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
268 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
269 sal_Bool processSpecialBaseSegments
,
270 css::uri::RelativeUriExcessParentSegments excessParentSegments
) override
;
272 virtual css::uno::Reference
< css::uri::XUriReference
> SAL_CALL
274 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
275 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
276 sal_Bool preferAuthorityOverRelativePath
,
277 sal_Bool preferAbsoluteOverRelativePath
,
278 sal_Bool encodeRetainedSpecialSegments
) override
;
281 virtual ~Factory() override
{}
283 css::uno::Reference
< css::uri::XUriReference
> clone(
284 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
)
285 { return parse(uriReference
->getUriReference()); }
287 css::uno::Reference
< css::uno::XComponentContext
> m_context
;
290 OUString
Factory::getImplementationName()
292 return OUString("com.sun.star.comp.uri.UriReferenceFactory");
295 sal_Bool
Factory::supportsService(OUString
const & serviceName
)
297 return cppu::supportsService(this, serviceName
);
300 css::uno::Sequence
< OUString
> Factory::getSupportedServiceNames()
302 css::uno::Sequence
< OUString
> s
{ "com.sun.star.uri.UriReferenceFactory" };
306 css::uno::Reference
< css::uri::XUriReference
> Factory::parse(
307 OUString
const & uriReference
)
309 sal_Int32 fragment
= uriReference
.indexOf('#');
310 if (fragment
== -1) {
311 fragment
= uriReference
.getLength();
314 OUString schemeSpecificPart
;
315 OUString serviceName
;
316 sal_Int32 n
= parseScheme(uriReference
);
317 assert(n
< fragment
);
319 scheme
= uriReference
.copy(0, n
);
320 schemeSpecificPart
= uriReference
.copy(n
+ 1, fragment
- (n
+ 1));
322 buf
.append("com.sun.star.uri.UriSchemeParser_");
323 for (sal_Int32 i
= 0; i
< scheme
.getLength(); ++i
) {
324 sal_Unicode c
= scheme
[i
];
325 if (rtl::isAsciiUpperCase(c
)) {
326 buf
.append(static_cast<sal_Unicode
>(rtl::toAsciiLowerCase(c
)));
327 } else if (c
== '+') {
329 } else if (c
== '-') {
330 buf
.append("HYPHEN");
331 } else if (c
== '.') {
334 assert(rtl::isAsciiLowerCase(c
) || rtl::isAsciiDigit(c
));
338 serviceName
= buf
.makeStringAndClear();
340 schemeSpecificPart
= uriReference
.copy(0, fragment
);
342 css::uno::Reference
< css::uri::XUriSchemeParser
> parser
;
343 if (!serviceName
.isEmpty()) {
344 css::uno::Reference
< css::lang::XMultiComponentFactory
> factory(
345 m_context
->getServiceManager());
347 css::uno::Reference
< css::uno::XInterface
> service
;
349 service
= factory
->createInstanceWithContext(
350 serviceName
, m_context
);
351 } catch (css::uno::RuntimeException
&) {
353 } catch (const css::uno::Exception
& e
) {
354 throw css::lang::WrappedTargetRuntimeException(
355 "creating service " + serviceName
,
356 static_cast< cppu::OWeakObject
* >(this),
357 css::uno::makeAny(e
)); //TODO: preserve type of e
360 parser
.set( service
, css::uno::UNO_QUERY_THROW
);
364 css::uno::Reference
< css::uri::XUriReference
> uriRef(
366 ? parser
->parse(scheme
, schemeSpecificPart
)
367 : parseGeneric(scheme
, schemeSpecificPart
));
368 if (uriRef
.is() && fragment
!= uriReference
.getLength()) {
369 uriRef
->setFragment(uriReference
.copy(fragment
+ 1));
374 css::uno::Reference
< css::uri::XUriReference
> Factory::makeAbsolute(
375 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
376 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
377 sal_Bool processSpecialBaseSegments
,
378 css::uri::RelativeUriExcessParentSegments excessParentSegments
)
380 if (!baseUriReference
.is() || !baseUriReference
->isAbsolute()
381 || !baseUriReference
->isHierarchical() || !uriReference
.is()) {
383 } else if (uriReference
->isAbsolute()) {
384 return clone(uriReference
);
385 } else if (!uriReference
->hasAuthority()
386 && uriReference
->getPath().isEmpty()
387 && !uriReference
->hasQuery()) {
388 css::uno::Reference
< css::uri::XUriReference
> abs(
389 clone(baseUriReference
));
390 if (uriReference
->hasFragment()) {
391 abs
->setFragment(uriReference
->getFragment());
393 abs
->clearFragment();
397 OUStringBuffer
abs(baseUriReference
->getScheme());
399 if (uriReference
->hasAuthority()) {
401 abs
.append(uriReference
->getAuthority());
402 } else if (baseUriReference
->hasAuthority()) {
404 abs
.append(baseUriReference
->getAuthority());
406 if (uriReference
->hasRelativePath()) {
409 segments
, baseUriReference
, true, processSpecialBaseSegments
);
410 processSegments(segments
, uriReference
, false, true);
411 // If the path component of the base URI reference is empty (which
412 // implies that the base URI reference denotes a "root entity"), and
413 // the resulting URI reference denotes the same root entity, make
414 // sure the path component of the resulting URI reference is also
415 // empty (and not "/"). RFC 2396 is unclear about this, and I chose
416 // these rules for consistent results.
417 bool slash
= !baseUriReference
->getPath().isEmpty();
421 for (Segments::iterator
i(segments
.begin()); i
!= segments
.end();
426 baseUriReference
->getPathSegment(-(*i
+ 2)));
427 if (!segment
.isEmpty() || segments
.size() > 1) {
436 OUString
segment(uriReference
->getPathSegment(*i
- 2));
437 if (!segment
.isEmpty() || segments
.size() > 1) {
444 } else if (*i
== 0) {
445 if (segments
.size() > 1 && !slash
) {
449 switch (excessParentSegments
) {
450 case css::uri::RelativeUriExcessParentSegments_ERROR
:
453 case css::uri::RelativeUriExcessParentSegments_RETAIN
:
464 case css::uri::RelativeUriExcessParentSegments_REMOVE
:
474 abs
.append(uriReference
->getPath());
476 if (uriReference
->hasQuery()) {
478 abs
.append(uriReference
->getQuery());
480 if (uriReference
->hasFragment()) {
482 abs
.append(uriReference
->getFragment());
484 return parse(abs
.makeStringAndClear());
488 css::uno::Reference
< css::uri::XUriReference
> Factory::makeRelative(
489 css::uno::Reference
< css::uri::XUriReference
> const & baseUriReference
,
490 css::uno::Reference
< css::uri::XUriReference
> const & uriReference
,
491 sal_Bool preferAuthorityOverRelativePath
,
492 sal_Bool preferAbsoluteOverRelativePath
,
493 sal_Bool encodeRetainedSpecialSegments
)
495 if (!baseUriReference
.is() || !baseUriReference
->isAbsolute()
496 || !baseUriReference
->isHierarchical() || !uriReference
.is()) {
498 } else if (!uriReference
->isAbsolute() || !uriReference
->isHierarchical()
499 || !baseUriReference
->getScheme().equalsIgnoreAsciiCase(
500 uriReference
->getScheme())) {
501 return clone(uriReference
);
504 bool omitQuery
= false;
505 if ((baseUriReference
->hasAuthority() != uriReference
->hasAuthority())
506 || !equalIgnoreEscapeCase(
507 baseUriReference
->getAuthority(),
508 uriReference
->getAuthority()))
510 if (uriReference
->hasAuthority()) {
512 rel
.append(uriReference
->getAuthority());
514 rel
.append(uriReference
->getPath());
515 } else if ((equalIgnoreEscapeCase(
516 baseUriReference
->getPath(), uriReference
->getPath())
517 || (baseUriReference
->getPath().getLength() <= 1
518 && uriReference
->getPath().getLength() <= 1))
519 && baseUriReference
->hasQuery() == uriReference
->hasQuery()
520 && equalIgnoreEscapeCase(
521 baseUriReference
->getQuery(), uriReference
->getQuery()))
525 sal_Int32 count1
= std::max
< sal_Int32
>(
526 baseUriReference
->getPathSegmentCount(), 1);
527 sal_Int32 count2
= std::max
< sal_Int32
>(
528 uriReference
->getPathSegmentCount(), 1);
530 for (; i
< std::min(count1
, count2
) - 1; ++i
) {
531 if (!equalIgnoreEscapeCase(
532 baseUriReference
->getPathSegment(i
),
533 uriReference
->getPathSegment(i
)))
538 if (i
== 0 && preferAbsoluteOverRelativePath
539 && (preferAuthorityOverRelativePath
540 || !uriReference
->getPath().startsWith("//")))
542 if (baseUriReference
->getPath().getLength() > 1
543 || uriReference
->getPath().getLength() > 1)
545 if (uriReference
->getPath().isEmpty()) {
548 assert(uriReference
->getPath()[0] == '/');
549 if (uriReference
->getPath().startsWith("//")) {
550 assert(uriReference
->hasAuthority());
552 rel
.append(uriReference
->getAuthority());
554 rel
.append(uriReference
->getPath());
558 bool segments
= false;
559 for (sal_Int32 j
= i
; j
< count1
- 1; ++j
) {
567 || (!uriReference
->getPathSegment(count2
- 1).isEmpty()))
570 && (uriReference
->getPathSegment(i
).isEmpty()
571 || (parseScheme(uriReference
->getPathSegment(i
))
577 for (; i
< count2
; ++i
) {
581 OUString
s(uriReference
->getPathSegment(i
));
582 if (encodeRetainedSpecialSegments
&& s
== ".") {
584 } else if (encodeRetainedSpecialSegments
&& s
== "..") {
585 rel
.append("%2E%2E");
594 if (!omitQuery
&& uriReference
->hasQuery()) {
596 rel
.append(uriReference
->getQuery());
598 if (uriReference
->hasFragment()) {
600 rel
.append(uriReference
->getFragment());
602 return parse(rel
.makeStringAndClear());
608 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
609 com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext
* rxContext
,
610 css::uno::Sequence
<css::uno::Any
> const &)
612 return ::cppu::acquire(new Factory(rxContext
));
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */