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