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 <set>
25 
26 #include <com/sun/star/uno/Any.hxx>
27 #include <com/sun/star/uno/RuntimeException.hpp>
28 #include <rtl/ref.hxx>
29 #include <rtl/strbuf.hxx>
30 #include <rtl/string.hxx>
31 #include <rtl/ustring.hxx>
32 #include <sal/log.hxx>
33 #include <xmlreader/span.hxx>
34 #include <xmlreader/xmlreader.hxx>
35 
36 #include "data.hxx"
37 #include "localizedpropertynode.hxx"
38 #include "localizedvaluenode.hxx"
39 #include "groupnode.hxx"
40 #include "modifications.hxx"
41 #include "node.hxx"
42 #include "nodemap.hxx"
43 #include "parsemanager.hxx"
44 #include "partial.hxx"
45 #include "propertynode.hxx"
46 #include "setnode.hxx"
47 #include "xcuparser.hxx"
48 #include "xmldata.hxx"
49 
50 namespace configmgr {
51 
XcuParser(int layer,Data & data,Partial const * partial,Modifications * broadcastModifications,Additions * additions)52 XcuParser::XcuParser(
53     int layer, Data & data, Partial const * partial,
54     Modifications * broadcastModifications, Additions * additions):
55     valueParser_(layer), data_(data),
56     partial_(partial), broadcastModifications_(broadcastModifications),
57     additions_(additions), recordModifications_(layer == Data::NO_LAYER),
58     trackPath_(
59         partial_ != nullptr || broadcastModifications_ != nullptr || additions_ != nullptr ||
60         recordModifications_)
61 {}
62 
~XcuParser()63 XcuParser::~XcuParser() {}
64 
getTextMode()65 xmlreader::XmlReader::Text XcuParser::getTextMode() {
66     return valueParser_.getTextMode();
67 }
68 
startElement(xmlreader::XmlReader & reader,int nsId,xmlreader::Span const & name,std::set<OUString> const *)69 bool XcuParser::startElement(
70     xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
71     std::set< OUString > const * /*existingDependencies*/)
72 {
73     if (valueParser_.startElement(reader, nsId, name)) {
74         return true;
75     }
76     if (state_.empty()) {
77         if (nsId == ParseManager::NAMESPACE_OOR &&
78             name == "component-data")
79         {
80             handleComponentData(reader);
81         } else if (nsId == ParseManager::NAMESPACE_OOR && name == "items")
82         {
83             state_.push(State::Modify(rtl::Reference< Node >()));
84         } else {
85             throw css::uno::RuntimeException(
86                 "bad root element <" + name.convertFromUtf8() + "> in " +
87                 reader.getUrl());
88         }
89     } else if (state_.top().ignore) {
90         state_.push(State::Ignore(false));
91     } else if (!state_.top().node.is()) {
92         if (nsId != xmlreader::XmlReader::NAMESPACE_NONE || name != "item")
93         {
94             throw css::uno::RuntimeException(
95                 "bad items node member <" + name.convertFromUtf8() + "> in " +
96                 reader.getUrl());
97         }
98         handleItem(reader);
99     } else {
100         switch (state_.top().node->kind()) {
101         case Node::KIND_PROPERTY:
102             if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
103                 name != "value")
104             {
105                 throw css::uno::RuntimeException(
106                     "bad property node member <" + name.convertFromUtf8() +
107                     "> in " + reader.getUrl());
108             }
109             handlePropValue(
110                 reader,
111                 static_cast< PropertyNode * >(state_.top().node.get()));
112             break;
113         case Node::KIND_LOCALIZED_PROPERTY:
114             if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
115                 name != "value")
116             {
117                 throw css::uno::RuntimeException(
118                     "bad localized property node member <" +
119                     name.convertFromUtf8() + "> in " + reader.getUrl());
120             }
121             handleLocpropValue(
122                 reader,
123                 static_cast< LocalizedPropertyNode * >(
124                     state_.top().node.get()));
125             break;
126         case Node::KIND_LOCALIZED_VALUE:
127             throw css::uno::RuntimeException(
128                 "bad member <" + name.convertFromUtf8() + "> in " +
129                 reader.getUrl());
130         case Node::KIND_GROUP:
131             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
132                 name == "prop")
133             {
134                 handleGroupProp(
135                     reader,
136                     static_cast< GroupNode * >(state_.top().node.get()));
137             } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
138                        name == "node")
139             {
140                 handleGroupNode(reader, state_.top().node);
141             } else {
142                 throw css::uno::RuntimeException(
143                     "bad group node member <" + name.convertFromUtf8() +
144                     "> in " + reader.getUrl());
145             }
146             break;
147         case Node::KIND_SET:
148             if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
149                 name == "node")
150             {
151                 handleSetNode(
152                     reader, static_cast< SetNode * >(state_.top().node.get()));
153             } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
154                        name == "prop")
155             {
156                 SAL_WARN(
157                     "configmgr",
158                     "bad set node <prop> member in \"" << reader.getUrl()
159                         << '"');
160                 state_.push(State::Ignore(false));
161             } else {
162                 throw css::uno::RuntimeException(
163                     "bad set node member <" + name.convertFromUtf8() +
164                     "> in " + reader.getUrl());
165             }
166             break;
167         case Node::KIND_ROOT:
168             assert(false); // this cannot happen
169             break;
170         }
171     }
172     return true;
173 }
174 
endElement(xmlreader::XmlReader const &)175 void XcuParser::endElement(xmlreader::XmlReader const &) {
176     if (valueParser_.endElement()) {
177         return;
178     }
179     assert(!state_.empty());
180     bool pop = state_.top().pop;
181     rtl::Reference< Node > insert;
182     OUString name;
183     if (state_.top().insert) {
184         insert = state_.top().node;
185         assert(insert.is());
186         name = state_.top().name;
187     }
188     state_.pop();
189     if (insert.is()) {
190         assert(!state_.empty() && state_.top().node.is());
191         state_.top().node->getMembers()[name] = insert;
192     }
193     if (pop && !path_.empty()) {
194         path_.pop_back();
195             // </item> will pop less than <item> pushed, but that is harmless,
196             // as the next <item> will reset path_
197     }
198 }
199 
characters(xmlreader::Span const & text)200 void XcuParser::characters(xmlreader::Span const & text) {
201     valueParser_.characters(text);
202 }
203 
parseOperation(xmlreader::Span const & text)204 XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
205     assert(text.is());
206     if (text == "modify") {
207         return OPERATION_MODIFY;
208     }
209     if (text == "replace") {
210         return OPERATION_REPLACE;
211     }
212     if (text == "fuse") {
213         return OPERATION_FUSE;
214     }
215     if (text == "remove") {
216         return OPERATION_REMOVE;
217     }
218     throw css::uno::RuntimeException(
219         "invalid op " + text.convertFromUtf8());
220 }
221 
handleComponentData(xmlreader::XmlReader & reader)222 void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
223     OStringBuffer buf(256);
224     buf.append('.');
225     bool hasPackage = false;
226     bool hasName = false;
227     Operation op = OPERATION_MODIFY;
228     bool finalized = false;
229     for (;;) {
230         int attrNsId;
231         xmlreader::Span attrLn;
232         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
233             break;
234         }
235         if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
236         {
237             if (hasPackage) {
238                 throw css::uno::RuntimeException(
239                     "multiple component-update package attributes in " +
240                     reader.getUrl());
241             }
242             hasPackage = true;
243             xmlreader::Span s(reader.getAttributeValue(false));
244             buf.insert(0, s.begin, s.length);
245         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
246                    attrLn == "name")
247         {
248             if (hasName) {
249                 throw css::uno::RuntimeException(
250                     "multiple component-update name attributes in " +
251                     reader.getUrl());
252             }
253             hasName = true;
254             xmlreader::Span s(reader.getAttributeValue(false));
255             buf.append(s.begin, s.length);
256         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
257                    attrLn == "op")
258         {
259             op = parseOperation(reader.getAttributeValue(true));
260         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
261                    attrLn == "finalized")
262         {
263             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
264         }
265     }
266     if (!hasPackage) {
267         throw css::uno::RuntimeException(
268             "no component-data package attribute in " + reader.getUrl());
269     }
270     if (!hasName) {
271         throw css::uno::RuntimeException(
272             "no component-data name attribute in " + reader.getUrl());
273     }
274     componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
275         convertFromUtf8();
276     if (trackPath_) {
277         assert(path_.empty());
278         path_.push_back(componentName_);
279         if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
280         {
281             state_.push(State::Ignore(true));
282             return;
283         }
284     }
285     rtl::Reference< Node > node(
286         data_.getComponents().findNode(valueParser_.getLayer(),
287                                        componentName_));
288     if (!node.is()) {
289         SAL_WARN(
290             "configmgr",
291             "unknown component \"" << componentName_ << "\" in \""
292                 << reader.getUrl() << '"');
293         state_.push(State::Ignore(true));
294         return;
295     }
296     switch (op) {
297     case OPERATION_MODIFY:
298     case OPERATION_FUSE:
299         break;
300     default:
301         throw css::uno::RuntimeException(
302             "invalid operation on root node in " + reader.getUrl());
303     }
304     int finalizedLayer = std::min(
305         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
306         node->getFinalized());
307     node->setFinalized(finalizedLayer);
308     if (finalizedLayer < valueParser_.getLayer()) {
309         state_.push(State::Ignore(true));
310         return;
311     }
312     state_.push(State::Modify(node));
313 }
314 
handleItem(xmlreader::XmlReader & reader)315 void XcuParser::handleItem(xmlreader::XmlReader & reader) {
316     xmlreader::Span attrPath;
317     for (;;) {
318         int attrNsId;
319         xmlreader::Span attrLn;
320         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
321             break;
322         }
323         if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "path") {
324             attrPath = reader.getAttributeValue(false);
325         }
326     }
327     if (!attrPath.is()) {
328         throw css::uno::RuntimeException(
329             "missing path attribute in " + reader.getUrl());
330     }
331     OUString path(attrPath.convertFromUtf8());
332     int finalizedLayer;
333     rtl::Reference< Node > node(
334         data_.resolvePathRepresentation(
335             path, nullptr, &path_, &finalizedLayer));
336     if (!node.is()) {
337         SAL_WARN(
338             "configmgr",
339             "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
340         state_.push(State::Ignore(true));
341         return;
342     }
343     assert(!path_.empty());
344     componentName_ = path_.front();
345     if (trackPath_) {
346         if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
347         {
348             state_.push(State::Ignore(true));
349             return;
350         }
351     } else {
352         path_.clear();
353     }
354     switch (node->kind()) {
355     case Node::KIND_PROPERTY:
356     case Node::KIND_LOCALIZED_VALUE:
357         SAL_WARN(
358             "configmgr",
359             "item of bad type \"" << path << "\" in \"" << reader.getUrl()
360                 << '"');
361         state_.push(State::Ignore(true));
362         return;
363     case Node::KIND_LOCALIZED_PROPERTY:
364         valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
365             node.get())->getStaticType();
366         break;
367     default:
368         break;
369     }
370     if (finalizedLayer < valueParser_.getLayer()) {
371         state_.push(State::Ignore(true));
372         return;
373     }
374     state_.push(State::Modify(node));
375 }
376 
handlePropValue(xmlreader::XmlReader & reader,PropertyNode * prop)377 void XcuParser::handlePropValue(
378     xmlreader::XmlReader & reader, PropertyNode * prop)
379  {
380     bool nil = false;
381     OString separator;
382     OUString external;
383     for (;;) {
384         int attrNsId;
385         xmlreader::Span attrLn;
386         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
387             break;
388         }
389         if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn == "nil") {
390             nil = xmldata::parseBoolean(reader.getAttributeValue(true));
391         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
392                    attrLn == "type")
393         {
394             Type type = xmldata::parseType(
395                 reader, reader.getAttributeValue(true));
396             if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
397                 throw css::uno::RuntimeException(
398                     "invalid value type in " + reader.getUrl());
399             }
400             valueParser_.type_ = type;
401         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
402                    attrLn == "separator")
403         {
404             xmlreader::Span s(reader.getAttributeValue(false));
405             if (s.length == 0) {
406                 throw css::uno::RuntimeException(
407                     "bad oor:separator attribute in " + reader.getUrl());
408             }
409             separator = OString(s.begin, s.length);
410         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
411                    attrLn == "external")
412         {
413             external = reader.getAttributeValue(true).convertFromUtf8();
414             if (external.isEmpty()) {
415                 throw css::uno::RuntimeException(
416                     "bad oor:external attribute value in " + reader.getUrl());
417             }
418         }
419     }
420     if (nil) {
421         if (!prop->isNillable()) {
422             throw css::uno::RuntimeException(
423                 "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
424         }
425         if (!external.isEmpty()) {
426             throw css::uno::RuntimeException(
427                 "xsi:nil and oor:external attributes for prop in " +
428                 reader.getUrl());
429         }
430         prop->setValue(valueParser_.getLayer(), css::uno::Any());
431         state_.push(State::Ignore(false));
432     } else if (external.isEmpty()) {
433         valueParser_.separator_ = separator;
434         valueParser_.start(prop);
435     } else {
436         prop->setExternal(valueParser_.getLayer(), external);
437         state_.push(State::Ignore(false));
438     }
439 }
440 
handleLocpropValue(xmlreader::XmlReader & reader,LocalizedPropertyNode * locprop)441 void XcuParser::handleLocpropValue(
442     xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
443 {
444     OUString name;
445     bool nil = false;
446     OString separator;
447     Operation op = OPERATION_FUSE;
448     for (;;) {
449         int attrNsId;
450         xmlreader::Span attrLn;
451         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
452             break;
453         }
454         if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
455             attrLn == "lang")
456         {
457             name = reader.getAttributeValue(false).convertFromUtf8();
458         } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
459                    attrLn == "nil")
460         {
461             nil = xmldata::parseBoolean(reader.getAttributeValue(true));
462         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
463                    attrLn == "type")
464         {
465             Type type = xmldata::parseType(
466                 reader, reader.getAttributeValue(true));
467             if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
468                 throw css::uno::RuntimeException(
469                     "invalid value type in " + reader.getUrl());
470             }
471             valueParser_.type_ = type;
472         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
473                    attrLn == "separator")
474         {
475             xmlreader::Span s(reader.getAttributeValue(false));
476             if (s.length == 0) {
477                 throw css::uno::RuntimeException(
478                     "bad oor:separator attribute in " + reader.getUrl());
479             }
480             separator = OString(s.begin, s.length);
481         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
482                    attrLn == "op")
483         {
484             op = parseOperation(reader.getAttributeValue(true));
485         }
486     }
487     if (trackPath_) {
488         path_.push_back(name);
489         if (partial_ != nullptr &&
490             partial_->contains(path_) != Partial::CONTAINS_NODE)
491         {
492             state_.push(State::Ignore(true));
493             return;
494         }
495     }
496     NodeMap & members = locprop->getMembers();
497     NodeMap::iterator i(members.find(name));
498     if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
499         state_.push(State::Ignore(true));
500         return;
501     }
502     if (nil && !locprop->isNillable()) {
503         throw css::uno::RuntimeException(
504             "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
505     }
506     switch (op) {
507     case OPERATION_FUSE:
508         {
509             bool pop = false;
510             if (nil) {
511                 if (i == members.end()) {
512                     members[name] = new LocalizedValueNode(
513                         valueParser_.getLayer(), css::uno::Any());
514                 } else {
515                     static_cast< LocalizedValueNode * >(
516                         i->second.get())->setValue(
517                             valueParser_.getLayer(), css::uno::Any());
518                 }
519                 state_.push(State::Ignore(true));
520             } else {
521                 valueParser_.separator_ = separator;
522                 valueParser_.start(locprop, name);
523                 pop = true;
524             }
525             if (trackPath_) {
526                 recordModification(false);
527                 if (pop) {
528                     path_.pop_back();
529                 }
530             }
531         }
532         break;
533     case OPERATION_REMOVE:
534         //TODO: only allow if parent.op == OPERATION_FUSE
535         //TODO: disallow removing when e.g. lang=""?
536         if (i != members.end()) {
537             members.erase(i);
538         }
539         state_.push(State::Ignore(true));
540         recordModification(false);
541         break;
542     default:
543         throw css::uno::RuntimeException(
544             "bad op attribute for value element in " + reader.getUrl());
545     }
546 }
547 
handleGroupProp(xmlreader::XmlReader & reader,GroupNode * group)548 void XcuParser::handleGroupProp(
549     xmlreader::XmlReader & reader, GroupNode * group)
550 {
551     bool hasName = false;
552     OUString name;
553     Type type = TYPE_ERROR;
554     Operation op = OPERATION_MODIFY;
555     bool finalized = false;
556     for (;;) {
557         int attrNsId;
558         xmlreader::Span attrLn;
559         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
560             break;
561         }
562         if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
563             hasName = true;
564             name = reader.getAttributeValue(false).convertFromUtf8();
565         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
566                    attrLn == "type")
567         {
568             type = xmldata::parseType(reader, reader.getAttributeValue(true));
569         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
570                    attrLn == "op")
571         {
572             op = parseOperation(reader.getAttributeValue(true));
573         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
574                    attrLn == "finalized")
575         {
576             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
577         }
578     }
579     if (!hasName) {
580         throw css::uno::RuntimeException(
581             "no prop name attribute in " + reader.getUrl());
582     }
583     if (trackPath_) {
584         path_.push_back(name);
585         //TODO: This ignores locprop values for which specific include paths
586         // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
587         if (partial_ != nullptr &&
588             partial_->contains(path_) != Partial::CONTAINS_NODE)
589         {
590             state_.push(State::Ignore(true));
591             return;
592         }
593     }
594     NodeMap & members = group->getMembers();
595     NodeMap::iterator i(members.find(name));
596     if (i == members.end()) {
597         handleUnknownGroupProp(reader, group, name, type, op, finalized);
598     } else {
599         switch (i->second->kind()) {
600         case Node::KIND_PROPERTY:
601             handlePlainGroupProp(reader, group, i, name, type, op, finalized);
602             break;
603         case Node::KIND_LOCALIZED_PROPERTY:
604             handleLocalizedGroupProp(
605                 reader,
606                 static_cast< LocalizedPropertyNode * >(i->second.get()), name,
607                 type, op, finalized);
608             break;
609         default:
610             throw css::uno::RuntimeException(
611                 "inappropriate prop " + name + " in " + reader.getUrl());
612         }
613     }
614 }
615 
handleUnknownGroupProp(xmlreader::XmlReader const & reader,GroupNode const * group,OUString const & name,Type type,Operation operation,bool finalized)616 void XcuParser::handleUnknownGroupProp(
617     xmlreader::XmlReader const & reader, GroupNode const * group,
618     OUString const & name, Type type, Operation operation, bool finalized)
619 {
620     switch (operation) {
621     case OPERATION_REPLACE:
622     case OPERATION_FUSE:
623         if (group->isExtensible()) {
624             if (type == TYPE_ERROR) {
625                 throw css::uno::RuntimeException(
626                     "missing type attribute for prop " + name + " in " +
627                     reader.getUrl());
628             }
629             valueParser_.type_ = type;
630             rtl::Reference< Node > prop(
631                 new PropertyNode(
632                     valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
633                     true));
634             if (finalized) {
635                 prop->setFinalized(valueParser_.getLayer());
636             }
637             state_.push(State::Insert(prop, name));
638             recordModification(false);
639             break;
640         }
641         [[fallthrough]];
642     default:
643         SAL_WARN(
644             "configmgr",
645             "unknown property \"" << name << "\" in \"" << reader.getUrl()
646                 << '"');
647         state_.push(State::Ignore(true));
648         break;
649     }
650 }
651 
handlePlainGroupProp(xmlreader::XmlReader const & reader,GroupNode * group,NodeMap::iterator const & propertyIndex,OUString const & name,Type type,Operation operation,bool finalized)652 void XcuParser::handlePlainGroupProp(
653     xmlreader::XmlReader const & reader, GroupNode * group,
654     NodeMap::iterator const & propertyIndex, OUString const & name,
655     Type type, Operation operation, bool finalized)
656 {
657     PropertyNode * property = static_cast< PropertyNode * >(
658         propertyIndex->second.get());
659     if (property->getLayer() > valueParser_.getLayer()) {
660         state_.push(State::Ignore(true));
661         return;
662     }
663     int finalizedLayer = std::min(
664         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
665         property->getFinalized());
666     property->setFinalized(finalizedLayer);
667     if (finalizedLayer < valueParser_.getLayer()) {
668         state_.push(State::Ignore(true));
669         return;
670     }
671     if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
672         type != property->getStaticType())
673     {
674         throw css::uno::RuntimeException(
675             "invalid type for prop " + name + " in " + reader.getUrl());
676     }
677     valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
678     switch (operation) {
679     case OPERATION_MODIFY:
680     case OPERATION_REPLACE:
681     case OPERATION_FUSE:
682         state_.push(State::Modify(property));
683         recordModification(false);
684         break;
685     case OPERATION_REMOVE:
686         if (!property->isExtension()) {
687             throw css::uno::RuntimeException(
688                 "invalid remove of non-extension prop " + name + " in " +
689                 reader.getUrl());
690         }
691         group->getMembers().erase(propertyIndex);
692         state_.push(State::Ignore(true));
693         recordModification(false);
694         break;
695     }
696 }
697 
handleLocalizedGroupProp(xmlreader::XmlReader const & reader,LocalizedPropertyNode * property,OUString const & name,Type type,Operation operation,bool finalized)698 void XcuParser::handleLocalizedGroupProp(
699     xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
700     OUString const & name, Type type, Operation operation, bool finalized)
701 {
702     if (property->getLayer() > valueParser_.getLayer()) {
703         state_.push(State::Ignore(true));
704         return;
705     }
706     int finalizedLayer = std::min(
707         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
708         property->getFinalized());
709     property->setFinalized(finalizedLayer);
710     if (finalizedLayer < valueParser_.getLayer()) {
711         state_.push(State::Ignore(true));
712         return;
713     }
714     if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
715         type != property->getStaticType())
716     {
717         throw css::uno::RuntimeException(
718             "invalid type for prop " + name + " in " + reader.getUrl());
719     }
720     valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
721     switch (operation) {
722     case OPERATION_MODIFY:
723     case OPERATION_FUSE:
724         state_.push(State::Modify(property));
725         break;
726     case OPERATION_REPLACE:
727         {
728             rtl::Reference< Node > replacement(
729                 new LocalizedPropertyNode(
730                     valueParser_.getLayer(), property->getStaticType(),
731                     property->isNillable()));
732             replacement->setFinalized(property->getFinalized());
733             state_.push(State::Insert(replacement, name));
734             recordModification(false);
735         }
736         break;
737     case OPERATION_REMOVE:
738         throw css::uno::RuntimeException(
739             "invalid remove of non-extension prop " + name + " in " +
740             reader.getUrl());
741     }
742 }
743 
handleGroupNode(xmlreader::XmlReader & reader,rtl::Reference<Node> const & group)744 void XcuParser::handleGroupNode(
745     xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
746 {
747     bool hasName = false;
748     OUString name;
749     Operation op = OPERATION_MODIFY;
750     bool finalized = false;
751     for (;;) {
752         int attrNsId;
753         xmlreader::Span attrLn;
754         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
755             break;
756         }
757         if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
758             hasName = true;
759             name = reader.getAttributeValue(false).convertFromUtf8();
760         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
761                    attrLn == "op")
762         {
763             op = parseOperation(reader.getAttributeValue(true));
764         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
765                    attrLn == "finalized")
766         {
767             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
768         }
769     }
770     if (!hasName) {
771         throw css::uno::RuntimeException(
772             "no node name attribute in " + reader.getUrl());
773     }
774     if (trackPath_) {
775         path_.push_back(name);
776         if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
777         {
778             state_.push(State::Ignore(true));
779             return;
780         }
781     }
782     rtl::Reference< Node > child(
783         group->getMembers().findNode(valueParser_.getLayer(), name));
784     if (!child.is()) {
785         SAL_WARN(
786             "configmgr",
787             "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
788         state_.push(State::Ignore(true));
789         return;
790     }
791     Node::Kind kind = child->kind();
792     if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
793         throw css::uno::RuntimeException(
794             "bad <node> \"" + name + "\" of non group/set kind in " +
795             reader.getUrl());
796     }
797     if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
798         throw css::uno::RuntimeException(
799             "invalid operation on group node in " + reader.getUrl());
800     }
801     int finalizedLayer = std::min(
802         finalized ? valueParser_.getLayer() : Data::NO_LAYER,
803         child->getFinalized());
804     child->setFinalized(finalizedLayer);
805     if (finalizedLayer < valueParser_.getLayer()) {
806         state_.push(State::Ignore(true));
807         return;
808     }
809     state_.push(State::Modify(child));
810 }
811 
handleSetNode(xmlreader::XmlReader & reader,SetNode * set)812 void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
813     bool hasName = false;
814     OUString name;
815     OUString component(componentName_);
816     bool hasNodeType = false;
817     OUString nodeType;
818     Operation op = OPERATION_MODIFY;
819     bool finalized = false;
820     bool mandatory = false;
821     for (;;) {
822         int attrNsId;
823         xmlreader::Span attrLn;
824         if (!reader.nextAttribute(&attrNsId, &attrLn)) {
825             break;
826         }
827         if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
828             hasName = true;
829             name = reader.getAttributeValue(false).convertFromUtf8();
830         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
831                    attrLn == "component")
832         {
833             component = reader.getAttributeValue(false).convertFromUtf8();
834         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
835                    attrLn == "node-type")
836         {
837             hasNodeType = true;
838             nodeType = reader.getAttributeValue(false).convertFromUtf8();
839         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
840                    attrLn == "op")
841         {
842             op = parseOperation(reader.getAttributeValue(true));
843         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
844                    attrLn == "finalized")
845         {
846             finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
847         } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
848                    attrLn == "mandatory")
849         {
850             mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
851         }
852     }
853     if (!hasName) {
854         throw css::uno::RuntimeException(
855             "no node name attribute in " + reader.getUrl());
856     }
857     if (trackPath_) {
858         path_.push_back(name);
859         if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
860         {
861             state_.push(State::Ignore(true));
862             return;
863         }
864     }
865     OUString templateName(
866         xmldata::parseTemplateReference(
867             component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
868     if (!set->isValidTemplate(templateName)) {
869         throw css::uno::RuntimeException(
870             "set member node " + name + " references invalid template " +
871             templateName + " in " + reader.getUrl());
872     }
873     rtl::Reference< Node > tmpl(
874         data_.getTemplate(valueParser_.getLayer(), templateName));
875     if (!tmpl.is()) {
876         throw css::uno::RuntimeException(
877             "set member node " + name + " references undefined template " +
878             templateName + " in " + reader.getUrl());
879     }
880     int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
881     int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
882     NodeMap & members = set->getMembers();
883     NodeMap::iterator i(members.find(name));
884     if (i != members.end()) {
885         finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
886         i->second->setFinalized(finalizedLayer);
887         mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
888         i->second->setMandatory(mandatoryLayer);
889         if (i->second->getLayer() > valueParser_.getLayer()) {
890             state_.push(State::Ignore(true));
891             return;
892         }
893     }
894     if (finalizedLayer < valueParser_.getLayer()) {
895         state_.push(State::Ignore(true));
896         return;
897     }
898     switch (op) {
899     case OPERATION_MODIFY:
900         if (i == members.end()) {
901             SAL_WARN(
902                 "configmgr",
903                 "ignoring modify of unknown set member node \"" << name
904                     << "\" in \"" << reader.getUrl() << '"');
905             state_.push(State::Ignore(true));
906         } else {
907             state_.push(State::Modify(i->second));
908         }
909         break;
910     case OPERATION_REPLACE:
911         {
912             rtl::Reference< Node > member(tmpl->clone(true));
913             member->setLayer(valueParser_.getLayer());
914             member->setFinalized(finalizedLayer);
915             member->setMandatory(mandatoryLayer);
916             state_.push(State::Insert(member, name));
917             recordModification(i == members.end());
918         }
919         break;
920     case OPERATION_FUSE:
921         if (i == members.end()) {
922             rtl::Reference< Node > member(tmpl->clone(true));
923             member->setLayer(valueParser_.getLayer());
924             member->setFinalized(finalizedLayer);
925             member->setMandatory(mandatoryLayer);
926             state_.push(State::Insert(member, name));
927             recordModification(true);
928         } else {
929             state_.push(State::Modify(i->second));
930         }
931         break;
932     case OPERATION_REMOVE:
933         {
934             // Ignore removal of unknown members and members made mandatory in
935             // this or a lower layer; forget about user-layer removals that no
936             // longer remove anything (so that paired additions/removals in the
937             // user layer do not grow registrymodifications.xcu unbounded):
938             bool known = i != members.end();
939             if (known &&
940                 (mandatoryLayer == Data::NO_LAYER ||
941                  mandatoryLayer > valueParser_.getLayer()))
942             {
943                 members.erase(i);
944             }
945             state_.push(State::Ignore(true));
946             if (known) {
947                 recordModification(false);
948             }
949             break;
950         }
951     }
952 }
953 
recordModification(bool addition)954 void XcuParser::recordModification(bool addition) {
955     if (broadcastModifications_ != nullptr) {
956         broadcastModifications_->add(path_);
957     }
958     if (addition && additions_ != nullptr) {
959         additions_->push_back(path_);
960     }
961     if (recordModifications_) {
962         data_.modifications.add(path_);
963     }
964 }
965 
966 }
967 
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
969