1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
25 #include <string_view>
27 #include <com/sun/star/uno/Any.hxx>
28 #include <com/sun/star/uno/RuntimeException.hpp>
29 #include <com/sun/star/uno/Sequence.hxx>
31 #include <osl/file.hxx>
32 #include <rtl/string.h>
33 #include <rtl/string.hxx>
34 #include <rtl/textcvt.h>
35 #include <rtl/textenc.h>
36 #include <rtl/ustring.hxx>
37 #include <rtl/strbuf.hxx>
38 #include <sal/log.hxx>
39 #include <sal/types.h>
40 #include <xmlreader/span.hxx>
43 #include "groupnode.hxx"
44 #include "localizedpropertynode.hxx"
45 #include "localizedvaluenode.hxx"
46 #include "modifications.hxx"
48 #include "nodemap.hxx"
49 #include "propertynode.hxx"
51 #include "writemodfile.hxx"
59 OString
convertToUtf8(std::u16string_view text
) {
61 assert(text
.size() <= sal_uInt32(std::numeric_limits
<sal_Int32
>::max()));
62 if (!rtl_convertUStringToString(
63 &s
.pData
, text
.data(), text
.size(),
64 RTL_TEXTENCODING_UTF8
,
65 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
66 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
)))
68 throw css::uno::RuntimeException(
69 "cannot convert to UTF-8");
74 } // anonymous namespace
76 TempFile::~TempFile() {
77 if (handle
!= nullptr) {
79 oslFileError e
= osl_closeFile(handle
);
80 if (e
!= osl_File_E_None
) {
81 SAL_WARN("configmgr", "osl_closeFile failed with " << +e
);
84 osl::FileBase::RC e
= osl::File::remove(url
);
85 if (e
!= osl::FileBase::E_None
) {
88 "osl::File::remove(" << url
<< ") failed with " << +e
);
94 oslFileError
TempFile::closeWithoutUnlink() {
96 oslFileError e
= osl_closeFile(handle
);
103 void TempFile::closeAndRename(const OUString
&_url
) {
104 oslFileError e
= flush();
105 if (e
!= osl_File_E_None
) {
106 throw css::uno::RuntimeException(
107 "cannot write to " + url
);
109 e
= osl_closeFile(handle
);
111 if (e
!= osl_File_E_None
) {
112 throw css::uno::RuntimeException(
113 "cannot close " + url
);
115 if (osl::File::move(url
, _url
) != osl::FileBase::E_None
) {
116 throw css::uno::RuntimeException(
117 "cannot move " + url
);
122 oslFileError
TempFile::flush() {
123 oslFileError e
= osl_File_E_None
;
124 if (!buffer
.isEmpty()) {
125 sal_uInt64 nBytesWritten
= 0;
126 e
= osl_writeFile(handle
, buffer
.getStr(),
127 static_cast< sal_uInt32
>(buffer
.getLength()),
129 if (nBytesWritten
!= static_cast< sal_uInt32
>(buffer
.getLength())) {
130 // queue up any error / exception until close.
131 buffer
.remove(0, static_cast< sal_Int32
>( nBytesWritten
) );
139 void TempFile::writeString(std::string_view text
) {
140 buffer
.append(text
.data(), text
.size());
141 if (buffer
.getLength() > 0x10000)
147 void writeValueContent_(TempFile
&, bool) = delete;
148 // silence loplugin:salbool
149 void writeValueContent_(TempFile
&handle
, sal_Bool value
) {
151 handle
.writeString("true");
153 handle
.writeString("false");
157 void writeValueContent_(TempFile
&handle
, sal_Int16 value
) {
158 handle
.writeString(OString::number(value
));
161 void writeValueContent_(TempFile
&handle
, sal_Int32 value
) {
162 handle
.writeString(OString::number(value
));
165 void writeValueContent_(TempFile
&handle
, sal_Int64 value
) {
166 handle
.writeString(OString::number(value
));
169 void writeValueContent_(TempFile
&handle
, double value
) {
170 handle
.writeString(OString::number(value
));
173 void writeValueContent_(TempFile
&handle
, const OUString
& value
) {
174 writeValueContent(handle
, value
);
177 void writeValueContent_(
178 TempFile
&handle
, css::uno::Sequence
< sal_Int8
> const & value
)
180 for (sal_Int32 i
= 0; i
< value
.getLength(); ++i
) {
181 static char const hexDigit
[16] = {
182 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
185 std::string_view(hexDigit
+ ((value
[i
] >> 4) & 0xF), 1));
186 handle
.writeString(std::string_view(hexDigit
+ (value
[i
] & 0xF), 1));
190 template< typename T
> void writeSingleValue(
191 TempFile
&handle
, css::uno::Any
const & value
)
193 handle
.writeString(">");
196 writeValueContent_(handle
, val
);
197 handle
.writeString("</value>");
200 template< typename T
> void writeListValue(
201 TempFile
&handle
, css::uno::Any
const & value
)
203 handle
.writeString(">");
204 css::uno::Sequence
< T
> val
;
206 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
208 handle
.writeString(" ");
210 writeValueContent_(handle
, val
[i
]);
212 handle
.writeString("</value>");
215 template< typename T
> void writeItemListValue(
216 TempFile
&handle
, css::uno::Any
const & value
)
218 handle
.writeString(">");
219 css::uno::Sequence
< T
> val
;
221 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
222 handle
.writeString("<it>");
223 writeValueContent_(handle
, val
[i
]);
224 handle
.writeString("</it>");
226 handle
.writeString("</value>");
229 void writeValue(TempFile
&handle
, Type type
, css::uno::Any
const & value
) {
232 writeSingleValue
< sal_Bool
>(handle
, value
);
235 writeSingleValue
< sal_Int16
>(handle
, value
);
238 writeSingleValue
< sal_Int32
>(handle
, value
);
241 writeSingleValue
< sal_Int64
>(handle
, value
);
244 writeSingleValue
< double >(handle
, value
);
247 writeSingleValue
< OUString
>(handle
, value
);
250 writeSingleValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
252 case TYPE_BOOLEAN_LIST
:
253 writeListValue
< sal_Bool
>(handle
, value
);
255 case TYPE_SHORT_LIST
:
256 writeListValue
< sal_Int16
>(handle
, value
);
259 writeListValue
< sal_Int32
>(handle
, value
);
262 writeListValue
< sal_Int64
>(handle
, value
);
264 case TYPE_DOUBLE_LIST
:
265 writeListValue
< double >(handle
, value
);
267 case TYPE_STRING_LIST
:
268 writeItemListValue
< OUString
>(handle
, value
);
270 case TYPE_HEXBINARY_LIST
:
271 writeItemListValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
273 default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
274 assert(false); // this cannot happen
279 Components
& components
, TempFile
&handle
,
280 rtl::Reference
< Node
> const & parent
, std::u16string_view name
,
281 rtl::Reference
< Node
> const & node
)
283 static xmlreader::Span
const typeNames
[] = {
284 xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
285 // TYPE_ERROR, TYPE_NIL, TYPE_ANY
286 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
287 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
288 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
289 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
290 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
291 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
292 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
293 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
294 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
295 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
296 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
297 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
298 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
299 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
300 switch (node
->kind()) {
301 case Node::KIND_PROPERTY
:
303 PropertyNode
* prop
= static_cast< PropertyNode
* >(node
.get());
304 handle
.writeString("<prop oor:name=\"");
305 writeAttributeValue(handle
, name
);
306 handle
.writeString("\" oor:op=\"fuse\"");
307 Type type
= prop
->getStaticType();
308 Type dynType
= getDynamicType(prop
->getValue(components
));
309 assert(dynType
!= TYPE_ERROR
);
310 if (type
== TYPE_ANY
) {
312 if (type
!= TYPE_NIL
) {
313 handle
.writeString(" oor:type=\"");
316 typeNames
[type
].begin
, typeNames
[type
].length
));
317 handle
.writeString("\"");
320 handle
.writeString("><value");
321 if (dynType
== TYPE_NIL
) {
322 handle
.writeString(" xsi:nil=\"true\"/>");
324 writeValue(handle
, type
, prop
->getValue(components
));
326 handle
.writeString("</prop>");
329 case Node::KIND_LOCALIZED_PROPERTY
:
330 handle
.writeString("<prop oor:name=\"");
331 writeAttributeValue(handle
, name
);
332 handle
.writeString("\" oor:op=\"fuse\">");
333 for (auto const& member
: node
->getMembers())
335 writeNode(components
, handle
, node
, member
.first
, member
.second
);
337 handle
.writeString("</prop>");
339 case Node::KIND_LOCALIZED_VALUE
:
341 handle
.writeString("<value");
343 handle
.writeString(" xml:lang=\"");
344 writeAttributeValue(handle
, name
);
345 handle
.writeString("\"");
347 Type type
= static_cast< LocalizedPropertyNode
* >(parent
.get())->
350 static_cast< LocalizedValueNode
* >(node
.get())->getValue());
351 Type dynType
= getDynamicType(value
);
352 assert(dynType
!= TYPE_ERROR
);
353 if (type
== TYPE_ANY
) {
355 if (type
!= TYPE_NIL
) {
356 handle
.writeString(" oor:type=\"");
359 typeNames
[type
].begin
, typeNames
[type
].length
));
360 handle
.writeString("\"");
363 if (dynType
== TYPE_NIL
) {
364 handle
.writeString(" xsi:nil=\"true\"/>");
366 writeValue(handle
, type
, value
);
370 case Node::KIND_GROUP
:
372 handle
.writeString("<node oor:name=\"");
373 writeAttributeValue(handle
, name
);
374 if (!node
->getTemplateName().isEmpty()) { // set member
375 handle
.writeString("\" oor:op=\"replace");
377 handle
.writeString("\">");
378 for (auto const& member
: node
->getMembers())
380 writeNode(components
, handle
, node
, member
.first
, member
.second
);
382 handle
.writeString("</node>");
384 case Node::KIND_ROOT
:
385 assert(false); // this cannot happen
390 // helpers to allow sorting of configmgr::Modifications::Node
391 typedef std::pair
< const OUString
, configmgr::Modifications::Node
> ModNodePairEntry
;
392 struct PairEntrySorter
394 bool operator() (const ModNodePairEntry
* pValue1
, const ModNodePairEntry
* pValue2
) const
396 return pValue1
->first
.compareTo(pValue2
->first
) < 0;
400 void writeModifications(
401 Components
& components
, TempFile
&handle
,
402 OUString
const & parentPathRepresentation
,
403 rtl::Reference
< Node
> const & parent
, OUString
const & nodeName
,
404 rtl::Reference
< Node
> const & node
,
405 Modifications::Node
const & modifications
)
407 // It is never necessary to write oor:finalized or oor:mandatory attributes,
408 // as they cannot be set via the UNO API.
409 if (modifications
.children
.empty()) {
411 // components themselves have no parent but must have children
412 handle
.writeString("<item oor:path=\"");
413 writeAttributeValue(handle
, parentPathRepresentation
);
414 handle
.writeString("\">");
416 writeNode(components
, handle
, parent
, nodeName
, node
);
418 switch (parent
->kind()) {
419 case Node::KIND_LOCALIZED_PROPERTY
:
420 handle
.writeString("<value");
421 if (!nodeName
.isEmpty()) {
422 handle
.writeString(" xml:lang=\"");
423 writeAttributeValue(handle
, nodeName
);
424 handle
.writeString("\"");
426 handle
.writeString(" oor:op=\"remove\"/>");
428 case Node::KIND_GROUP
:
430 static_cast< GroupNode
* >(parent
.get())->isExtensible());
431 handle
.writeString("<prop oor:name=\"");
432 writeAttributeValue(handle
, nodeName
);
433 handle
.writeString("\" oor:op=\"remove\"/>");
436 handle
.writeString("<node oor:name=\"");
437 writeAttributeValue(handle
, nodeName
);
438 handle
.writeString("\" oor:op=\"remove\"/>");
441 assert(false); // this cannot happen
445 handle
.writeString("</item>\n");
449 parentPathRepresentation
+ "/" +
450 Data::createSegment(node
->getTemplateName(), nodeName
));
452 // copy configmgr::Modifications::Node's to a sortable list. Use pointers
453 // to just reference the data instead of copying it
454 std::vector
< const ModNodePairEntry
* > ModNodePairEntryVector
;
455 ModNodePairEntryVector
.reserve(modifications
.children
.size());
457 for (const auto& rCand
: modifications
.children
)
459 ModNodePairEntryVector
.push_back(&rCand
);
463 std::sort(ModNodePairEntryVector
.begin(), ModNodePairEntryVector
.end(), PairEntrySorter());
465 // now use the list to write entries in sorted order
466 // instead of random as from the unordered map
467 for (const auto & i
: ModNodePairEntryVector
)
470 components
, handle
, pathRep
, node
, i
->first
,
471 node
->getMember(i
->first
), i
->second
);
478 void writeAttributeValue(TempFile
&handle
, std::u16string_view value
) {
481 for (; j
!= value
.size(); ++j
) {
483 value
[j
] == 0x0009 || value
[j
] == 0x000A || value
[j
] == 0x000D ||
484 (value
[j
] >= 0x0020 && value
[j
] != 0xFFFE && value
[j
] != 0xFFFF));
487 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
488 handle
.writeString("	");
492 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
493 handle
.writeString("
");
497 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
498 handle
.writeString("
");
502 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
503 handle
.writeString(""");
507 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
508 handle
.writeString("&");
512 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
513 handle
.writeString("<");
520 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
523 void writeValueContent(TempFile
&handle
, std::u16string_view value
) {
526 for (; j
!= value
.size(); ++j
) {
527 char16_t c
= value
[j
];
528 if ((c
< 0x0020 && c
!= 0x0009 && c
!= 0x000A && c
!= 0x000D) ||
529 c
== 0xFFFE || c
== 0xFFFF)
531 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
532 handle
.writeString("<unicode oor:scalar=\"");
533 handle
.writeString(OString::number(c
));
534 handle
.writeString("\"/>");
536 } else if (c
== '\x0D') {
537 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
538 handle
.writeString("
");
540 } else if (c
== '&') {
541 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
542 handle
.writeString("&");
544 } else if (c
== '<') {
545 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
546 handle
.writeString("<");
548 } else if (c
== '>') {
549 // "MUST, for compatibility, be escaped [...] when it appears in the
551 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
552 handle
.writeString(">");
556 handle
.writeString(convertToUtf8(value
.substr(i
, j
- i
)));
560 Components
& components
, OUString
const & url
, Data
const & data
)
562 sal_Int32 i
= url
.lastIndexOf('/');
564 OUString
dir(url
.copy(0, i
));
565 switch (osl::Directory::createPath(dir
)) {
566 case osl::FileBase::E_None
:
567 case osl::FileBase::E_EXIST
:
569 case osl::FileBase::E_ACCES
:
572 ("cannot create registrymodifications.xcu path (E_ACCES); changes"
576 throw css::uno::RuntimeException(
577 "cannot create directory " + dir
);
580 switch (osl::FileBase::createTempFile(&dir
, &tmp
.handle
, &tmp
.url
)) {
581 case osl::FileBase::E_None
:
583 case osl::FileBase::E_ACCES
:
586 ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
590 throw css::uno::RuntimeException(
591 "cannot create temporary file in " + dir
);
594 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
595 " xmlns:oor=\"http://openoffice.org/2001/registry\""
596 " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
597 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
598 //TODO: Do not write back information about those removed items that did not
599 // come from the .xcs/.xcu files, anyway (but had been added dynamically
602 // For profilesafemode it is necessary to detect changes in the
603 // registrymodifications file, this is done based on file size in bytes and crc32.
604 // Unfortunately this write is based on writing unordered map entries, which creates
605 // valid and semantically equal XML-Files, bubt with different crc32 checksums. For
606 // the future usage it will be preferable to have easily comparable config files
607 // which is guaranteed by writing the entries in sorted order. Indeed with this change
608 // (and in the recursive writeModifications call) the same config files get written
610 // copy configmgr::Modifications::Node's to a sortable list. Use pointers
611 // to just reference the data instead of copying it
612 std::vector
< const ModNodePairEntry
* > ModNodePairEntryVector
;
613 ModNodePairEntryVector
.reserve(data
.modifications
.getRoot().children
.size());
615 for (const auto& rCand
: data
.modifications
.getRoot().children
)
617 ModNodePairEntryVector
.push_back(&rCand
);
621 std::sort(ModNodePairEntryVector
.begin(), ModNodePairEntryVector
.end(), PairEntrySorter());
623 // now use the list to write entries in sorted order
624 // instead of random as from the unordered map
625 for (const auto& j
: ModNodePairEntryVector
)
628 components
, tmp
, "", rtl::Reference
< Node
>(), j
->first
,
629 data
.getComponents().findNode(Data::NO_LAYER
, j
->first
),
632 tmp
.writeString("</oor:items>\n");
633 tmp
.closeAndRename(url
);
638 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */