1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_configmgr.hxx"
25 #include "sal/config.h"
26 
27 #include <algorithm>
28 #include <cstddef>
29 #include <list>
30 
31 #include "com/sun/star/beans/Optional.hpp"
32 #include "com/sun/star/beans/UnknownPropertyException.hpp"
33 #include "com/sun/star/beans/XPropertySet.hpp"
34 #include "com/sun/star/container/NoSuchElementException.hpp"
35 #include "com/sun/star/lang/WrappedTargetException.hpp"
36 #include "com/sun/star/lang/XMultiComponentFactory.hpp"
37 #include "com/sun/star/uno/Any.hxx"
38 #include "com/sun/star/uno/Exception.hpp"
39 #include "com/sun/star/uno/Reference.hxx"
40 #include "com/sun/star/uno/RuntimeException.hpp"
41 #include "com/sun/star/uno/XComponentContext.hpp"
42 #include "com/sun/star/uno/XInterface.hpp"
43 #include "osl/conditn.hxx"
44 #include "osl/diagnose.h"
45 #include "osl/file.hxx"
46 #include "osl/mutex.hxx"
47 #include "osl/thread.hxx"
48 #include "rtl/bootstrap.hxx"
49 #include "rtl/logfile.h"
50 #include "rtl/ref.hxx"
51 #include "rtl/string.h"
52 #include "rtl/textenc.h"
53 #include "rtl/ustring.h"
54 #include "rtl/ustring.hxx"
55 #include "sal/types.h"
56 #include "salhelper/simplereferenceobject.hxx"
57 
58 #include "additions.hxx"
59 #include "components.hxx"
60 #include "data.hxx"
61 #include "lock.hxx"
62 #include "modifications.hxx"
63 #include "node.hxx"
64 #include "nodemap.hxx"
65 #include "parsemanager.hxx"
66 #include "partial.hxx"
67 #include "rootaccess.hxx"
68 #include "writemodfile.hxx"
69 #include "xcdparser.hxx"
70 #include "xcuparser.hxx"
71 #include "xcsparser.hxx"
72 
73 namespace configmgr {
74 
75 namespace {
76 
77 namespace css = com::sun::star;
78 
79 struct UnresolvedListItem {
80     rtl::OUString name;
81     rtl::Reference< ParseManager > manager;
82 
UnresolvedListItemconfigmgr::__anonc2e24f310111::UnresolvedListItem83     UnresolvedListItem(
84         rtl::OUString const & theName,
85         rtl::Reference< ParseManager > theManager):
86         name(theName), manager(theManager) {}
87 };
88 
89 typedef std::list< UnresolvedListItem > UnresolvedList;
90 
parseXcsFile(rtl::OUString const & url,int layer,Data & data,Partial const * partial,Modifications * modifications,Additions * additions)91 void parseXcsFile(
92     rtl::OUString const & url, int layer, Data & data, Partial const * partial,
93     Modifications * modifications, Additions * additions)
94     SAL_THROW((
95         css::container::NoSuchElementException, css::uno::RuntimeException))
96 {
97     OSL_ASSERT(partial == 0 && modifications == 0 && additions == 0);
98     (void) partial; (void) modifications; (void) additions;
99     OSL_VERIFY(
100         rtl::Reference< ParseManager >(
101             new ParseManager(url, new XcsParser(layer, data)))->parse());
102 }
103 
parseXcuFile(rtl::OUString const & url,int layer,Data & data,Partial const * partial,Modifications * modifications,Additions * additions)104 void parseXcuFile(
105     rtl::OUString const & url, int layer, Data & data, Partial const * partial,
106     Modifications * modifications, Additions * additions)
107     SAL_THROW((
108         css::container::NoSuchElementException, css::uno::RuntimeException))
109 {
110     OSL_VERIFY(
111         rtl::Reference< ParseManager >(
112             new ParseManager(
113                 url,
114                 new XcuParser(
115                     layer, data, partial, modifications, additions)))->
116         parse());
117 }
118 
expand(rtl::OUString const & str)119 rtl::OUString expand(rtl::OUString const & str) {
120     rtl::OUString s(str);
121     rtl::Bootstrap::expandMacros(s); //TODO: detect failure
122     return s;
123 }
124 
canRemoveFromLayer(int layer,rtl::Reference<Node> const & node)125 bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
126     OSL_ASSERT(node.is());
127     if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
128         return false;
129     }
130     switch (node->kind()) {
131     case Node::KIND_LOCALIZED_PROPERTY:
132     case Node::KIND_GROUP:
133         for (NodeMap::iterator i(node->getMembers().begin());
134              i != node->getMembers().end(); ++i)
135         {
136             if (!canRemoveFromLayer(layer, i->second)) {
137                 return false;
138             }
139         }
140         return true;
141     case Node::KIND_SET:
142         return node->getMembers().empty();
143     default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
144         return true;
145     }
146 }
147 
148 static bool singletonCreated = false;
149 static Components * singleton = 0;
150 
151 }
152 
153 class Components::WriteThread:
154     public osl::Thread, public salhelper::SimpleReferenceObject
155 {
156 public:
operator new(std::size_t size)157     static void * operator new(std::size_t size)
158     { return Thread::operator new(size); }
159 
operator delete(void * pointer)160     static void operator delete(void * pointer)
161     { Thread::operator delete(pointer); }
162 
163     WriteThread(
164         rtl::Reference< WriteThread > * reference, Components & components,
165         rtl::OUString const & url, Data const & data);
166 
flush()167     void flush() { delay_.set(); }
168 
169 private:
~WriteThread()170     virtual ~WriteThread() {}
171 
172     virtual void SAL_CALL run();
173 
onTerminated()174     virtual void SAL_CALL onTerminated() { release(); }
175 
176     rtl::Reference< WriteThread > * reference_;
177     Components & components_;
178     rtl::OUString url_;
179     Data const & data_;
180     osl::Condition delay_;
181 };
182 
WriteThread(rtl::Reference<WriteThread> * reference,Components & components,rtl::OUString const & url,Data const & data)183 Components::WriteThread::WriteThread(
184     rtl::Reference< WriteThread > * reference, Components & components,
185     rtl::OUString const & url, Data const & data):
186     reference_(reference), components_(components), url_(url), data_(data)
187 {
188     OSL_ASSERT(reference != 0);
189     acquire();
190 }
191 
run()192 void Components::WriteThread::run() {
193     TimeValue t = { 1, 0 }; // 1 sec
194     delay_.wait(&t); // must not throw; result_error is harmless and ignored
195     osl::MutexGuard g(lock); // must not throw
196     try {
197         try {
198             writeModFile(components_, url_, data_);
199         } catch (css::uno::RuntimeException & e) {
200             // Silently ignore write errors, instead of aborting:
201             OSL_TRACE(
202                 "configmgr error writing modifications: %s",
203                 rtl::OUStringToOString(
204                     e.Message, RTL_TEXTENCODING_UTF8).getStr());
205         }
206     } catch (...) {
207         reference_->clear();
208         throw;
209     }
210     reference_->clear();
211 }
212 
getSingleton(css::uno::Reference<css::uno::XComponentContext> const & context)213 Components & Components::getSingleton(
214     css::uno::Reference< css::uno::XComponentContext > const & context)
215 {
216     OSL_ASSERT(context.is());
217     if (!singletonCreated) {
218         singletonCreated = true;
219         static Components theSingleton(context);
220         singleton = &theSingleton;
221     }
222     if (singleton == 0) {
223         throw css::uno::RuntimeException(
224             rtl::OUString(
225                 RTL_CONSTASCII_USTRINGPARAM(
226                     "configmgr no Components singleton")),
227             css::uno::Reference< css::uno::XInterface >());
228     }
229     return *singleton;
230 }
231 
allLocales(rtl::OUString const & locale)232 bool Components::allLocales(rtl::OUString const & locale) {
233     return locale.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("*"));
234 }
235 
resolvePathRepresentation(rtl::OUString const & pathRepresentation,rtl::OUString * canonicRepresentation,Path * path,int * finalizedLayer) const236 rtl::Reference< Node > Components::resolvePathRepresentation(
237     rtl::OUString const & pathRepresentation,
238     rtl::OUString * canonicRepresentation, Path * path, int * finalizedLayer)
239     const
240 {
241     return data_.resolvePathRepresentation(
242         pathRepresentation, canonicRepresentation, path, finalizedLayer);
243 }
244 
getTemplate(int layer,rtl::OUString const & fullName) const245 rtl::Reference< Node > Components::getTemplate(
246     int layer, rtl::OUString const & fullName) const
247 {
248     return data_.getTemplate(layer, fullName);
249 }
250 
addRootAccess(rtl::Reference<RootAccess> const & access)251 void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
252     roots_.insert(access.get());
253 }
254 
removeRootAccess(RootAccess * access)255 void Components::removeRootAccess(RootAccess * access) {
256     roots_.erase(access);
257 }
258 
initGlobalBroadcaster(Modifications const & modifications,rtl::Reference<RootAccess> const & exclude,Broadcaster * broadcaster)259 void Components::initGlobalBroadcaster(
260     Modifications const & modifications,
261     rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
262 {
263     //TODO: Iterate only over roots w/ listeners:
264     for (WeakRootSet::iterator i(roots_.begin()); i != roots_.end(); ++i) {
265         rtl::Reference< RootAccess > root;
266         if ((*i)->acquireCounting() > 1) {
267             root.set(*i); // must not throw
268         }
269         (*i)->releaseNondeleting();
270         if (root.is()) {
271             if (root != exclude) {
272                 Path path(root->getAbsolutePath());
273                 Modifications::Node const * mods = &modifications.getRoot();
274                 for (Path::iterator j(path.begin()); j != path.end(); ++j) {
275                     Modifications::Node::Children::const_iterator k(
276                         mods->children.find(*j));
277                     if (k == mods->children.end()) {
278                         mods = 0;
279                         break;
280                     }
281                     mods = &k->second;
282                 }
283                 //TODO: If the complete tree of which root is a part is deleted,
284                 // or replaced, mods will be null, but some of the listeners
285                 // from within root should probably fire nonetheless:
286                 if (mods != 0) {
287                     root->initBroadcaster(*mods, broadcaster);
288                 }
289             }
290         }
291     }
292 }
293 
addModification(Path const & path)294 void Components::addModification(Path const & path) {
295     data_.modifications.add(path);
296 }
297 
writeModifications()298 void Components::writeModifications() {
299     if (!writeThread_.is()) {
300         writeThread_ = new WriteThread(
301             &writeThread_, *this, getModificationFileUrl(), data_);
302         writeThread_->create();
303     }
304 }
305 
flushModifications()306 void Components::flushModifications() {
307     rtl::Reference< WriteThread > thread;
308     {
309         osl::MutexGuard g(lock);
310         thread = writeThread_;
311     }
312     if (thread.is()) {
313         thread->flush();
314         thread->join();
315     }
316 }
317 
insertExtensionXcsFile(bool shared,rtl::OUString const & fileUri)318 void Components::insertExtensionXcsFile(
319     bool shared, rtl::OUString const & fileUri)
320 {
321     try {
322         parseXcsFile(fileUri, shared ? 9 : 13, data_, 0, 0, 0);
323     } catch (css::container::NoSuchElementException & e) {
324         throw css::uno::RuntimeException(
325             (rtl::OUString(
326                 RTL_CONSTASCII_USTRINGPARAM(
327                     "insertExtensionXcsFile does not exist: ")) +
328              e.Message),
329             css::uno::Reference< css::uno::XInterface >());
330     }
331 }
332 
insertExtensionXcuFile(bool shared,rtl::OUString const & fileUri,Modifications * modifications)333 void Components::insertExtensionXcuFile(
334     bool shared, rtl::OUString const & fileUri, Modifications * modifications)
335 {
336     OSL_ASSERT(modifications != 0);
337     int layer = shared ? 10 : 14;
338     Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
339     try {
340         parseXcuFile(fileUri, layer, data_, 0, modifications, adds);
341     } catch (css::container::NoSuchElementException & e) {
342         data_.removeExtensionXcuAdditions(fileUri);
343         throw css::uno::RuntimeException(
344             (rtl::OUString(
345                 RTL_CONSTASCII_USTRINGPARAM(
346                     "insertExtensionXcuFile does not exist: ")) +
347              e.Message),
348             css::uno::Reference< css::uno::XInterface >());
349     }
350 }
351 
removeExtensionXcuFile(rtl::OUString const & fileUri,Modifications * modifications)352 void Components::removeExtensionXcuFile(
353     rtl::OUString const & fileUri, Modifications * modifications)
354 {
355     //TODO: Ideally, exactly the data coming from the specified xcu file would
356     // be removed.  However, not enough information is recorded in the in-memory
357     // data structures to do so.  So, as a workaround, all those set elements
358     // that were freshly added by the xcu and have afterwards been left
359     // unchanged or have only had their properties changed in the user layer are
360     // removed (and nothing else).  The heuristic to determine
361     // whether a node has been left unchanged is to check the layer ID (as
362     // usual) and additionally to check that the node does not recursively
363     // contain any non-empty sets (multiple extension xcu files are merged into
364     // one layer, so checking layer ID alone is not enough).  Since
365     // item->additions records all additions of set members in textual order,
366     // the latter check works well when iterating through item->additions in
367     // reverse order.
368     OSL_ASSERT(modifications != 0);
369     rtl::Reference< Data::ExtensionXcu > item(
370         data_.removeExtensionXcuAdditions(fileUri));
371     if (item.is()) {
372         for (Additions::reverse_iterator i(item->additions.rbegin());
373              i != item->additions.rend(); ++i)
374         {
375             rtl::Reference< Node > parent;
376             NodeMap const * map = &data_.components;
377             rtl::Reference< Node > node;
378             for (Path::const_iterator j(i->begin()); j != i->end(); ++j) {
379                 parent = node;
380                 node = Data::findNode(Data::NO_LAYER, *map, *j);
381                 if (!node.is()) {
382                     break;
383                 }
384                 map = &node->getMembers();
385             }
386             if (node.is()) {
387                 OSL_ASSERT(parent.is());
388                 if (parent->kind() == Node::KIND_SET) {
389                     OSL_ASSERT(
390                         node->kind() == Node::KIND_GROUP ||
391                         node->kind() == Node::KIND_SET);
392                     if (canRemoveFromLayer(item->layer, node)) {
393                         parent->getMembers().erase(i->back());
394                         data_.modifications.remove(*i);
395                         modifications->add(*i);
396                     }
397                 }
398             }
399         }
400         writeModifications();
401     }
402 }
403 
insertModificationXcuFile(rtl::OUString const & fileUri,std::set<rtl::OUString> const & includedPaths,std::set<rtl::OUString> const & excludedPaths,Modifications * modifications)404 void Components::insertModificationXcuFile(
405     rtl::OUString const & fileUri,
406     std::set< rtl::OUString > const & includedPaths,
407     std::set< rtl::OUString > const & excludedPaths,
408     Modifications * modifications)
409 {
410     OSL_ASSERT(modifications != 0);
411     Partial part(includedPaths, excludedPaths);
412     try {
413         parseFileLeniently(
414             &parseXcuFile, fileUri, Data::NO_LAYER, data_, &part, modifications,
415             0);
416     } catch (css::container::NoSuchElementException & e) {
417         OSL_TRACE(
418             "configmgr error inserting non-existing %s: %s",
419             rtl::OUStringToOString(fileUri, RTL_TEXTENCODING_UTF8).getStr(),
420             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
421     }
422 }
423 
getExternalValue(rtl::OUString const & descriptor)424 css::beans::Optional< css::uno::Any > Components::getExternalValue(
425     rtl::OUString const & descriptor)
426 {
427     sal_Int32 i = descriptor.indexOf(' ');
428     if (i <= 0) {
429         throw css::uno::RuntimeException(
430             (rtl::OUString(
431                 RTL_CONSTASCII_USTRINGPARAM("bad external value descriptor ")) +
432              descriptor),
433             css::uno::Reference< css::uno::XInterface >());
434     }
435     //TODO: Do not make calls with mutex locked:
436     rtl::OUString name(descriptor.copy(0, i));
437     ExternalServices::iterator j(externalServices_.find(name));
438     if (j == externalServices_.end()) {
439         css::uno::Reference< css::uno::XInterface > service;
440         try {
441             service = css::uno::Reference< css::lang::XMultiComponentFactory >(
442                 context_->getServiceManager(), css::uno::UNO_SET_THROW)->
443                 createInstanceWithContext(name, context_);
444         } catch (css::uno::RuntimeException &) {
445             // Assuming these exceptions are real errors:
446             throw;
447         } catch (css::uno::Exception & e) {
448             // Assuming these exceptions indicate that the service is not
449             // installed:
450             OSL_TRACE(
451                 "createInstance(%s) failed with %s",
452                 rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8).getStr(),
453                 rtl::OUStringToOString(
454                     e.Message, RTL_TEXTENCODING_UTF8).getStr());
455         }
456         css::uno::Reference< css::beans::XPropertySet > propset;
457         if (service.is()) {
458             propset = css::uno::Reference< css::beans::XPropertySet >(
459                 service, css::uno::UNO_QUERY_THROW);
460         }
461         j = externalServices_.insert(
462             ExternalServices::value_type(name, propset)).first;
463     }
464     css::beans::Optional< css::uno::Any > value;
465     if (j->second.is()) {
466         try {
467             if (!(j->second->getPropertyValue(descriptor.copy(i + 1)) >>=
468                   value))
469             {
470                 throw css::uno::RuntimeException(
471                     (rtl::OUString(
472                         RTL_CONSTASCII_USTRINGPARAM(
473                             "cannot obtain external value through ")) +
474                      descriptor),
475                     css::uno::Reference< css::uno::XInterface >());
476             }
477         } catch (css::beans::UnknownPropertyException & e) {
478             throw css::uno::RuntimeException(
479                 (rtl::OUString(
480                     RTL_CONSTASCII_USTRINGPARAM(
481                         "unknwon external value descriptor ID: ")) +
482                  e.Message),
483                 css::uno::Reference< css::uno::XInterface >());
484         } catch (css::lang::WrappedTargetException & e) {
485             throw css::uno::RuntimeException(
486                 (rtl::OUString(
487                     RTL_CONSTASCII_USTRINGPARAM(
488                         "cannot obtain external value: ")) +
489                  e.Message),
490                 css::uno::Reference< css::uno::XInterface >());
491         }
492     }
493     return value;
494 }
495 
Components(css::uno::Reference<css::uno::XComponentContext> const & context)496 Components::Components(
497     css::uno::Reference< css::uno::XComponentContext > const & context):
498     context_(context)
499 {
500     OSL_ASSERT(context.is());
501     RTL_LOGFILE_TRACE_AUTHOR("configmgr", "sb", "begin parsing");
502     parseXcsXcuLayer(
503         0,
504         expand(
505             rtl::OUString(
506                 RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/share/registry"))));
507     parseModuleLayer(
508         2,
509         expand(
510             rtl::OUString(
511                 RTL_CONSTASCII_USTRINGPARAM(
512                     "$OOO_BASE_DIR/share/registry/modules"))));
513     parseResLayer(
514         3,
515         expand(
516             rtl::OUString(
517                 RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/share/registry"))));
518 	parseXcsXcuIniLayer(
519         7,
520         expand(
521             rtl::OUString(
522                 RTL_CONSTASCII_USTRINGPARAM(
523                     "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")
524                     ":BUNDLED_EXTENSIONS_USER}/registry/"
525                     "com.sun.star.comp.deployment.configuration."
526                     "PackageRegistryBackend/configmgr.ini"))),
527         false);
528     parseXcsXcuIniLayer(
529         9,
530         expand(
531             rtl::OUString(
532                 RTL_CONSTASCII_USTRINGPARAM(
533                     "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")
534                     ":SHARED_EXTENSIONS_USER}/registry/"
535                     "com.sun.star.comp.deployment.configuration."
536                     "PackageRegistryBackend/configmgr.ini"))),
537         true);
538     parseXcsXcuLayer(
539         11,
540         expand(
541             rtl::OUString(
542                 RTL_CONSTASCII_USTRINGPARAM(
543                     "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")
544                     ":UNO_USER_PACKAGES_CACHE}/registry/"
545                     "com.sun.star.comp.deployment.configuration."
546                     "PackageRegistryBackend/registry"))));
547         // can be dropped once old UserInstallation format can no longer exist
548         // (probably OOo 4)
549     parseXcsXcuIniLayer(
550         13,
551         expand(
552             rtl::OUString(
553                 RTL_CONSTASCII_USTRINGPARAM(
554                     "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("uno")
555                     ":UNO_USER_PACKAGES_CACHE}/registry/"
556                     "com.sun.star.comp.deployment.configuration."
557                     "PackageRegistryBackend/configmgr.ini"))),
558         true);
559     parseModificationLayer();
560     RTL_LOGFILE_TRACE_AUTHOR("configmgr", "sb", "end parsing");
561 }
562 
~Components()563 Components::~Components() {}
564 
parseFileLeniently(FileParser * parseFile,rtl::OUString const & url,int layer,Data & data,Partial const * partial,Modifications * modifications,Additions * additions)565 void Components::parseFileLeniently(
566     FileParser * parseFile, rtl::OUString const & url, int layer, Data & data,
567     Partial const * partial, Modifications * modifications,
568     Additions * additions)
569 {
570     OSL_ASSERT(parseFile != 0);
571     try {
572         (*parseFile)(url, layer, data, partial, modifications, additions);
573     } catch (css::container::NoSuchElementException &) {
574         throw;
575     } catch (css::uno::Exception & e) { //TODO: more specific exception catching
576         // Silently ignore invalid XML files, instead of completely preventing
577         // OOo from starting:
578         OSL_TRACE(
579             "configmgr error reading %s: %s",
580             rtl::OUStringToOString(url, RTL_TEXTENCODING_UTF8).getStr(),
581             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
582     }
583 }
584 
parseFiles(int layer,rtl::OUString const & extension,FileParser * parseFile,rtl::OUString const & url,bool recursive)585 void Components::parseFiles(
586     int layer, rtl::OUString const & extension, FileParser * parseFile,
587     rtl::OUString const & url, bool recursive)
588 {
589     osl::Directory dir(url);
590     switch (dir.open()) {
591     case osl::FileBase::E_None:
592         break;
593     case osl::FileBase::E_NOENT:
594         if (!recursive) {
595             return;
596         }
597         // fall through
598     default:
599         throw css::uno::RuntimeException(
600             (rtl::OUString(
601                 RTL_CONSTASCII_USTRINGPARAM("cannot open directory ")) +
602              url),
603             css::uno::Reference< css::uno::XInterface >());
604     }
605     for (;;) {
606         osl::DirectoryItem i;
607         osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
608         if (rc == osl::FileBase::E_NOENT) {
609             break;
610         }
611         if (rc != osl::FileBase::E_None) {
612             throw css::uno::RuntimeException(
613                 (rtl::OUString(
614                     RTL_CONSTASCII_USTRINGPARAM("cannot iterate directory ")) +
615                  url),
616                 css::uno::Reference< css::uno::XInterface >());
617         }
618         osl::FileStatus stat(
619             FileStatusMask_Type | FileStatusMask_FileName |
620             FileStatusMask_FileURL);
621         if (i.getFileStatus(stat) != osl::FileBase::E_None) {
622             throw css::uno::RuntimeException(
623                 (rtl::OUString(
624                     RTL_CONSTASCII_USTRINGPARAM("cannot stat in directory ")) +
625                  url),
626                 css::uno::Reference< css::uno::XInterface >());
627         }
628         if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
629             parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
630         } else {
631             rtl::OUString file(stat.getFileName());
632             if (file.getLength() >= extension.getLength() &&
633                 file.match(extension, file.getLength() - extension.getLength()))
634             {
635                 try {
636                     parseFileLeniently(
637                         parseFile, stat.getFileURL(), layer, data_, 0, 0, 0);
638                 } catch (css::container::NoSuchElementException & e) {
639                     throw css::uno::RuntimeException(
640                         (rtl::OUString(
641                             RTL_CONSTASCII_USTRINGPARAM(
642                                 "stat'ed file does not exist: ")) +
643                          e.Message),
644                         css::uno::Reference< css::uno::XInterface >());
645                 }
646             }
647         }
648     }
649 }
650 
parseFileList(int layer,FileParser * parseFile,rtl::OUString const & urls,rtl::Bootstrap const & ini,bool recordAdditions)651 void Components::parseFileList(
652     int layer, FileParser * parseFile, rtl::OUString const & urls,
653     rtl::Bootstrap const & ini, bool recordAdditions)
654 {
655     for (sal_Int32 i = 0;;) {
656         rtl::OUString url(urls.getToken(0, ' ', i));
657         if (url.getLength() != 0) {
658             ini.expandMacrosFrom(url); //TODO: detect failure
659             Additions * adds = 0;
660             if (recordAdditions) {
661                 adds = data_.addExtensionXcuAdditions(url, layer);
662             }
663             try {
664                 parseFileLeniently(parseFile, url, layer, data_, 0, 0, adds);
665             } catch (css::container::NoSuchElementException & e) {
666                 OSL_TRACE(
667                     "configmgr file does not exist: %s",
668                     rtl::OUStringToOString(
669                         e.Message, RTL_TEXTENCODING_UTF8).getStr());
670                 if (adds != 0) {
671                     data_.removeExtensionXcuAdditions(url);
672                 }
673             }
674         }
675         if (i == -1) {
676             break;
677         }
678     }
679 }
680 
parseXcdFiles(int layer,rtl::OUString const & url)681 void Components::parseXcdFiles(int layer, rtl::OUString const & url) {
682     osl::Directory dir(url);
683     switch (dir.open()) {
684     case osl::FileBase::E_None:
685         break;
686     case osl::FileBase::E_NOENT:
687         return;
688     default:
689         throw css::uno::RuntimeException(
690             (rtl::OUString(
691                 RTL_CONSTASCII_USTRINGPARAM("cannot open directory ")) +
692              url),
693             css::uno::Reference< css::uno::XInterface >());
694     }
695     UnresolvedList unres;
696     XcdParser::Dependencies deps;
697     for (;;) {
698         osl::DirectoryItem i;
699         osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
700         if (rc == osl::FileBase::E_NOENT) {
701             break;
702         }
703         if (rc != osl::FileBase::E_None) {
704             throw css::uno::RuntimeException(
705                 (rtl::OUString(
706                     RTL_CONSTASCII_USTRINGPARAM("cannot iterate directory ")) +
707                  url),
708                 css::uno::Reference< css::uno::XInterface >());
709         }
710         osl::FileStatus stat(
711             FileStatusMask_Type | FileStatusMask_FileName |
712             FileStatusMask_FileURL);
713         if (i.getFileStatus(stat) != osl::FileBase::E_None) {
714             throw css::uno::RuntimeException(
715                 (rtl::OUString(
716                     RTL_CONSTASCII_USTRINGPARAM("cannot stat in directory ")) +
717                  url),
718                 css::uno::Reference< css::uno::XInterface >());
719         }
720         if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
721             rtl::OUString file(stat.getFileName());
722             if (file.getLength() >= RTL_CONSTASCII_LENGTH(".xcd") &&
723                 file.matchAsciiL(
724                     RTL_CONSTASCII_STRINGPARAM(".xcd"),
725                     file.getLength() - RTL_CONSTASCII_LENGTH(".xcd")))
726             {
727                 rtl::OUString name(
728                     file.copy(
729                         0, file.getLength() - RTL_CONSTASCII_LENGTH(".xcd")));
730                 rtl::Reference< ParseManager > manager;
731                 try {
732                     manager = new ParseManager(
733                         stat.getFileURL(), new XcdParser(layer, deps, data_));
734                 } catch (css::container::NoSuchElementException & e) {
735                     throw css::uno::RuntimeException(
736                         (rtl::OUString(
737                             RTL_CONSTASCII_USTRINGPARAM(
738                                 "stat'ed file does not exist: ")) +
739                          e.Message),
740                         css::uno::Reference< css::uno::XInterface >());
741                 }
742                 if (manager->parse()) {
743                     deps.insert(name);
744                 } else {
745                     unres.push_back(UnresolvedListItem(name, manager));
746                 }
747             }
748         }
749     }
750     while (!unres.empty()) {
751         bool resolved = false;
752         for (UnresolvedList::iterator i(unres.begin()); i != unres.end();) {
753             if (i->manager->parse()) {
754                 deps.insert(i->name);
755                 unres.erase(i++);
756                 resolved = true;
757             } else {
758                 ++i;
759             }
760         }
761         if (!resolved) {
762             throw css::uno::RuntimeException(
763                 (rtl::OUString(
764                     RTL_CONSTASCII_USTRINGPARAM(
765                         "xcd: unresolved dependencies in ")) +
766                  url),
767                 css::uno::Reference< css::uno::XInterface >());
768         }
769     }
770 }
771 
parseXcsXcuLayer(int layer,rtl::OUString const & url)772 void Components::parseXcsXcuLayer(int layer, rtl::OUString const & url) {
773     parseXcdFiles(layer, url);
774     parseFiles(
775         layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcs")),
776         &parseXcsFile,
777         url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/schema")), false);
778     parseFiles(
779         layer + 1, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
780         &parseXcuFile,
781         url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/data")), false);
782 }
783 
parseXcsXcuIniLayer(int layer,rtl::OUString const & url,bool recordAdditions)784 void Components::parseXcsXcuIniLayer(
785     int layer, rtl::OUString const & url, bool recordAdditions)
786 {
787     //TODO: rtl::Bootstrap::getFrom "first trie[s] to retrieve the value via the
788     // global function"
789     rtl::Bootstrap ini(url);
790     rtl::OUString urls;
791     if (ini.getFrom(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SCHEMA")), urls))
792     {
793         parseFileList(layer, &parseXcsFile, urls, ini, false);
794     }
795     if (ini.getFrom(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DATA")), urls))
796     {
797         parseFileList(layer + 1, &parseXcuFile, urls, ini, recordAdditions);
798     }
799 }
800 
parseModuleLayer(int layer,rtl::OUString const & url)801 void Components::parseModuleLayer(int layer, rtl::OUString const & url) {
802     parseFiles(
803         layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
804         &parseXcuFile, url, false);
805 }
806 
parseResLayer(int layer,rtl::OUString const & url)807 void Components::parseResLayer(int layer, rtl::OUString const & url) {
808     rtl::OUString resUrl(
809         url + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/res")));
810     parseXcdFiles(layer, resUrl);
811     parseFiles(
812         layer, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
813         &parseXcuFile, resUrl, false);
814 }
815 
getModificationFileUrl() const816 rtl::OUString Components::getModificationFileUrl() const {
817     return expand(
818         rtl::OUString(
819             RTL_CONSTASCII_USTRINGPARAM(
820                 "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap")
821                 ":UserInstallation}/user/registrymodifications.xcu")));
822 }
823 
parseModificationLayer()824 void Components::parseModificationLayer() {
825     try {
826         parseFileLeniently(
827             &parseXcuFile, getModificationFileUrl(), Data::NO_LAYER, data_, 0,
828             0, 0);
829     } catch (css::container::NoSuchElementException &) {
830         OSL_TRACE(
831             "configmgr user registrymodifications.xcu does not (yet) exist");
832         // Migrate old user layer data (can be removed once migration is no
833         // longer relevant, probably OOo 4; also see hack for xsi namespace in
834         // xmlreader::XmlReader::registerNamespaceIri):
835         parseFiles(
836             Data::NO_LAYER, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".xcu")),
837             &parseXcuFile,
838             expand(
839                 rtl::OUString(
840                     RTL_CONSTASCII_USTRINGPARAM(
841                         "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("bootstrap")
842                         ":UserInstallation}/user/registry/data"))),
843             false);
844     }
845 }
846 
847 }
848