Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/configmgr/source/data.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 <sal/config.h>
21
22
#include <algorithm>
23
#include <cassert>
24
#include <cstddef>
25
26
#include <com/sun/star/uno/RuntimeException.hpp>
27
#include <rtl/ref.hxx>
28
#include <rtl/string.h>
29
#include <rtl/ustrbuf.hxx>
30
#include <rtl/ustring.h>
31
#include <rtl/ustring.hxx>
32
#include <sal/log.hxx>
33
#include <sal/types.h>
34
#include <o3tl/string_view.hxx>
35
36
#include "additions.hxx"
37
#include "data.hxx"
38
#include "node.hxx"
39
#include "nodemap.hxx"
40
#include "rootnode.hxx"
41
#include "setnode.hxx"
42
43
namespace configmgr {
44
45
namespace {
46
47
bool decode(
48
    std::u16string_view encoded, std::size_t begin, std::size_t end,
49
    OUString * decoded)
50
0
{
51
0
    assert(
52
0
        begin <= end && end <= encoded.size() &&
53
0
        decoded != nullptr);
54
0
    OUStringBuffer buf(end - begin);
55
0
    while (begin != end) {
56
0
        sal_Unicode c = encoded[begin++];
57
0
        if (c == '&') {
58
0
            if (o3tl::starts_with(encoded.substr(begin), u"amp;")) {
59
0
                buf.append('&');
60
0
                begin += RTL_CONSTASCII_LENGTH("amp;");
61
0
            } else if (o3tl::starts_with(encoded.substr(begin), u"quot;")) {
62
0
                buf.append('"');
63
0
                begin += RTL_CONSTASCII_LENGTH("quot;");
64
0
            } else if (o3tl::starts_with(encoded.substr(begin), u"apos;")) {
65
0
                buf.append('\'');
66
0
                begin += RTL_CONSTASCII_LENGTH("apos;");
67
0
            } else {
68
0
                return false;
69
0
            }
70
0
            assert(begin <= end);
71
0
        } else {
72
0
            buf.append(c);
73
0
        }
74
0
    }
75
0
    *decoded = buf.makeStringAndClear();
76
0
    return true;
77
0
}
78
79
}
80
81
OUString Data::createSegment(
82
    std::u16string_view templateName, OUString const & name)
83
0
{
84
0
    if (templateName.empty()) {
85
0
        return name;
86
0
    }
87
0
    OUStringBuffer buf(128);
88
    //TODO: verify template name contains no bad chars?
89
0
    buf.append(OUString::Concat(templateName) + "['");
90
0
    for (sal_Int32 i = 0; i < name.getLength(); ++i) {
91
0
        sal_Unicode c = name[i];
92
0
        switch (c) {
93
0
        case '&':
94
0
            buf.append("&amp;");
95
0
            break;
96
0
        case '"':
97
0
            buf.append("&quot;");
98
0
            break;
99
0
        case '\'':
100
0
            buf.append("&apos;");
101
0
            break;
102
0
        default:
103
0
            buf.append(c);
104
0
            break;
105
0
        }
106
0
    }
107
0
    buf.append("']");
108
0
    return buf.makeStringAndClear();
109
0
}
110
111
sal_Int32 Data::parseSegment(
112
    OUString const & path, sal_Int32 index, OUString * name,
113
    bool * setElement, OUString * templateName)
114
124k
{
115
124k
    assert(
116
124k
        index >= 0 && index <= path.getLength() && name != nullptr &&
117
124k
        setElement != nullptr);
118
124k
    sal_Int32 i = index;
119
3.67M
    while (i < path.getLength() && path[i] != '/' && path[i] != '[') {
120
3.55M
        ++i;
121
3.55M
    }
122
124k
    if (i == path.getLength() || path[i] == '/') {
123
124k
        *name = path.copy(index, i - index);
124
124k
        *setElement = false;
125
124k
        return i;
126
124k
    }
127
0
    if (i - index == 1 && path[index] == '*') {
128
0
        *setElement = true;
129
0
        if (templateName != nullptr) {
130
0
            templateName->clear();
131
0
        }
132
0
    } else {
133
0
        *setElement = i != index;
134
0
        if (templateName != nullptr) {
135
0
            *templateName = path.copy(index, i - index);
136
0
        }
137
0
    }
138
0
    if (++i == path.getLength()) {
139
0
        return -1;
140
0
    }
141
0
    sal_Unicode del = path[i++];
142
0
    if (del != '\'' && del != '"') {
143
0
        return -1;
144
0
    }
145
0
    sal_Int32 j = path.indexOf(del, i);
146
0
    if (j == -1 || j + 1 == path.getLength() || path[j + 1] != ']' ||
147
0
        !decode(path, i, j, name))
148
0
    {
149
0
        return -1;
150
0
    }
151
0
    return j + 2;
152
0
}
153
154
OUString Data::fullTemplateName(
155
    std::u16string_view component, std::u16string_view name)
156
0
{
157
0
    if (component.find(':') != std::u16string_view::npos || name.find(':') != std::u16string_view::npos) {
158
0
        throw css::uno::RuntimeException(
159
0
            OUString::Concat("bad component/name pair containing colon ") + component + "/" +
160
0
            name);
161
0
    }
162
0
    return OUString::Concat(component) + ":" + name;
163
0
}
164
165
bool Data::equalTemplateNames(
166
    OUString const & shortName, OUString const & longName)
167
0
{
168
0
    if (shortName.indexOf(':') == -1) {
169
0
        sal_Int32 i = longName.indexOf(':') + 1;
170
0
        assert(i > 0);
171
0
        return
172
0
            rtl_ustr_compare_WithLength(
173
0
                shortName.getStr(), shortName.getLength(),
174
0
                longName.getStr() + i, longName.getLength() - i) ==
175
0
            0;
176
0
    } else {
177
0
        return shortName == longName;
178
0
    }
179
0
}
180
181
13
Data::Data(): root_(new RootNode) {}
182
183
rtl::Reference< Node > Data::resolvePathRepresentation(
184
    OUString const & pathRepresentation,
185
    OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
186
    const
187
124k
{
188
124k
    if (pathRepresentation.isEmpty() || pathRepresentation[0] != '/') {
189
0
        throw css::uno::RuntimeException(
190
0
            "bad path " + pathRepresentation);
191
0
    }
192
124k
    if (path != nullptr) {
193
124k
        path->clear();
194
124k
    }
195
124k
    if (pathRepresentation == "/") {
196
65.2k
        if (canonicRepresentation != nullptr) {
197
65.2k
            *canonicRepresentation = pathRepresentation;
198
65.2k
        }
199
65.2k
        if (finalizedLayer != nullptr) {
200
65.2k
            *finalizedLayer = NO_LAYER;
201
65.2k
        }
202
65.2k
        return root_;
203
65.2k
    }
204
59.2k
    OUString seg;
205
59.2k
    bool setElement;
206
59.2k
    OUString templateName;
207
59.2k
    sal_Int32 n = parseSegment(pathRepresentation, 1, &seg, &setElement, nullptr);
208
59.2k
    if (n == -1 || setElement)
209
0
    {
210
0
        throw css::uno::RuntimeException(
211
0
            "bad path " + pathRepresentation);
212
0
    }
213
59.2k
    NodeMap const & components = getComponents();
214
59.2k
    NodeMap::const_iterator i(components.find(seg));
215
59.2k
    OUStringBuffer canonic(128);
216
59.2k
    rtl::Reference< Node > parent;
217
59.2k
    int finalized = NO_LAYER;
218
59.2k
    for (rtl::Reference< Node > p(i == components.end() ? nullptr : i->second);;) {
219
59.2k
        if (!p.is()) {
220
59.2k
            return p;
221
59.2k
        }
222
0
        if (canonicRepresentation != nullptr) {
223
0
            canonic.append("/" + createSegment(templateName, seg));
224
0
        }
225
0
        if (path != nullptr) {
226
0
            path->push_back(seg);
227
0
        }
228
0
        finalized = std::min(finalized, p->getFinalized());
229
0
        if (n != pathRepresentation.getLength() &&
230
0
            pathRepresentation[n++] != '/')
231
0
        {
232
0
            throw css::uno::RuntimeException(
233
0
                "bad path " + pathRepresentation);
234
0
        }
235
        // for backwards compatibility, ignore a final slash
236
0
        if (n == pathRepresentation.getLength()) {
237
0
            if (canonicRepresentation != nullptr) {
238
0
                *canonicRepresentation = canonic.makeStringAndClear();
239
0
            }
240
0
            if (finalizedLayer != nullptr) {
241
0
                *finalizedLayer = finalized;
242
0
            }
243
0
            return p;
244
0
        }
245
0
        parent = p;
246
0
        templateName.clear();
247
0
        n = parseSegment(
248
0
            pathRepresentation, n, &seg, &setElement, &templateName);
249
0
        if (n == -1) {
250
0
            throw css::uno::RuntimeException(
251
0
                "bad path " + pathRepresentation);
252
0
        }
253
        // For backwards compatibility, allow set members to be accessed with
254
        // simple path segments, like group members:
255
0
        p = p->getMember(seg);
256
0
        if (setElement) {
257
0
            switch (parent->kind()) {
258
0
            case Node::KIND_LOCALIZED_PROPERTY:
259
0
                if (!templateName.isEmpty()) {
260
0
                    throw css::uno::RuntimeException(
261
0
                        "bad path " + pathRepresentation);
262
0
                }
263
0
                break;
264
0
            case Node::KIND_SET:
265
0
                if (!templateName.isEmpty() &&
266
0
                    !static_cast< SetNode * >(parent.get())->isValidTemplate(
267
0
                        templateName))
268
0
                {
269
0
                    throw css::uno::RuntimeException(
270
0
                        "bad path " + pathRepresentation);
271
0
                }
272
0
                break;
273
0
            default:
274
0
                throw css::uno::RuntimeException(
275
0
                    "bad path " + pathRepresentation);
276
0
            }
277
0
            if (!templateName.isEmpty() && p != nullptr) {
278
0
                assert(!p->getTemplateName().isEmpty());
279
0
                if (!equalTemplateNames(templateName, p->getTemplateName())) {
280
0
                    throw css::uno::RuntimeException(
281
0
                        "bad path " + pathRepresentation);
282
0
                }
283
0
            }
284
0
        }
285
0
    }
286
59.2k
}
287
288
rtl::Reference< Node > Data::getTemplate(
289
    int layer, OUString const & fullName) const
290
0
{
291
0
    return templates.findNode(layer, fullName);
292
0
}
293
294
59.2k
NodeMap & Data::getComponents() const {
295
59.2k
    return root_->getMembers();
296
59.2k
}
297
298
Additions * Data::addExtensionXcuAdditions(
299
    OUString const & url, int layer)
300
0
{
301
0
    rtl::Reference item(new ExtensionXcu);
302
0
    ExtensionXcuAdditions::iterator i(
303
0
        extensionXcuAdditions_.emplace(
304
0
                url, rtl::Reference< ExtensionXcu >()).first);
305
0
    if (i->second.is()) {
306
0
        throw css::uno::RuntimeException(
307
0
            "already added extension xcu " + url);
308
0
    }
309
0
    i->second = item;
310
0
    item->layer = layer;
311
0
    return &item->additions;
312
0
}
313
314
rtl::Reference< Data::ExtensionXcu > Data::removeExtensionXcuAdditions(
315
    OUString const & url)
316
0
{
317
0
    ExtensionXcuAdditions::iterator i(extensionXcuAdditions_.find(url));
318
0
    if (i == extensionXcuAdditions_.end()) {
319
        // This can happen, as migration of pre OOo 3.3 UserInstallation
320
        // extensions in dp_registry::backend::configuration::BackendImpl::
321
        // PackageImpl::processPackage_ can cause just-in-time creation of
322
        // extension xcu files that are never added via addExtensionXcuAdditions
323
        // (also, there might be url spelling differences between calls to
324
        // addExtensionXcuAdditions and removeExtensionXcuAdditions?):
325
0
        SAL_INFO(
326
0
            "configmgr",
327
0
            "unknown Data::removeExtensionXcuAdditions(" << url << ")");
328
0
        return rtl::Reference< ExtensionXcu >();
329
0
    }
330
0
    rtl::Reference< ExtensionXcu > item(i->second);
331
0
    extensionXcuAdditions_.erase(i);
332
0
    return item;
333
0
}
334
335
}
336
337
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */