/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("&"); |
95 | 0 | break; |
96 | 0 | case '"': |
97 | 0 | buf.append("""); |
98 | 0 | break; |
99 | 0 | case '\'': |
100 | 0 | buf.append("'"); |
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: */ |