Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx
Line
Count
Source
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 "UriReference.hxx"
21
22
#include <com/sun/star/lang/IllegalArgumentException.hpp>
23
#include <com/sun/star/lang/XServiceInfo.hpp>
24
#include <com/sun/star/uno/Reference.hxx>
25
#include <com/sun/star/uno/Sequence.hxx>
26
#include <com/sun/star/uri/XUriSchemeParser.hpp>
27
#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
28
#include <cppuhelper/implbase.hxx>
29
#include <cppuhelper/supportsservice.hxx>
30
#include <cppuhelper/weak.hxx>
31
#include <rtl/character.hxx>
32
#include <rtl/uri.hxx>
33
#include <rtl/ustrbuf.hxx>
34
#include <rtl/ustring.hxx>
35
#include <sal/types.h>
36
#include <o3tl/safeint.hxx>
37
#include <o3tl/numeric.hxx>
38
39
#include <string_view>
40
41
namespace com::sun::star::uno { class XComponentContext; }
42
namespace com::sun::star::uno { class XInterface; }
43
namespace com::sun::star::uri { class XUriReference; }
44
45
namespace {
46
47
0
int parseEscaped(std::u16string_view part, sal_Int32 * index) {
48
0
    if (part.size() - *index < 3 || part[*index] != '%') {
49
0
        return -1;
50
0
    }
51
0
    int n1 = o3tl::convertToHex<int>(part[*index + 1]);
52
0
    int n2 = o3tl::convertToHex<int>(part[*index + 2]);
53
0
    if (n1 < 0 || n2 < 0) {
54
0
        return -1;
55
0
    }
56
0
    *index += 3;
57
0
    return (n1 << 4) | n2;
58
0
}
59
60
OUString parsePart(
61
    std::u16string_view part, bool namePart, sal_Int32 * index)
62
0
{
63
0
    OUStringBuffer buf(64);
64
0
    while (o3tl::make_unsigned(*index) < part.size()) {
65
0
        sal_Unicode c = part[*index];
66
0
        if (namePart ? c == '?' : c == '&' || c == '=') {
67
0
            break;
68
0
        } else if (c == '%') {
69
0
            sal_Int32 i = *index;
70
0
            int n = parseEscaped(part, &i);
71
0
            if (n >= 0 && n <= 0x7F) {
72
0
                buf.append(static_cast< sal_Unicode >(n));
73
0
            } else if (n >= 0xC0 && n <= 0xFC) {
74
0
                sal_Int32 encoded;
75
0
                int shift;
76
0
                sal_Int32 min;
77
0
                if (n <= 0xDF) {
78
0
                    encoded = (n & 0x1F) << 6;
79
0
                    shift = 0;
80
0
                    min = 0x80;
81
0
                } else if (n <= 0xEF) {
82
0
                    encoded = (n & 0x0F) << 12;
83
0
                    shift = 6;
84
0
                    min = 0x800;
85
0
                } else if (n <= 0xF7) {
86
0
                    encoded = (n & 0x07) << 18;
87
0
                    shift = 12;
88
0
                    min = 0x10000;
89
0
                } else if (n <= 0xFB) {
90
0
                    encoded = (n & 0x03) << 24;
91
0
                    shift = 18;
92
0
                    min = 0x200000;
93
0
                } else {
94
0
                    encoded = 0;
95
0
                    shift = 24;
96
0
                    min = 0x4000000;
97
0
                }
98
0
                bool utf8 = true;
99
0
                for (; shift >= 0; shift -= 6) {
100
0
                    n = parseEscaped(part, &i);
101
0
                    if (n < 0x80 || n > 0xBF) {
102
0
                        utf8 = false;
103
0
                        break;
104
0
                    }
105
0
                    encoded |= (n & 0x3F) << shift;
106
0
                }
107
0
                if (!utf8 || !rtl::isUnicodeScalarValue(encoded)
108
0
                    || encoded < min)
109
0
                {
110
0
                    break;
111
0
                }
112
0
                buf.appendUtf32(encoded);
113
0
            } else {
114
0
                break;
115
0
            }
116
0
            *index = i;
117
0
        } else {
118
0
            buf.append(c);
119
0
            ++*index;
120
0
        }
121
0
    }
122
0
    return buf.makeStringAndClear();
123
0
}
124
125
0
OUString encodeNameOrParamFragment(OUString const & fragment) {
126
0
    static constexpr auto nameOrParamFragment = rtl::createUriCharClass(
127
0
        u8"!$'()*+,-.0123456789:;@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~");
128
0
    return rtl::Uri::encode(
129
0
        fragment, nameOrParamFragment.data(), rtl_UriEncodeIgnoreEscapes,
130
0
        RTL_TEXTENCODING_UTF8);
131
0
}
132
133
0
bool parseSchemeSpecificPart(std::u16string_view part) {
134
0
    size_t len = part.size();
135
0
    sal_Int32 i = 0;
136
0
    if (parsePart(part, true, &i).isEmpty() || part[0] == '/') {
137
0
        return false;
138
0
    }
139
0
    if (o3tl::make_unsigned(i) == len) {
140
0
        return true;
141
0
    }
142
0
    for (;;) {
143
0
        ++i; // skip '?' or '&'
144
0
        if (parsePart(part, false, &i).isEmpty() || o3tl::make_unsigned(i) == len
145
0
            || part[i] != '=')
146
0
        {
147
0
            return false;
148
0
        }
149
0
        ++i;
150
0
        parsePart(part, false, &i);
151
0
        if (o3tl::make_unsigned(i) == len) {
152
0
            return true;
153
0
        }
154
0
        if (part[i] != '&') {
155
0
            return false;
156
0
        }
157
0
    }
158
0
}
159
160
class UrlReference:
161
    public cppu::WeakImplHelper<css::uri::XVndSunStarScriptUrlReference>
162
{
163
public:
164
    UrlReference(OUString const & scheme, OUString const & path):
165
0
        m_base(
166
0
            scheme, false, OUString(), path, false, OUString())
167
0
    {}
168
169
    UrlReference(const UrlReference&) = delete;
170
    UrlReference& operator=(const UrlReference&) = delete;
171
172
    virtual OUString SAL_CALL getUriReference() override
173
0
    { return m_base.getUriReference(); }
174
175
    virtual sal_Bool SAL_CALL isAbsolute() override
176
0
    { return m_base.isAbsolute(); }
177
178
    virtual OUString SAL_CALL getScheme() override
179
0
    { return m_base.getScheme(); }
180
181
    virtual OUString SAL_CALL getSchemeSpecificPart() override
182
0
    { return m_base.getSchemeSpecificPart(); }
183
184
    virtual sal_Bool SAL_CALL isHierarchical() override
185
0
    { return m_base.isHierarchical(); }
186
187
    virtual sal_Bool SAL_CALL hasAuthority() override
188
0
    { return m_base.hasAuthority(); }
189
190
    virtual OUString SAL_CALL getAuthority() override
191
0
    { return m_base.getAuthority(); }
192
193
    virtual OUString SAL_CALL getPath() override
194
0
    { return m_base.getPath(); }
195
196
    virtual sal_Bool SAL_CALL hasRelativePath() override
197
0
    { return m_base.hasRelativePath(); }
198
199
    virtual sal_Int32 SAL_CALL getPathSegmentCount() override
200
0
    { return m_base.getPathSegmentCount(); }
201
202
    virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override
203
0
    { return m_base.getPathSegment(index); }
204
205
    virtual sal_Bool SAL_CALL hasQuery() override
206
0
    { return m_base.hasQuery(); }
207
208
    virtual OUString SAL_CALL getQuery() override
209
0
    { return m_base.getQuery(); }
210
211
    virtual sal_Bool SAL_CALL hasFragment() override
212
0
    { return m_base.hasFragment(); }
213
214
    virtual OUString SAL_CALL getFragment() override
215
0
    { return m_base.getFragment(); }
216
217
    virtual void SAL_CALL setFragment(OUString const & fragment) override
218
0
    { m_base.setFragment(fragment); }
219
220
    virtual void SAL_CALL clearFragment() override
221
0
    { m_base.clearFragment(); }
222
223
    virtual OUString SAL_CALL getName() override;
224
225
    virtual void SAL_CALL setName(OUString const & name) override;
226
227
    virtual sal_Bool SAL_CALL hasParameter(OUString const & key) override;
228
229
    virtual OUString SAL_CALL getParameter(OUString const & key) override;
230
231
    virtual void SAL_CALL setParameter(OUString const & key, OUString const & value) override;
232
233
private:
234
0
    virtual ~UrlReference() override {}
235
236
    sal_Int32 findParameter(std::u16string_view key) const;
237
238
    stoc::uriproc::UriReference m_base;
239
};
240
241
0
OUString UrlReference::getName() {
242
0
    std::lock_guard g(m_base.m_mutex);
243
0
    sal_Int32 i = 0;
244
0
    return parsePart(m_base.m_path, true, &i);
245
0
}
246
247
void SAL_CALL UrlReference::setName(OUString const & name)
248
0
{
249
0
    if (name.isEmpty())
250
0
        throw css::lang::IllegalArgumentException(
251
0
            OUString(), *this, 1);
252
253
0
    std::lock_guard g(m_base.m_mutex);
254
0
    sal_Int32 i = 0;
255
0
    parsePart(m_base.m_path, true, &i);
256
257
0
    m_base.m_path = encodeNameOrParamFragment(name) + m_base.m_path.subView(i);
258
0
}
259
260
sal_Bool UrlReference::hasParameter(OUString const & key)
261
0
{
262
0
    std::lock_guard g(m_base.m_mutex);
263
0
    return findParameter(key) >= 0;
264
0
}
265
266
OUString UrlReference::getParameter(OUString const & key)
267
0
{
268
0
    std::lock_guard g(m_base.m_mutex);
269
0
    sal_Int32 i = findParameter(key);
270
0
    return i >= 0 ? parsePart(m_base.m_path, false, &i) : OUString();
271
0
}
272
273
void UrlReference::setParameter(OUString const & key, OUString const & value)
274
0
{
275
0
    if (key.isEmpty())
276
0
        throw css::lang::IllegalArgumentException(
277
0
            OUString(), *this, 1);
278
279
0
    std::lock_guard g(m_base.m_mutex);
280
0
    sal_Int32 i = findParameter(key);
281
0
    bool bExistent = ( i>=0 );
282
0
    if (!bExistent) {
283
0
        i = m_base.m_path.getLength();
284
0
    }
285
286
0
    OUStringBuffer newPath(128);
287
0
    newPath.append(m_base.m_path.subView(0, i));
288
0
    if (!bExistent) {
289
0
        newPath.append( m_base.m_path.indexOf('?') < 0 ? '?' : '&' );
290
0
        newPath.append(encodeNameOrParamFragment(key) + "=");
291
0
    }
292
0
    newPath.append(encodeNameOrParamFragment(value));
293
0
    if (bExistent) {
294
        /*oldValue = */
295
0
        parsePart(m_base.m_path, false, &i); // skip key
296
0
        newPath.append(m_base.m_path.subView(i));
297
0
    }
298
299
0
    m_base.m_path = newPath.makeStringAndClear();
300
0
}
301
302
0
sal_Int32 UrlReference::findParameter(std::u16string_view key) const {
303
0
    sal_Int32 i = 0;
304
0
    parsePart(m_base.m_path, true, &i); // skip name
305
0
    for (;;) {
306
0
        if (i == m_base.m_path.getLength()) {
307
0
            return -1;
308
0
        }
309
0
        ++i; // skip '?' or '&'
310
0
        OUString k = parsePart(m_base.m_path, false, &i);
311
0
        ++i; // skip '='
312
0
        if (k == key) {
313
0
            return i;
314
0
        }
315
0
        parsePart(m_base.m_path, false, &i); // skip value
316
0
    }
317
0
}
318
319
class Parser:
320
    public cppu::WeakImplHelper<
321
        css::lang::XServiceInfo, css::uri::XUriSchemeParser>
322
{
323
public:
324
0
    Parser() {}
325
326
    Parser(const Parser&) = delete;
327
    Parser& operator=(const Parser&) = delete;
328
329
    virtual OUString SAL_CALL getImplementationName() override;
330
331
    virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
332
333
    virtual css::uno::Sequence< OUString > SAL_CALL
334
    getSupportedServiceNames() override;
335
336
    virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL
337
    parse(
338
        OUString const & scheme, OUString const & schemeSpecificPart) override;
339
340
private:
341
0
    virtual ~Parser() override {}
342
};
343
344
OUString Parser::getImplementationName()
345
0
{
346
0
    return u"com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"_ustr;
347
0
}
348
349
sal_Bool Parser::supportsService(OUString const & serviceName)
350
0
{
351
0
    return cppu::supportsService(this, serviceName);
352
0
}
353
354
css::uno::Sequence< OUString > Parser::getSupportedServiceNames()
355
0
{
356
0
    return { u"com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"_ustr };
357
0
}
358
359
css::uno::Reference< css::uri::XUriReference >
360
Parser::parse(
361
    OUString const & scheme, OUString const & schemeSpecificPart)
362
0
{
363
0
    if (!parseSchemeSpecificPart(schemeSpecificPart)) {
364
0
        return nullptr;
365
0
    }
366
0
    return new UrlReference(scheme, schemeSpecificPart);
367
0
}
368
369
}
370
371
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
372
com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTscript_get_implementation(css::uno::XComponentContext*,
373
        css::uno::Sequence<css::uno::Any> const &)
374
0
{
375
    //TODO: single instance
376
0
    return ::cppu::acquire(new Parser());
377
0
}
378
379
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */