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