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>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/Reference.hxx>
26 #include <com/sun/star/uno/RuntimeException.hpp>
27 #include <com/sun/star/uno/Sequence.hxx>
28 #include <com/sun/star/uno/XInterface.hpp>
30 #include <osl/file.hxx>
31 #include <rtl/string.h>
32 #include <rtl/string.hxx>
33 #include <rtl/textcvt.h>
34 #include <rtl/textenc.h>
35 #include <rtl/ustrbuf.hxx>
36 #include <rtl/ustring.h>
37 #include <rtl/ustring.hxx>
38 #include <rtl/strbuf.hxx>
39 #include <sal/log.hxx>
40 #include <sal/types.h>
41 #include <xmlreader/span.hxx>
44 #include "groupnode.hxx"
45 #include "localizedpropertynode.hxx"
46 #include "localizedvaluenode.hxx"
47 #include "modifications.hxx"
49 #include "nodemap.hxx"
50 #include "propertynode.hxx"
52 #include "writemodfile.hxx"
60 OString
convertToUtf8(
61 OUString
const & text
, sal_Int32 offset
, sal_Int32 length
)
63 assert(offset
<= text
.getLength() && text
.getLength() - offset
>= length
);
65 if (!rtl_convertUStringToString(
66 &s
.pData
, text
.pData
->buffer
+ offset
, length
,
67 RTL_TEXTENCODING_UTF8
,
68 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
69 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
)))
71 throw css::uno::RuntimeException(
72 "cannot convert to UTF-8");
77 } // anonymous namespace
79 TempFile::~TempFile() {
80 if (handle
!= nullptr) {
82 oslFileError e
= osl_closeFile(handle
);
83 if (e
!= osl_File_E_None
) {
84 SAL_WARN("configmgr", "osl_closeFile failed with " << +e
);
87 osl::FileBase::RC e
= osl::File::remove(url
);
88 if (e
!= osl::FileBase::E_None
) {
91 "osl::File::remove(" << url
<< ") failed with " << +e
);
97 oslFileError
TempFile::closeWithoutUnlink() {
99 oslFileError e
= osl_closeFile(handle
);
106 void TempFile::closeAndRename(const OUString
&_url
) {
107 oslFileError e
= flush();
108 if (e
!= osl_File_E_None
) {
109 throw css::uno::RuntimeException(
110 "cannot write to " + url
);
112 e
= osl_closeFile(handle
);
114 if (e
!= osl_File_E_None
) {
115 throw css::uno::RuntimeException(
116 "cannot close " + url
);
118 if (osl::File::move(url
, _url
) != osl::FileBase::E_None
) {
119 throw css::uno::RuntimeException(
120 "cannot move " + url
);
125 oslFileError
TempFile::flush() {
126 oslFileError e
= osl_File_E_None
;
127 if (!buffer
.isEmpty()) {
128 sal_uInt64 nBytesWritten
= 0;
129 e
= osl_writeFile(handle
, buffer
.getStr(),
130 static_cast< sal_uInt32
>(buffer
.getLength()),
132 if (nBytesWritten
!= static_cast< sal_uInt32
>(buffer
.getLength())) {
133 // queue up any error / exception until close.
134 buffer
.remove(0, static_cast< sal_Int32
>( nBytesWritten
) );
142 void TempFile::writeString(char const *begin
, sal_Int32 length
) {
143 buffer
.append(begin
, length
);
144 if (buffer
.getLength() > 0x10000)
150 void writeData_(TempFile
&handle
, char const * begin
, sal_Int32 length
) {
152 handle
.writeString(begin
, length
);
155 void writeValueContent_(TempFile
&, bool) = delete;
156 // silence loplugin:salbool
157 void writeValueContent_(TempFile
&handle
, sal_Bool value
) {
159 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("true"));
161 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("false"));
165 void writeValueContent_(TempFile
&handle
, sal_Int16 value
) {
166 writeData(handle
, OString::number(value
));
169 void writeValueContent_(TempFile
&handle
, sal_Int32 value
) {
170 writeData(handle
, OString::number(value
));
173 void writeValueContent_(TempFile
&handle
, sal_Int64 value
) {
174 writeData(handle
, OString::number(value
));
177 void writeValueContent_(TempFile
&handle
, double value
) {
178 writeData(handle
, OString::number(value
));
181 void writeValueContent_(TempFile
&handle
, const OUString
& value
) {
182 writeValueContent(handle
, value
);
185 void writeValueContent_(
186 TempFile
&handle
, css::uno::Sequence
< sal_Int8
> const & value
)
188 for (sal_Int32 i
= 0; i
< value
.getLength(); ++i
) {
189 static char const hexDigit
[16] = {
190 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
192 writeData_(handle
, hexDigit
+ ((value
[i
] >> 4) & 0xF), 1);
193 writeData_(handle
, hexDigit
+ (value
[i
] & 0xF), 1);
197 template< typename T
> void writeSingleValue(
198 TempFile
&handle
, css::uno::Any
const & value
)
200 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
203 writeValueContent_(handle
, val
);
204 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
207 template< typename T
> void writeListValue(
208 TempFile
&handle
, css::uno::Any
const & value
)
210 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
211 css::uno::Sequence
< T
> val
;
213 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
215 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(" "));
217 writeValueContent_(handle
, val
[i
]);
219 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
222 template< typename T
> void writeItemListValue(
223 TempFile
&handle
, css::uno::Any
const & value
)
225 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
226 css::uno::Sequence
< T
> val
;
228 for (sal_Int32 i
= 0; i
< val
.getLength(); ++i
) {
229 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<it>"));
230 writeValueContent_(handle
, val
[i
]);
231 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</it>"));
233 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</value>"));
236 void writeValue(TempFile
&handle
, Type type
, css::uno::Any
const & value
) {
239 writeSingleValue
< sal_Bool
>(handle
, value
);
242 writeSingleValue
< sal_Int16
>(handle
, value
);
245 writeSingleValue
< sal_Int32
>(handle
, value
);
248 writeSingleValue
< sal_Int64
>(handle
, value
);
251 writeSingleValue
< double >(handle
, value
);
254 writeSingleValue
< OUString
>(handle
, value
);
257 writeSingleValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
259 case TYPE_BOOLEAN_LIST
:
260 writeListValue
< sal_Bool
>(handle
, value
);
262 case TYPE_SHORT_LIST
:
263 writeListValue
< sal_Int16
>(handle
, value
);
266 writeListValue
< sal_Int32
>(handle
, value
);
269 writeListValue
< sal_Int64
>(handle
, value
);
271 case TYPE_DOUBLE_LIST
:
272 writeListValue
< double >(handle
, value
);
274 case TYPE_STRING_LIST
:
275 writeItemListValue
< OUString
>(handle
, value
);
277 case TYPE_HEXBINARY_LIST
:
278 writeItemListValue
< css::uno::Sequence
< sal_Int8
> >(handle
, value
);
280 default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
281 assert(false); // this cannot happen
286 Components
& components
, TempFile
&handle
,
287 rtl::Reference
< Node
> const & parent
, OUString
const & name
,
288 rtl::Reference
< Node
> const & node
)
290 static xmlreader::Span
const typeNames
[] = {
291 xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
292 // TYPE_ERROR, TYPE_NIL, TYPE_ANY
293 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
294 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
295 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
296 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
297 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
298 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
299 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
300 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
301 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
302 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
303 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
304 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
305 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
306 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
307 switch (node
->kind()) {
308 case Node::KIND_PROPERTY
:
310 PropertyNode
* prop
= static_cast< PropertyNode
* >(node
.get());
311 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
312 writeAttributeValue(handle
, name
);
314 handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\""));
315 Type type
= prop
->getStaticType();
316 Type dynType
= getDynamicType(prop
->getValue(components
));
317 assert(dynType
!= TYPE_ERROR
);
318 if (type
== TYPE_ANY
) {
320 if (type
!= TYPE_NIL
) {
322 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
324 handle
, typeNames
[type
].begin
, typeNames
[type
].length
);
325 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
328 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("><value"));
329 if (dynType
== TYPE_NIL
) {
331 handle
, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
333 writeValue(handle
, type
, prop
->getValue(components
));
335 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</prop>"));
338 case Node::KIND_LOCALIZED_PROPERTY
:
339 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
340 writeAttributeValue(handle
, name
);
341 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\">"));
342 for (NodeMap::const_iterator
i(node
->getMembers().begin());
343 i
!= node
->getMembers().end(); ++i
)
345 writeNode(components
, handle
, node
, i
->first
, i
->second
);
347 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</prop>"));
349 case Node::KIND_LOCALIZED_VALUE
:
351 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<value"));
352 if (!name
.isEmpty()) {
353 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
354 writeAttributeValue(handle
, name
);
355 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
357 Type type
= static_cast< LocalizedPropertyNode
* >(parent
.get())->
360 static_cast< LocalizedValueNode
* >(node
.get())->getValue());
361 Type dynType
= getDynamicType(value
);
362 assert(dynType
!= TYPE_ERROR
);
363 if (type
== TYPE_ANY
) {
365 if (type
!= TYPE_NIL
) {
367 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
369 handle
, typeNames
[type
].begin
, typeNames
[type
].length
);
370 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
373 if (dynType
== TYPE_NIL
) {
375 handle
, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
377 writeValue(handle
, type
, value
);
381 case Node::KIND_GROUP
:
383 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
384 writeAttributeValue(handle
, name
);
385 if (!node
->getTemplateName().isEmpty()) { // set member
387 handle
, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"replace"));
389 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\">"));
390 for (NodeMap::const_iterator
i(node
->getMembers().begin());
391 i
!= node
->getMembers().end(); ++i
)
393 writeNode(components
, handle
, node
, i
->first
, i
->second
);
395 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</node>"));
397 case Node::KIND_ROOT
:
398 assert(false); // this cannot happen
403 // helpers to allow sorting of configmgr::Modifications::Node
404 typedef std::pair
< const rtl::OUString
, configmgr::Modifications::Node
> ModNodePairEntry
;
405 struct PairEntrySorter
407 bool operator() (const ModNodePairEntry
* pValue1
, const ModNodePairEntry
* pValue2
) const
409 return pValue1
->first
.compareTo(pValue2
->first
) < 0;
413 void writeModifications(
414 Components
& components
, TempFile
&handle
,
415 OUString
const & parentPathRepresentation
,
416 rtl::Reference
< Node
> const & parent
, OUString
const & nodeName
,
417 rtl::Reference
< Node
> const & node
,
418 Modifications::Node
const & modifications
)
420 // It is never necessary to write oor:finalized or oor:mandatory attributes,
421 // as they cannot be set via the UNO API.
422 if (modifications
.children
.empty()) {
424 // components themselves have no parent but must have children
425 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<item oor:path=\""));
426 writeAttributeValue(handle
, parentPathRepresentation
);
427 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\">"));
429 writeNode(components
, handle
, parent
, nodeName
, node
);
431 switch (parent
->kind()) {
432 case Node::KIND_LOCALIZED_PROPERTY
:
433 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<value"));
434 if (!nodeName
.isEmpty()) {
436 handle
, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
437 writeAttributeValue(handle
, nodeName
);
438 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\""));
441 handle
, RTL_CONSTASCII_STRINGPARAM(" oor:op=\"remove\"/>"));
443 case Node::KIND_GROUP
:
445 static_cast< GroupNode
* >(parent
.get())->isExtensible());
447 handle
, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
448 writeAttributeValue(handle
, nodeName
);
451 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
455 handle
, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
456 writeAttributeValue(handle
, nodeName
);
459 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
462 assert(false); // this cannot happen
466 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("</item>\n"));
470 parentPathRepresentation
+ "/" +
471 Data::createSegment(node
->getTemplateName(), nodeName
));
473 // copy configmgr::Modifications::Node's to a sortable list. Use pointers
474 // to just reference the data instead of copying it
475 std::vector
< const ModNodePairEntry
* > ModNodePairEntryVector
;
476 ModNodePairEntryVector
.reserve(modifications
.children
.size());
478 for (const auto& rCand
: modifications
.children
)
480 ModNodePairEntryVector
.push_back(&rCand
);
484 std::sort(ModNodePairEntryVector
.begin(), ModNodePairEntryVector
.end(), PairEntrySorter());
486 // now use the list to write entries in sorted order
487 // instead of random as from the unordered map
488 for (const auto & i
: ModNodePairEntryVector
)
491 components
, handle
, pathRep
, node
, i
->first
,
492 node
->getMember(i
->first
), i
->second
);
499 void writeData(TempFile
&handle
, OString
const & text
) {
500 writeData_(handle
, text
.getStr(), text
.getLength());
503 void writeAttributeValue(TempFile
&handle
, OUString
const & value
) {
506 for (; j
< value
.getLength(); ++j
) {
508 value
[j
] == 0x0009 || value
[j
] == 0x000A || value
[j
] == 0x000D ||
509 (value
[j
] >= 0x0020 && value
[j
] != 0xFFFE && value
[j
] != 0xFFFF));
512 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
513 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("	"));
517 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
518 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
522 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
523 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
527 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
528 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("""));
532 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
533 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("&"));
537 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
538 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<"));
545 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
548 void writeValueContent(TempFile
&handle
, OUString
const & value
) {
551 for (; j
< value
.getLength(); ++j
) {
552 sal_Unicode c
= value
[j
];
553 if ((c
< 0x0020 && c
!= 0x0009 && c
!= 0x000A && c
!= 0x000D) ||
554 c
== 0xFFFE || c
== 0xFFFF)
556 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
558 handle
, RTL_CONSTASCII_STRINGPARAM("<unicode oor:scalar=\""));
560 handle
, OString::number(c
));
561 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("\"/>"));
563 } else if (c
== '\x0D') {
564 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
565 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("
"));
567 } else if (c
== '&') {
568 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
569 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("&"));
571 } else if (c
== '<') {
572 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
573 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM("<"));
575 } else if (c
== '>') {
576 // "MUST, for compatibility, be escaped [...] when it appears in the
578 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
579 writeData_(handle
, RTL_CONSTASCII_STRINGPARAM(">"));
583 writeData(handle
, convertToUtf8(value
, i
, j
- i
));
587 Components
& components
, OUString
const & url
, Data
const & data
)
589 sal_Int32 i
= url
.lastIndexOf('/');
591 OUString
dir(url
.copy(0, i
));
592 switch (osl::Directory::createPath(dir
)) {
593 case osl::FileBase::E_None
:
594 case osl::FileBase::E_EXIST
:
596 case osl::FileBase::E_ACCES
:
599 ("cannot create registrymodifications.xcu path (E_ACCES); changes"
603 throw css::uno::RuntimeException(
604 "cannot create directory " + dir
);
607 switch (osl::FileBase::createTempFile(&dir
, &tmp
.handle
, &tmp
.url
)) {
608 case osl::FileBase::E_None
:
610 case osl::FileBase::E_ACCES
:
613 ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
617 throw css::uno::RuntimeException(
618 "cannot create temporary file in " + dir
);
622 RTL_CONSTASCII_STRINGPARAM(
623 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
624 " xmlns:oor=\"http://openoffice.org/2001/registry\""
625 " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
626 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"));
627 //TODO: Do not write back information about those removed items that did not
628 // come from the .xcs/.xcu files, anyway (but had been added dynamically
631 // For profilesafemode it is necessary to detect changes in the
632 // registrymodifications file, this is done based on file size in bytes and crc32.
633 // Unfortunately this write is based on writing unordered map entries, which creates
634 // valid and semantically equal XML-Files, bubt with different crc32 checksums. For
635 // the future usage it will be preferable to have easily comparable config files
636 // which is guaranteed by writing the entries in sorted order. Indeed with this change
637 // (and in the recursive writeModifications call) the same config files get written
639 // copy configmgr::Modifications::Node's to a sortable list. Use pointers
640 // to just reference the data instead of copying it
641 std::vector
< const ModNodePairEntry
* > ModNodePairEntryVector
;
642 ModNodePairEntryVector
.reserve(data
.modifications
.getRoot().children
.size());
644 for (const auto& rCand
: data
.modifications
.getRoot().children
)
646 ModNodePairEntryVector
.push_back(&rCand
);
650 std::sort(ModNodePairEntryVector
.begin(), ModNodePairEntryVector
.end(), PairEntrySorter());
652 // now use the list to write entries in sorted order
653 // instead of random as from the unordered map
654 for (const auto& j
: ModNodePairEntryVector
)
657 components
, tmp
, "", rtl::Reference
< Node
>(), j
->first
,
658 data
.getComponents().findNode(Data::NO_LAYER
, j
->first
),
661 writeData_(tmp
, RTL_CONSTASCII_STRINGPARAM("</oor:items>\n"));
662 tmp
.closeAndRename(url
);
667 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */