Coverage Report

Created: 2025-07-07 10:01

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