bump product version to 4.1.6.2
[LibreOffice.git] / configmgr / source / writemodfile.cxx
blobdaae55c2d860c50c675311bcbae108af1ac07543
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
22 #include <cassert>
24 #include "boost/noncopyable.hpp"
25 #include "com/sun/star/uno/Any.hxx"
26 #include "com/sun/star/uno/Reference.hxx"
27 #include "com/sun/star/uno/RuntimeException.hpp"
28 #include "com/sun/star/uno/Sequence.hxx"
29 #include "com/sun/star/uno/XInterface.hpp"
30 #include "osl/file.h"
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/ustrbuf.hxx"
37 #include "rtl/ustring.h"
38 #include "rtl/ustring.hxx"
39 #include "sal/log.hxx"
40 #include "sal/types.h"
41 #include "xmlreader/span.hxx"
43 #include "data.hxx"
44 #include "groupnode.hxx"
45 #include "localizedpropertynode.hxx"
46 #include "localizedvaluenode.hxx"
47 #include "modifications.hxx"
48 #include "node.hxx"
49 #include "nodemap.hxx"
50 #include "propertynode.hxx"
51 #include "type.hxx"
52 #include "writemodfile.hxx"
54 namespace configmgr {
56 class Components;
58 namespace {
60 OString convertToUtf8(
61 OUString const & text, sal_Int32 offset, sal_Int32 length)
63 assert(offset <= text.getLength() && text.getLength() - offset >= length);
64 OString s;
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 OUString("cannot convert to UTF-8"),
73 css::uno::Reference< css::uno::XInterface >());
75 return s;
78 struct TempFile: public boost::noncopyable {
79 OUString url;
80 oslFileHandle handle;
81 bool closed;
83 TempFile(): handle(0), closed(false) {}
85 ~TempFile();
88 TempFile::~TempFile() {
89 if (handle != 0) {
90 if (!closed) {
91 oslFileError e = osl_closeFile(handle);
92 if (e != osl_File_E_None) {
93 SAL_WARN("configmgr", "osl_closeFile failed with " << +e);
96 osl::FileBase::RC e = osl::File::remove(url);
97 if (e != osl::FileBase::E_None) {
98 SAL_WARN(
99 "configmgr",
100 "osl::File::remove(" << url << ") failed with " << +e);
105 void writeData(oslFileHandle handle, char const * begin, sal_Int32 length) {
106 assert(length >= 0);
107 sal_uInt64 n;
108 if ((osl_writeFile(handle, begin, static_cast< sal_uInt32 >(length), &n) !=
109 osl_File_E_None) ||
110 n != static_cast< sal_uInt32 >(length))
112 throw css::uno::RuntimeException(
113 OUString("write failure"),
114 css::uno::Reference< css::uno::XInterface >());
118 void writeData(oslFileHandle handle, OString const & text) {
119 writeData(handle, text.getStr(), text.getLength());
122 void writeAttributeValue(oslFileHandle handle, OUString const & value) {
123 sal_Int32 i = 0;
124 sal_Int32 j = i;
125 for (; j < value.getLength(); ++j) {
126 assert(
127 value[j] == 0x0009 || value[j] == 0x000A || value[j] == 0x000D ||
128 (value[j] >= 0x0020 && value[j] != 0xFFFE && value[j] != 0xFFFF));
129 switch(value[j]) {
130 case '\x09':
131 writeData(handle, convertToUtf8(value, i, j - i));
132 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&#9;"));
133 i = j + 1;
134 break;
135 case '\x0A':
136 writeData(handle, convertToUtf8(value, i, j - i));
137 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&#xA;"));
138 i = j + 1;
139 break;
140 case '\x0D':
141 writeData(handle, convertToUtf8(value, i, j - i));
142 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&#xD;"));
143 i = j + 1;
144 break;
145 case '"':
146 writeData(handle, convertToUtf8(value, i, j - i));
147 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&quot;"));
148 i = j + 1;
149 break;
150 case '&':
151 writeData(handle, convertToUtf8(value, i, j - i));
152 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&amp;"));
153 i = j + 1;
154 break;
155 case '<':
156 writeData(handle, convertToUtf8(value, i, j - i));
157 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&lt;"));
158 i = j + 1;
159 break;
160 default:
161 break;
164 writeData(handle, convertToUtf8(value, i, j - i));
167 void writeValueContent(oslFileHandle handle, sal_Bool value) {
168 if (value) {
169 writeData(handle, RTL_CONSTASCII_STRINGPARAM("true"));
170 } else {
171 writeData(handle, RTL_CONSTASCII_STRINGPARAM("false"));
175 void writeValueContent(oslFileHandle handle, sal_Int16 value) {
176 writeData(handle, OString::number(value));
179 void writeValueContent(oslFileHandle handle, sal_Int32 value) {
180 writeData(handle, OString::valueOf(value));
183 void writeValueContent(oslFileHandle handle, sal_Int64 value) {
184 writeData(handle, OString::valueOf(value));
187 void writeValueContent(oslFileHandle handle, double value) {
188 writeData(handle, OString::valueOf(value));
191 void writeValueContent(oslFileHandle handle, OUString const & value) {
192 sal_Int32 i = 0;
193 sal_Int32 j = i;
194 for (; j < value.getLength(); ++j) {
195 sal_Unicode c = value[j];
196 if ((c < 0x0020 && c != 0x0009 && c != 0x000A && c != 0x000D) ||
197 c == 0xFFFE || c == 0xFFFF)
199 writeData(handle, convertToUtf8(value, i, j - i));
200 writeData(
201 handle, RTL_CONSTASCII_STRINGPARAM("<unicode oor:scalar=\""));
202 writeData(
203 handle, OString::number(c));
204 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\"/>"));
205 i = j + 1;
206 } else if (c == '\x0D') {
207 writeData(handle, convertToUtf8(value, i, j - i));
208 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&#xD;"));
209 i = j + 1;
210 } else if (c == '&') {
211 writeData(handle, convertToUtf8(value, i, j - i));
212 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&amp;"));
213 i = j + 1;
214 } else if (c == '<') {
215 writeData(handle, convertToUtf8(value, i, j - i));
216 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&lt;"));
217 i = j + 1;
218 } else if (c == '>') {
219 // "MUST, for compatibility, be escaped [...] when it appears in the
220 // string ']]>'":
221 writeData(handle, convertToUtf8(value, i, j - i));
222 writeData(handle, RTL_CONSTASCII_STRINGPARAM("&gt;"));
223 i = j + 1;
226 writeData(handle, convertToUtf8(value, i, j - i));
229 void writeValueContent(
230 oslFileHandle handle, css::uno::Sequence< sal_Int8 > const & value)
232 for (sal_Int32 i = 0; i < value.getLength(); ++i) {
233 static char const hexDigit[16] = {
234 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
235 'D', 'E', 'F' };
236 writeData(handle, hexDigit + ((value[i] >> 4) & 0xF), 1);
237 writeData(handle, hexDigit + (value[i] & 0xF), 1);
241 template< typename T > void writeSingleValue(
242 oslFileHandle handle, css::uno::Any const & value)
244 writeData(handle, RTL_CONSTASCII_STRINGPARAM(">"));
245 T val = T();
246 value >>= val;
247 writeValueContent(handle, val);
248 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</value>"));
251 template< typename T > void writeListValue(
252 oslFileHandle handle, css::uno::Any const & value)
254 writeData(handle, RTL_CONSTASCII_STRINGPARAM(">"));
255 css::uno::Sequence< T > val;
256 value >>= val;
257 for (sal_Int32 i = 0; i < val.getLength(); ++i) {
258 if (i != 0) {
259 writeData(handle, RTL_CONSTASCII_STRINGPARAM(" "));
261 writeValueContent(handle, val[i]);
263 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</value>"));
266 template< typename T > void writeItemListValue(
267 oslFileHandle handle, css::uno::Any const & value)
269 writeData(handle, RTL_CONSTASCII_STRINGPARAM(">"));
270 css::uno::Sequence< T > val;
271 value >>= val;
272 for (sal_Int32 i = 0; i < val.getLength(); ++i) {
273 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<it>"));
274 writeValueContent(handle, val[i]);
275 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</it>"));
277 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</value>"));
280 void writeValue(oslFileHandle handle, Type type, css::uno::Any const & value) {
281 switch (type) {
282 case TYPE_BOOLEAN:
283 writeSingleValue< sal_Bool >(handle, value);
284 break;
285 case TYPE_SHORT:
286 writeSingleValue< sal_Int16 >(handle, value);
287 break;
288 case TYPE_INT:
289 writeSingleValue< sal_Int32 >(handle, value);
290 break;
291 case TYPE_LONG:
292 writeSingleValue< sal_Int64 >(handle, value);
293 break;
294 case TYPE_DOUBLE:
295 writeSingleValue< double >(handle, value);
296 break;
297 case TYPE_STRING:
298 writeSingleValue< OUString >(handle, value);
299 break;
300 case TYPE_HEXBINARY:
301 writeSingleValue< css::uno::Sequence< sal_Int8 > >(handle, value);
302 break;
303 case TYPE_BOOLEAN_LIST:
304 writeListValue< sal_Bool >(handle, value);
305 break;
306 case TYPE_SHORT_LIST:
307 writeListValue< sal_Int16 >(handle, value);
308 break;
309 case TYPE_INT_LIST:
310 writeListValue< sal_Int32 >(handle, value);
311 break;
312 case TYPE_LONG_LIST:
313 writeListValue< sal_Int64 >(handle, value);
314 break;
315 case TYPE_DOUBLE_LIST:
316 writeListValue< double >(handle, value);
317 break;
318 case TYPE_STRING_LIST:
319 writeItemListValue< OUString >(handle, value);
320 break;
321 case TYPE_HEXBINARY_LIST:
322 writeItemListValue< css::uno::Sequence< sal_Int8 > >(handle, value);
323 break;
324 default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
325 assert(false); // this cannot happen
329 void writeNode(
330 Components & components, oslFileHandle handle,
331 rtl::Reference< Node > const & parent, OUString const & name,
332 rtl::Reference< Node > const & node)
334 static xmlreader::Span const typeNames[] = {
335 xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
336 // TYPE_ERROR, TYPE_NIL, TYPE_ANY
337 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
338 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
339 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
340 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
341 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
342 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
343 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
344 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
345 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
346 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
347 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
348 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
349 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
350 xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
351 switch (node->kind()) {
352 case Node::KIND_PROPERTY:
354 PropertyNode * prop = dynamic_cast< PropertyNode * >(node.get());
355 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
356 writeAttributeValue(handle, name);
357 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\""));
358 Type type = prop->getStaticType();
359 Type dynType = getDynamicType(prop->getValue(components));
360 assert(dynType != TYPE_ERROR);
361 if (type == TYPE_ANY) {
362 type = dynType;
363 if (type != TYPE_NIL) {
364 writeData(
365 handle, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
366 writeData(
367 handle, typeNames[type].begin, typeNames[type].length);
368 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\""));
371 writeData(handle, "><value");
372 if (dynType == TYPE_NIL) {
373 writeData(
374 handle, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
375 } else {
376 writeValue(handle, type, prop->getValue(components));
378 writeData(handle, "</prop>");
380 break;
381 case Node::KIND_LOCALIZED_PROPERTY:
382 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
383 writeAttributeValue(handle, name);
384 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"fuse\">"));
385 for (NodeMap::const_iterator i(node->getMembers().begin());
386 i != node->getMembers().end(); ++i)
388 writeNode(components, handle, node, i->first, i->second);
390 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</prop>"));
391 break;
392 case Node::KIND_LOCALIZED_VALUE:
394 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<value"));
395 if (!name.isEmpty()) {
396 writeData(handle, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
397 writeAttributeValue(handle, name);
398 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\""));
400 Type type = dynamic_cast< LocalizedPropertyNode * >(parent.get())->
401 getStaticType();
402 css::uno::Any value(
403 dynamic_cast< LocalizedValueNode * >(node.get())->getValue());
404 Type dynType = getDynamicType(value);
405 assert(dynType != TYPE_ERROR);
406 if (type == TYPE_ANY) {
407 type = dynType;
408 if (type != TYPE_NIL) {
409 writeData(
410 handle, RTL_CONSTASCII_STRINGPARAM(" oor:type=\""));
411 writeData(
412 handle, typeNames[type].begin, typeNames[type].length);
413 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\""));
416 if (dynType == TYPE_NIL) {
417 writeData(
418 handle, RTL_CONSTASCII_STRINGPARAM(" xsi:nil=\"true\"/>"));
419 } else {
420 writeValue(handle, type, value);
423 break;
424 case Node::KIND_GROUP:
425 case Node::KIND_SET:
426 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
427 writeAttributeValue(handle, name);
428 if (!node->getTemplateName().isEmpty()) { // set member
429 writeData(
430 handle, RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"replace"));
432 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\">"));
433 for (NodeMap::const_iterator i(node->getMembers().begin());
434 i != node->getMembers().end(); ++i)
436 writeNode(components, handle, node, i->first, i->second);
438 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</node>"));
439 break;
440 case Node::KIND_ROOT:
441 assert(false); // this cannot happen
442 break;
446 void writeModifications(
447 Components & components, oslFileHandle handle,
448 OUString const & parentPathRepresentation,
449 rtl::Reference< Node > const & parent, OUString const & nodeName,
450 rtl::Reference< Node > const & node,
451 Modifications::Node const & modifications)
453 // It is never necessary to write oor:finalized or oor:mandatory attributes,
454 // as they cannot be set via the UNO API.
455 if (modifications.children.empty()) {
456 assert(parent.is());
457 // components themselves have no parent but must have children
458 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<item oor:path=\""));
459 writeAttributeValue(handle, parentPathRepresentation);
460 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\">"));
461 if (node.is()) {
462 writeNode(components, handle, parent, nodeName, node);
463 } else {
464 switch (parent->kind()) {
465 case Node::KIND_LOCALIZED_PROPERTY:
466 writeData(handle, RTL_CONSTASCII_STRINGPARAM("<value"));
467 if (!nodeName.isEmpty()) {
468 writeData(
469 handle, RTL_CONSTASCII_STRINGPARAM(" xml:lang=\""));
470 writeAttributeValue(handle, nodeName);
471 writeData(handle, RTL_CONSTASCII_STRINGPARAM("\""));
473 writeData(
474 handle, RTL_CONSTASCII_STRINGPARAM(" oor:op=\"remove\"/>"));
475 break;
476 case Node::KIND_GROUP:
477 assert(
478 dynamic_cast< GroupNode * >(parent.get())->isExtensible());
479 writeData(
480 handle, RTL_CONSTASCII_STRINGPARAM("<prop oor:name=\""));
481 writeAttributeValue(handle, nodeName);
482 writeData(
483 handle,
484 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
485 break;
486 case Node::KIND_SET:
487 writeData(
488 handle, RTL_CONSTASCII_STRINGPARAM("<node oor:name=\""));
489 writeAttributeValue(handle, nodeName);
490 writeData(
491 handle,
492 RTL_CONSTASCII_STRINGPARAM("\" oor:op=\"remove\"/>"));
493 break;
494 default:
495 assert(false); // this cannot happen
496 break;
499 writeData(handle, RTL_CONSTASCII_STRINGPARAM("</item>\n"));
500 } else {
501 assert(node.is());
502 OUString pathRep(
503 parentPathRepresentation +
504 OUString("/") +
505 Data::createSegment(node->getTemplateName(), nodeName));
506 for (Modifications::Node::Children::const_iterator i(
507 modifications.children.begin());
508 i != modifications.children.end(); ++i)
510 writeModifications(
511 components, handle, pathRep, node, i->first,
512 node->getMember(i->first), i->second);
519 void writeModFile(
520 Components & components, OUString const & url, Data const & data)
522 sal_Int32 i = url.lastIndexOf('/');
523 assert(i != -1);
524 OUString dir(url.copy(0, i));
525 switch (osl::Directory::createPath(dir)) {
526 case osl::FileBase::E_None:
527 case osl::FileBase::E_EXIST:
528 break;
529 case osl::FileBase::E_ACCES:
530 SAL_INFO(
531 "configmgr",
532 ("cannot create registrymodifications.xcu path (E_ACCES); changes"
533 " will be lost"));
534 return;
535 default:
536 throw css::uno::RuntimeException(
537 (OUString("cannot create directory ") +
538 dir),
539 css::uno::Reference< css::uno::XInterface >());
541 TempFile tmp;
542 switch (osl::FileBase::createTempFile(&dir, &tmp.handle, &tmp.url)) {
543 case osl::FileBase::E_None:
544 break;
545 case osl::FileBase::E_ACCES:
546 SAL_INFO(
547 "configmgr",
548 ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
549 " will be lost"));
550 return;
551 default:
552 throw css::uno::RuntimeException(
553 (OUString("cannot create temporary file in ") +
554 dir),
555 css::uno::Reference< css::uno::XInterface >());
557 writeData(
558 tmp.handle,
559 RTL_CONSTASCII_STRINGPARAM(
560 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
561 " xmlns:oor=\"http://openoffice.org/2001/registry\""
562 " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
563 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"));
564 //TODO: Do not write back information about those removed items that did not
565 // come from the .xcs/.xcu files, anyway (but had been added dynamically
566 // instead):
567 for (Modifications::Node::Children::const_iterator j(
568 data.modifications.getRoot().children.begin());
569 j != data.modifications.getRoot().children.end(); ++j)
571 writeModifications(
572 components, tmp.handle, OUString(), rtl::Reference< Node >(),
573 j->first,
574 Data::findNode(Data::NO_LAYER, data.getComponents(), j->first),
575 j->second);
577 writeData(tmp.handle, RTL_CONSTASCII_STRINGPARAM("</oor:items>\n"));
578 oslFileError e = osl_closeFile(tmp.handle);
579 tmp.closed = true;
580 if (e != osl_File_E_None) {
581 throw css::uno::RuntimeException(
582 (OUString("cannot close ") +
583 tmp.url),
584 css::uno::Reference< css::uno::XInterface >());
586 if (osl::File::move(tmp.url, url) != osl::FileBase::E_None) {
587 throw css::uno::RuntimeException(
588 (OUString("cannot move ") +
589 tmp.url),
590 css::uno::Reference< css::uno::XInterface >());
592 tmp.handle = 0;
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */