1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <algorithm>
23 #include <cassert>
24 #include <cstddef>
25 #include <string_view>
26 #include <utility>
27 #include <vector>
28 
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>
51 
52 #include "UriReference.hxx"
53 
54 namespace {
55 
equalIgnoreEscapeCase(OUString const & s1,OUString const & s2)56 bool equalIgnoreEscapeCase(OUString const & s1, OUString const & s2) {
57     if (s1.getLength() == s2.getLength()) {
58         for (sal_Int32 i = 0; i < s1.getLength();) {
59             if (s1[i] == '%' && s2[i] == '%' && s1.getLength() - 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)
66             {
67                 i += 3;
68             } else if (s1[i] != s2[i]) {
69                 return false;
70             } else {
71                 ++i;
72             }
73         }
74         return true;
75     } else {
76         return false;
77     }
78 }
79 
parseScheme(OUString const & uriReference)80 sal_Int32 parseScheme(OUString const & uriReference) {
81     if (uriReference.getLength() >= 2 && rtl::isAsciiAlpha(uriReference[0])) {
82         for (sal_Int32 i = 0; i < uriReference.getLength(); ++i) {
83             sal_Unicode c = uriReference[i];
84             if (c == ':') {
85                 return i;
86             } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c)
87                        && c != '+' && c != '-' && c != '.')
88             {
89                 break;
90             }
91         }
92     }
93     return -1;
94 }
95 
96 class UriReference:
97     public cppu::WeakImplHelper<css::uri::XUriReference>
98 {
99 public:
UriReference(OUString const & scheme,bool bHasAuthority,OUString const & authority,OUString const & path,bool bHasQuery,OUString const & query)100     UriReference(
101         OUString const & scheme, bool bHasAuthority,
102         OUString const & authority, OUString const & path,
103         bool bHasQuery, OUString const & query):
104         m_base(
105             scheme, bHasAuthority, authority, path, bHasQuery,
106             query)
107     {}
108 
109     UriReference(const UriReference&) = delete;
110     UriReference& operator=(const UriReference&) = delete;
111 
getUriReference()112     virtual OUString SAL_CALL getUriReference() override
113     { return m_base.getUriReference(); }
114 
isAbsolute()115     virtual sal_Bool SAL_CALL isAbsolute() override
116     { return m_base.isAbsolute(); }
117 
getScheme()118     virtual OUString SAL_CALL getScheme() override
119     { return m_base.getScheme(); }
120 
getSchemeSpecificPart()121     virtual OUString SAL_CALL getSchemeSpecificPart() override
122     { return m_base.getSchemeSpecificPart(); }
123 
isHierarchical()124     virtual sal_Bool SAL_CALL isHierarchical() override
125     { return m_base.isHierarchical(); }
126 
hasAuthority()127     virtual sal_Bool SAL_CALL hasAuthority() override
128     { return m_base.hasAuthority(); }
129 
getAuthority()130     virtual OUString SAL_CALL getAuthority() override
131     { return m_base.getAuthority(); }
132 
getPath()133     virtual OUString SAL_CALL getPath() override
134     { return m_base.getPath(); }
135 
hasRelativePath()136     virtual sal_Bool SAL_CALL hasRelativePath() override
137     { return m_base.hasRelativePath(); }
138 
getPathSegmentCount()139     virtual sal_Int32 SAL_CALL getPathSegmentCount() override
140     { return m_base.getPathSegmentCount(); }
141 
getPathSegment(sal_Int32 index)142     virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override
143     { return m_base.getPathSegment(index); }
144 
hasQuery()145     virtual sal_Bool SAL_CALL hasQuery() override
146     { return m_base.hasQuery(); }
147 
getQuery()148     virtual OUString SAL_CALL getQuery() override
149     { return m_base.getQuery(); }
150 
hasFragment()151     virtual sal_Bool SAL_CALL hasFragment() override
152     { return m_base.hasFragment(); }
153 
getFragment()154     virtual OUString SAL_CALL getFragment() override
155     { return m_base.getFragment(); }
156 
setFragment(OUString const & fragment)157     virtual void SAL_CALL setFragment(OUString const & fragment) override
158     { m_base.setFragment(fragment); }
159 
clearFragment()160     virtual void SAL_CALL clearFragment() override
161     { m_base.clearFragment(); }
162 
163 private:
~UriReference()164     virtual ~UriReference() override {}
165 
166     stoc::uriproc::UriReference m_base;
167 };
168 
parseGeneric(OUString const & scheme,OUString const & schemeSpecificPart)169 css::uno::Reference< css::uri::XUriReference > parseGeneric(
170     OUString const & scheme, OUString const & schemeSpecificPart)
171 {
172     sal_Int32 len = schemeSpecificPart.getLength();
173     sal_Int32 i = 0;
174     bool hasAuthority = false;
175     OUString authority;
176     if (len - i >= 2 && schemeSpecificPart[i] == '/'
177         && schemeSpecificPart[i + 1] == '/')
178     {
179         i += 2;
180         sal_Int32 n = i;
181         while (i < len && schemeSpecificPart[i] != '/'
182                && schemeSpecificPart[i] != '?') {
183             ++i;
184         }
185         hasAuthority = true;
186         authority = schemeSpecificPart.copy(n, i - n);
187     }
188     sal_Int32 n = i;
189     i = schemeSpecificPart.indexOf('?', i);
190     if (i == -1) {
191         i = len;
192     }
193     OUString path = schemeSpecificPart.copy(n, i - n);
194     bool hasQuery = false;
195     OUString query;
196     if (i != len) {
197         hasQuery = true;
198         query = schemeSpecificPart.copy(i + 1);
199     }
200     return new UriReference(
201         scheme, hasAuthority, authority, path, hasQuery, query);
202 }
203 
204 struct Segment {
205     bool leadingSlash;
206     bool excessParent;
207     std::u16string_view segment;
208 
Segment__anonfbf9f3780111::Segment209     Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment):
210         leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {}
211 };
212 
processSegments(std::u16string_view first,std::u16string_view second,bool processSpecialSegments)213 std::pair<std::vector<Segment>, bool> processSegments(
214     std::u16string_view first, std::u16string_view second, bool processSpecialSegments)
215 {
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;
222     bool slash = false;
223     if (index == half->length()) {
224         half = &second;
225         index = 0;
226     }
227     if (index != half->length()) {
228         if ((*half)[index] == u'/') {
229             slash = true;
230             ++index;
231         }
232         for (;;) {
233             if (index == half->length() && half == &first) {
234                 half = &second;
235                 index = 0;
236             }
237             if (index == half->length()) {
238                 if (slash) {
239                     segments.emplace_back(true, false, std::u16string_view());
240                 }
241                 break;
242             }
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;
247             index = n;
248             slash = false;
249             if (index == half->length() && half == &first) {
250                 half = &second;
251                 index = 0;
252             }
253             if (index != half->length() && (*half)[index] == u'/') {
254                 slash = true;
255                 ++index;
256             }
257             if (process) {
258                 if (segment == u".") {
259                     slash = leadingSlash;
260                     processed = true;
261                     continue;
262                 } else if (segment == u"..") {
263                     if (segments.empty() || segments.back().excessParent) {
264                         segments.emplace_back(leadingSlash, true, segment);
265                     } else {
266                         if (leadingSlash) {
267                             segments.pop_back();
268                         }
269                         slash = leadingSlash;
270                     }
271                     processed = true;
272                     continue;
273                 }
274             }
275             segments.emplace_back(leadingSlash, false, segment);
276         }
277     }
278     return {segments, processed};
279 }
280 
281 class Factory:
282     public cppu::WeakImplHelper<
283         css::lang::XServiceInfo, css::uri::XUriReferenceFactory>
284 {
285 public:
Factory(css::uno::Reference<css::uno::XComponentContext> const & context)286     explicit Factory(
287         css::uno::Reference< css::uno::XComponentContext > const & context):
288         m_context(context) {}
289 
290     Factory(const Factory&) = delete;
291     Factory& operator=(const Factory&) = delete;
292 
293     virtual OUString SAL_CALL getImplementationName() override;
294 
295     virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
296 
297     virtual css::uno::Sequence< OUString > SAL_CALL
298     getSupportedServiceNames() override;
299 
300     virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
301     parse(OUString const & uriReference) override;
302 
303     virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
304     makeAbsolute(
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;
309 
310     virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
311     makeRelative(
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;
317 
318 private:
~Factory()319     virtual ~Factory() override {}
320 
clone(css::uno::Reference<css::uri::XUriReference> const & uriReference)321     css::uno::Reference< css::uri::XUriReference > clone(
322         css::uno::Reference< css::uri::XUriReference > const & uriReference)
323     { return parse(uriReference->getUriReference()); }
324 
325     css::uno::Reference< css::uno::XComponentContext > m_context;
326 };
327 
getImplementationName()328 OUString Factory::getImplementationName()
329 {
330     return "com.sun.star.comp.uri.UriReferenceFactory";
331 }
332 
supportsService(OUString const & serviceName)333 sal_Bool Factory::supportsService(OUString const & serviceName)
334 {
335     return cppu::supportsService(this, serviceName);
336 }
337 
getSupportedServiceNames()338 css::uno::Sequence< OUString > Factory::getSupportedServiceNames()
339 {
340     css::uno::Sequence< OUString > s { "com.sun.star.uri.UriReferenceFactory" };
341     return s;
342 }
343 
parse(OUString const & uriReference)344 css::uno::Reference< css::uri::XUriReference > Factory::parse(
345     OUString const & uriReference)
346 {
347     sal_Int32 fragment = uriReference.indexOf('#');
348     if (fragment == -1) {
349         fragment = uriReference.getLength();
350     }
351     OUString scheme;
352     OUString schemeSpecificPart;
353     OUString serviceName;
354     sal_Int32 n = parseScheme(uriReference);
355     assert(n < fragment);
356     if (n >= 0) {
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 == '+') {
366                 buf.append("PLUS");
367             } else if (c == '-') {
368                 buf.append("HYPHEN");
369             } else if (c == '.') {
370                 buf.append("DOT");
371             } else {
372                 assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c));
373                 buf.append(c);
374             }
375         }
376         serviceName = buf.makeStringAndClear();
377     } else {
378         schemeSpecificPart = uriReference.copy(0, fragment);
379     }
380     css::uno::Reference< css::uri::XUriSchemeParser > parser;
381     if (!serviceName.isEmpty()) {
382         css::uno::Reference< css::lang::XMultiComponentFactory > factory(
383             m_context->getServiceManager());
384         if (factory.is()) {
385             css::uno::Reference< css::uno::XInterface > service;
386             try {
387                 service = factory->createInstanceWithContext(
388                     serviceName, m_context);
389             } catch (css::uno::RuntimeException &) {
390                 throw;
391             } catch (const css::uno::Exception &) {
392                 css::uno::Any anyEx = cppu::getCaughtException();
393                 throw css::lang::WrappedTargetRuntimeException(
394                     "creating service " + serviceName,
395                     static_cast< cppu::OWeakObject * >(this),
396                     anyEx);
397             }
398             if (service.is()) {
399                 parser.set( service, css::uno::UNO_QUERY_THROW);
400             }
401         }
402     }
403     css::uno::Reference< css::uri::XUriReference > uriRef(
404         parser.is()
405         ? parser->parse(scheme, schemeSpecificPart)
406         : parseGeneric(scheme, schemeSpecificPart));
407     if (uriRef.is() && fragment != uriReference.getLength()) {
408         uriRef->setFragment(uriReference.copy(fragment + 1));
409     }
410     return uriRef;
411 }
412 
makeAbsolute(css::uno::Reference<css::uri::XUriReference> const & baseUriReference,css::uno::Reference<css::uri::XUriReference> const & uriReference,sal_Bool processAdditionalSpecialSegments,css::uri::RelativeUriExcessParentSegments excessParentSegments)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)
418 {
419     if (!baseUriReference.is() || !baseUriReference->isAbsolute()
420         || !uriReference.is()) {
421         return nullptr;
422     } else if (uriReference->isAbsolute()) {
423         if (processAdditionalSpecialSegments) {
424             auto const path = uriReference->getPath();
425             auto [segments, proc] = processSegments(path, {}, true);
426             if (proc) {
427                 OUStringBuffer abs(uriReference->getScheme());
428                 abs.append(':');
429                 if (uriReference->hasAuthority()) {
430                     abs.append("//");
431                     abs.append(uriReference->getAuthority());
432                 }
433                 for (auto const & i : segments)
434                 {
435                     if (i.excessParent) {
436                         switch (excessParentSegments) {
437                         case css::uri::RelativeUriExcessParentSegments_ERROR:
438                             return nullptr;
439 
440                         case css::uri::RelativeUriExcessParentSegments_RETAIN:
441                             assert(i.segment == u"..");
442                             break;
443 
444                         case css::uri::RelativeUriExcessParentSegments_REMOVE:
445                             continue;
446 
447                         default:
448                             assert(false);
449                             break;
450                         }
451                     }
452                     if (i.leadingSlash) {
453                         abs.append('/');
454                     }
455                     abs.append(i.segment);
456                 }
457                 if (uriReference->hasQuery()) {
458                     abs.append('?');
459                     abs.append(uriReference->getQuery());
460                 }
461                 if (uriReference->hasFragment()) {
462                     abs.append('#');
463                     abs.append(uriReference->getFragment());
464                 }
465                 return parse(abs.makeStringAndClear());
466             }
467         }
468         return clone(uriReference);
469     } else if (!uriReference->hasAuthority()
470                && uriReference->getPath().isEmpty()) {
471         OUStringBuffer abs(baseUriReference->getScheme());
472         abs.append(':');
473         if (baseUriReference->hasAuthority()) {
474             abs.append("//");
475             abs.append(baseUriReference->getAuthority());
476         }
477         abs.append(baseUriReference->getPath());
478         if (uriReference->hasQuery()) {
479             abs.append('?');
480             abs.append(uriReference->getQuery());
481         } else if (baseUriReference->hasQuery()) {
482             abs.append('?');
483             abs.append(baseUriReference->getQuery());
484         }
485         if (uriReference->hasFragment()) {
486             abs.append('#');
487             abs.append(uriReference->getFragment());
488         }
489         return parse(abs.makeStringAndClear());
490     } else {
491         OUStringBuffer abs(128);
492         abs.append(baseUriReference->getScheme());
493         abs.append(':');
494         if (uriReference->hasAuthority()) {
495             abs.append("//");
496             abs.append(uriReference->getAuthority());
497         } else if (baseUriReference->hasAuthority()) {
498             abs.append("//");
499             abs.append(baseUriReference->getAuthority());
500         }
501         if (uriReference->hasRelativePath()) {
502             auto path1 = baseUriReference->getPath();
503             if (path1.isEmpty()) {
504                 if (baseUriReference->hasAuthority()) {
505                     path1 = "/";
506                 }
507             } else {
508                 path1 = path1.copy(0, path1.lastIndexOf('/') + 1);
509             }
510             auto const path2 = uriReference->getPath();
511             auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments);
512             (void)_;
513             for (auto const & i : segments)
514             {
515                 if (i.excessParent) {
516                     switch (excessParentSegments) {
517                     case css::uri::RelativeUriExcessParentSegments_ERROR:
518                         return nullptr;
519 
520                     case css::uri::RelativeUriExcessParentSegments_RETAIN:
521                         assert(i.segment == u"..");
522                         break;
523 
524                     case css::uri::RelativeUriExcessParentSegments_REMOVE:
525                         continue;
526 
527                     default:
528                         assert(false);
529                         break;
530                     }
531                 }
532                 if (i.leadingSlash) {
533                     abs.append('/');
534                 }
535                 abs.append(i.segment);
536             }
537         } else {
538             bool processed = false;
539             if (processAdditionalSpecialSegments) {
540                 auto const path = uriReference->getPath();
541                 auto [segments, proc] = processSegments(path, {}, true);
542                 if (proc) {
543                     for (auto const & i : segments)
544                     {
545                         if (i.excessParent) {
546                             switch (excessParentSegments) {
547                             case css::uri::RelativeUriExcessParentSegments_ERROR:
548                                 return nullptr;
549 
550                             case css::uri::RelativeUriExcessParentSegments_RETAIN:
551                                 assert(i.segment == u"..");
552                                 break;
553 
554                             case css::uri::RelativeUriExcessParentSegments_REMOVE:
555                                 continue;
556 
557                             default:
558                                 assert(false);
559                                 break;
560                             }
561                         }
562                         if (i.leadingSlash) {
563                             abs.append('/');
564                         }
565                         abs.append(i.segment);
566                     }
567                     processed = true;
568                 }
569             }
570             if (!processed) {
571                 abs.append(uriReference->getPath());
572             }
573         }
574         if (uriReference->hasQuery()) {
575             abs.append('?');
576             abs.append(uriReference->getQuery());
577         }
578         if (uriReference->hasFragment()) {
579             abs.append('#');
580             abs.append(uriReference->getFragment());
581         }
582         return parse(abs.makeStringAndClear());
583     }
584 }
585 
makeRelative(css::uno::Reference<css::uri::XUriReference> const & baseUriReference,css::uno::Reference<css::uri::XUriReference> const & uriReference,sal_Bool preferAuthorityOverRelativePath,sal_Bool preferAbsoluteOverRelativePath,sal_Bool encodeRetainedSpecialSegments)586 css::uno::Reference< css::uri::XUriReference > Factory::makeRelative(
587     css::uno::Reference< css::uri::XUriReference > const & baseUriReference,
588     css::uno::Reference< css::uri::XUriReference > const & uriReference,
589     sal_Bool preferAuthorityOverRelativePath,
590     sal_Bool preferAbsoluteOverRelativePath,
591     sal_Bool encodeRetainedSpecialSegments)
592 {
593     if (!baseUriReference.is() || !baseUriReference->isAbsolute()
594         || !uriReference.is()) {
595         return nullptr;
596     } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath()
597                || !baseUriReference->getScheme().equalsIgnoreAsciiCase(
598                    uriReference->getScheme())) {
599         return clone(uriReference);
600     } else {
601         OUStringBuffer rel(128);
602         bool omitQuery = false;
603         if ((baseUriReference->hasAuthority() != uriReference->hasAuthority())
604             || !equalIgnoreEscapeCase(
605                 baseUriReference->getAuthority(),
606                 uriReference->getAuthority()))
607         {
608             if (uriReference->hasAuthority()) {
609                 rel.append("//");
610                 rel.append(uriReference->getAuthority());
611             }
612             rel.append(uriReference->getPath());
613         } else if ((equalIgnoreEscapeCase(
614                         baseUriReference->getPath(), uriReference->getPath())
615                     || (baseUriReference->getPath() == "/"
616                         && uriReference->getPath().isEmpty()))
617                    && baseUriReference->hasQuery() == uriReference->hasQuery()
618                    && equalIgnoreEscapeCase(
619                        baseUriReference->getQuery(), uriReference->getQuery()))
620         {
621             omitQuery = true;
622         } else {
623             sal_Int32 count1 = std::max< sal_Int32 >(
624                 baseUriReference->getPathSegmentCount(), 1);
625             sal_Int32 count2 = std::max< sal_Int32 >(
626                 uriReference->getPathSegmentCount(), 1);
627             sal_Int32 i = 0;
628             for (; i < std::min(count1, count2) - 1; ++i) {
629                 if (!equalIgnoreEscapeCase(
630                         baseUriReference->getPathSegment(i),
631                         uriReference->getPathSegment(i)))
632                 {
633                     break;
634                 }
635             }
636             if (i == 0
637                 && (preferAbsoluteOverRelativePath || uriReference->hasQuery())
638                 && (preferAuthorityOverRelativePath
639                     || !uriReference->getPath().startsWith("//")))
640             {
641                 if (uriReference->getPath().isEmpty()) {
642                     if (!baseUriReference->getPath().isEmpty()
643                         && baseUriReference->getPath() != "/")
644                     {
645                         rel.append('/');
646                     }
647                 } else if (uriReference->getPath() == "/") {
648                     if (baseUriReference->getPath().isEmpty()
649                         || baseUriReference->getPath() != "/")
650                     {
651                         rel.append('/');
652                     }
653                 } else {
654                     if (uriReference->getPath().startsWith("//")) {
655                         assert(uriReference->hasAuthority());
656                         rel.append("//");
657                         rel.append(uriReference->getAuthority());
658                     }
659                     rel.append(uriReference->getPath());
660                 }
661             } else {
662                 bool segments = false;
663                 for (sal_Int32 j = i; j < count1 - 1; ++j) {
664                     if (segments) {
665                         rel.append('/');
666                     }
667                     rel.append("..");
668                     segments = true;
669                 }
670                 if (i < count2 - 1
671                     || (!uriReference->getPathSegment(count2 - 1).isEmpty()))
672                 {
673                     if (!segments
674                         && (uriReference->getPathSegment(i).isEmpty()
675                             || (parseScheme(uriReference->getPathSegment(i))
676                                 >= 0)))
677                     {
678                         rel.append('.');
679                         segments = true;
680                     }
681                     for (; i < count2; ++i) {
682                         if (segments) {
683                             rel.append('/');
684                         }
685                         OUString s(uriReference->getPathSegment(i));
686                         if (encodeRetainedSpecialSegments && s == ".") {
687                             rel.append("%2E");
688                         } else if (encodeRetainedSpecialSegments && s == "..") {
689                             rel.append("%2E%2E");
690                         } else {
691                             rel.append(s);
692                         }
693                         segments = true;
694                     }
695                 }
696             }
697         }
698         if (!omitQuery && uriReference->hasQuery()) {
699             rel.append('?');
700             rel.append(uriReference->getQuery());
701         }
702         if (uriReference->hasFragment()) {
703             rel.append('#');
704             rel.append(uriReference->getFragment());
705         }
706         return parse(rel.makeStringAndClear());
707     }
708 }
709 
710 }
711 
712 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext * rxContext,css::uno::Sequence<css::uno::Any> const &)713 com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext,
714         css::uno::Sequence<css::uno::Any> const &)
715 {
716     return ::cppu::acquire(new Factory(rxContext));
717 }
718 
719 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
720