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 <vector>
28 
29 #include "boost/noncopyable.hpp"
30 #include "com/sun/star/beans/NamedValue.hpp"
31 #include "com/sun/star/beans/PropertyValue.hpp"
32 #include "com/sun/star/lang/EventObject.hpp"
33 #include "com/sun/star/lang/Locale.hpp"
34 #include "com/sun/star/lang/XLocalizable.hpp"
35 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
36 #include "com/sun/star/lang/XServiceInfo.hpp"
37 #include "com/sun/star/lang/XSingleComponentFactory.hpp"
38 #include "com/sun/star/uno/Any.hxx"
39 #include "com/sun/star/uno/DeploymentException.hpp"
40 #include "com/sun/star/uno/Exception.hpp"
41 #include "com/sun/star/uno/Reference.hxx"
42 #include "com/sun/star/uno/RuntimeException.hpp"
43 #include "com/sun/star/uno/Sequence.hxx"
44 #include "com/sun/star/uno/XComponentContext.hpp"
45 #include "com/sun/star/uno/XInterface.hpp"
46 #include "com/sun/star/util/XFlushListener.hpp"
47 #include "com/sun/star/util/XFlushable.hpp"
48 #include "com/sun/star/util/XRefreshListener.hpp"
49 #include "com/sun/star/util/XRefreshable.hpp"
50 #include "comphelper/locale.hxx"
51 #include "cppu/unotype.hxx"
52 #include "cppuhelper/compbase5.hxx"
53 #include "cppuhelper/factory.hxx"
54 #include "cppuhelper/implbase2.hxx"
55 #include "cppuhelper/interfacecontainer.hxx"
56 #include "cppuhelper/weak.hxx"
57 #include "osl/diagnose.h"
58 #include "osl/mutex.hxx"
59 #include "sal/types.h"
60 #include "rtl/ref.hxx"
61 #include "rtl/unload.h"
62 #include "rtl/ustring.h"
63 #include "rtl/ustring.hxx"
64 
65 #include "components.hxx"
66 #include "configurationprovider.hxx"
67 #include "lock.hxx"
68 #include "rootaccess.hxx"
69 
70 namespace configmgr { namespace configuration_provider {
71 
72 namespace {
73 
74 namespace css = com::sun::star;
75 
76 char const accessServiceName[] =
77     "com.sun.star.configuration.ConfigurationAccess";
78 char const updateAccessServiceName[] =
79     "com.sun.star.configuration.ConfigurationUpdateAccess";
80 
badNodePath()81 void badNodePath() {
82     throw css::uno::Exception(
83         rtl::OUString(
84             RTL_CONSTASCII_USTRINGPARAM(
85                 "com.sun.star.configuration.ConfigurationProvider expects a"
86                 " single, non-empty, string nodepath argument")),
87         0);
88 }
89 
90 typedef
91     cppu::WeakComponentImplHelper5<
92         css::lang::XServiceInfo, css::lang::XMultiServiceFactory,
93         css::util::XRefreshable, css::util::XFlushable,
94         css::lang::XLocalizable >
95     ServiceBase;
96 
97 class Service:
98     private osl::Mutex, public ServiceBase, private boost::noncopyable
99 {
100 public:
Service(css::uno::Reference<css::uno::XComponentContext> const context,rtl::OUString const & locale)101     Service(
102         css::uno::Reference< css::uno::XComponentContext > const context,
103         rtl::OUString const & locale):
104         ServiceBase(*static_cast< osl::Mutex * >(this)), context_(context),
105         locale_(locale)
106     {
107         OSL_ASSERT(context.is());
108     }
109 
110 private:
~Service()111     virtual ~Service() {}
112 
disposing()113     virtual void SAL_CALL disposing() { flushModifications(); }
114 
getImplementationName()115     virtual rtl::OUString SAL_CALL getImplementationName()
116         throw (css::uno::RuntimeException)
117     { return configuration_provider::getImplementationName(); }
118 
supportsService(rtl::OUString const & ServiceName)119     virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName)
120         throw (css::uno::RuntimeException)
121     { return ServiceName == getSupportedServiceNames()[0]; } //TODO
122 
123     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
getSupportedServiceNames()124     getSupportedServiceNames() throw (css::uno::RuntimeException)
125     { return configuration_provider::getSupportedServiceNames(); }
126 
127     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(
128         rtl::OUString const & aServiceSpecifier)
129         throw (css::uno::Exception, css::uno::RuntimeException);
130 
131     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
132     createInstanceWithArguments(
133         rtl::OUString const & ServiceSpecifier,
134         css::uno::Sequence< css::uno::Any > const & Arguments)
135         throw (css::uno::Exception, css::uno::RuntimeException);
136 
137     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
138     getAvailableServiceNames() throw (css::uno::RuntimeException);
139 
140     virtual void SAL_CALL refresh() throw (css::uno::RuntimeException);
141 
142     virtual void SAL_CALL addRefreshListener(
143         css::uno::Reference< css::util::XRefreshListener > const & l)
144         throw (css::uno::RuntimeException);
145 
146     virtual void SAL_CALL removeRefreshListener(
147         css::uno::Reference< css::util::XRefreshListener > const & l)
148         throw (css::uno::RuntimeException);
149 
150     virtual void SAL_CALL flush() throw (css::uno::RuntimeException);
151 
152     virtual void SAL_CALL addFlushListener(
153         css::uno::Reference< css::util::XFlushListener > const & l)
154         throw (css::uno::RuntimeException);
155 
156     virtual void SAL_CALL removeFlushListener(
157         css::uno::Reference< css::util::XFlushListener > const & l)
158         throw (css::uno::RuntimeException);
159 
160     virtual void SAL_CALL setLocale(css::lang::Locale const & eLocale)
161         throw (css::uno::RuntimeException);
162 
163     virtual css::lang::Locale SAL_CALL getLocale()
164         throw (css::uno::RuntimeException);
165 
166     void flushModifications() const;
167 
168     css::uno::Reference< css::uno::XComponentContext > context_;
169     rtl::OUString locale_;
170 };
171 
createInstance(rtl::OUString const & aServiceSpecifier)172 css::uno::Reference< css::uno::XInterface > Service::createInstance(
173     rtl::OUString const & aServiceSpecifier)
174     throw (css::uno::Exception, css::uno::RuntimeException)
175 {
176     return createInstanceWithArguments(
177         aServiceSpecifier, css::uno::Sequence< css::uno::Any >());
178 }
179 
180 css::uno::Reference< css::uno::XInterface >
createInstanceWithArguments(rtl::OUString const & ServiceSpecifier,css::uno::Sequence<css::uno::Any> const & Arguments)181 Service::createInstanceWithArguments(
182     rtl::OUString const & ServiceSpecifier,
183     css::uno::Sequence< css::uno::Any > const & Arguments)
184     throw (css::uno::Exception, css::uno::RuntimeException)
185 {
186     rtl::OUString nodepath;
187     rtl::OUString locale;
188     for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
189         css::beans::NamedValue v1;
190         css::beans::PropertyValue v2;
191         rtl::OUString name;
192         css::uno::Any value;
193         if (Arguments[i] >>= v1) {
194             name = v1.Name;
195             value = v1.Value;
196         } else if (Arguments[i] >>= v2) {
197             name = v2.Name;
198             value = v2.Value;
199         } else if (Arguments.getLength() == 1 && (Arguments[i] >>= nodepath)) {
200             // For backwards compatibility, allow a single string argument that
201             // denotes nodepath.
202             if (nodepath.getLength() == 0) {
203                 badNodePath();
204             }
205             break;
206         } else {
207             throw css::uno::Exception(
208                 rtl::OUString(
209                     RTL_CONSTASCII_USTRINGPARAM(
210                         "com.sun.star.configuration.ConfigurationProvider"
211                         " expects NamedValue or PropertyValue arguments")),
212                 0);
213         }
214         // For backwards compatibility, allow "nodepath" and "Locale" in any
215         // case:
216         if (name.equalsIgnoreAsciiCaseAsciiL(
217                 RTL_CONSTASCII_STRINGPARAM("nodepath")))
218         {
219             if (nodepath.getLength() != 0 || !(value >>= nodepath) ||
220                 nodepath.getLength() == 0)
221             {
222                 badNodePath();
223             }
224         } else if (name.equalsIgnoreAsciiCaseAsciiL(
225                        RTL_CONSTASCII_STRINGPARAM("locale")))
226         {
227             if (locale.getLength() != 0 || !(value >>= locale) ||
228                 locale.getLength() == 0)
229             {
230                 throw css::uno::Exception(
231                     rtl::OUString(
232                         RTL_CONSTASCII_USTRINGPARAM(
233                             "com.sun.star.configuration.ConfigurationProvider"
234                             " expects at most one, non-empty, string Locale"
235                             " argument")),
236                     0);
237             }
238         }
239     }
240     if (nodepath.getLength() == 0) {
241         badNodePath();
242     }
243     // For backwards compatibility, allow a nodepath that misses the leading
244     // slash:
245     if (nodepath[0] != '/') {
246         nodepath = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/")) + nodepath;
247     }
248     if (locale.getLength() == 0) {
249         //TODO: should the Access use the dynamically changing locale_ instead?
250         locale = locale_;
251         if (locale.getLength() == 0) {
252             locale = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en-US"));
253         }
254     }
255     bool update;
256     if (ServiceSpecifier.equalsAsciiL(
257             RTL_CONSTASCII_STRINGPARAM(accessServiceName)))
258     {
259         update = false;
260     } else if (ServiceSpecifier.equalsAsciiL(
261                    RTL_CONSTASCII_STRINGPARAM(updateAccessServiceName)))
262     {
263         update = true;
264     } else {
265         throw css::uno::Exception(
266             (rtl::OUString(
267                 RTL_CONSTASCII_USTRINGPARAM(
268                     "com.sun.star.configuration.ConfigurationProvider does not"
269                     " support service ")) +
270              ServiceSpecifier),
271             static_cast< cppu::OWeakObject * >(this));
272     }
273     osl::MutexGuard guard(lock);
274     Components & components = Components::getSingleton(context_);
275     rtl::Reference< RootAccess > root(
276         new RootAccess(components, nodepath, locale, update));
277     if (root->isValue()) {
278         throw css::uno::Exception(
279             (rtl::OUString(
280                 RTL_CONSTASCII_USTRINGPARAM(
281                     "com.sun.star.configuration.ConfigurationProvider: there is"
282                     " a leaf value at nodepath ")) +
283              nodepath),
284             static_cast< cppu::OWeakObject * >(this));
285     }
286     components.addRootAccess(root);
287     return static_cast< cppu::OWeakObject * >(root.get());
288 }
289 
getAvailableServiceNames()290 css::uno::Sequence< rtl::OUString > Service::getAvailableServiceNames()
291     throw (css::uno::RuntimeException)
292 {
293     css::uno::Sequence< rtl::OUString > names(2);
294     names[0] = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(accessServiceName));
295     names[1] = rtl::OUString(
296         RTL_CONSTASCII_USTRINGPARAM(updateAccessServiceName));
297     return names;
298 }
299 
refresh()300 void Service::refresh() throw (css::uno::RuntimeException) {
301     //TODO
302     cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
303         cppu::UnoType< css::util::XRefreshListener >::get());
304     if (cont != 0) {
305         css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
306         cont->notifyEach(&css::util::XRefreshListener::refreshed, ev);
307     }
308 }
309 
addRefreshListener(css::uno::Reference<css::util::XRefreshListener> const & l)310 void Service::addRefreshListener(
311     css::uno::Reference< css::util::XRefreshListener > const & l)
312     throw (css::uno::RuntimeException)
313 {
314     rBHelper.addListener(
315         cppu::UnoType< css::util::XRefreshListener >::get(), l);
316 }
317 
removeRefreshListener(css::uno::Reference<css::util::XRefreshListener> const & l)318 void Service::removeRefreshListener(
319     css::uno::Reference< css::util::XRefreshListener > const & l)
320     throw (css::uno::RuntimeException)
321 {
322     rBHelper.removeListener(
323         cppu::UnoType< css::util::XRefreshListener >::get(), l);
324 }
325 
flush()326 void Service::flush() throw (css::uno::RuntimeException) {
327     flushModifications();
328     cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
329         cppu::UnoType< css::util::XFlushListener >::get());
330     if (cont != 0) {
331         css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
332         cont->notifyEach(&css::util::XFlushListener::flushed, ev);
333     }
334 }
335 
addFlushListener(css::uno::Reference<css::util::XFlushListener> const & l)336 void Service::addFlushListener(
337     css::uno::Reference< css::util::XFlushListener > const & l)
338     throw (css::uno::RuntimeException)
339 {
340     rBHelper.addListener(cppu::UnoType< css::util::XFlushListener >::get(), l);
341 }
342 
removeFlushListener(css::uno::Reference<css::util::XFlushListener> const & l)343 void Service::removeFlushListener(
344     css::uno::Reference< css::util::XFlushListener > const & l)
345     throw (css::uno::RuntimeException)
346 {
347     rBHelper.removeListener(
348         cppu::UnoType< css::util::XFlushListener >::get(), l);
349 }
350 
setLocale(css::lang::Locale const & eLocale)351 void Service::setLocale(css::lang::Locale const & eLocale)
352     throw (css::uno::RuntimeException)
353 {
354     osl::MutexGuard guard(lock);
355     locale_ = comphelper::Locale(
356         eLocale.Language, eLocale.Country, eLocale.Variant).toISO();
357 }
358 
getLocale()359 css::lang::Locale Service::getLocale() throw (css::uno::RuntimeException) {
360     osl::MutexGuard guard(lock);
361     css::lang::Locale loc;
362     if (locale_.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("*"))) {
363         loc.Language = locale_;
364     } else if (locale_.getLength() != 0) {
365         try {
366             comphelper::Locale l(locale_);
367             loc.Language = l.getLanguage();
368             loc.Country = l.getCountry();
369             loc.Variant = l.getVariant();
370         } catch (comphelper::Locale::MalFormedLocaleException & e) {
371             throw css::uno::RuntimeException(
372                 (rtl::OUString(
373                     RTL_CONSTASCII_USTRINGPARAM("MalformedLocaleException: ")) +
374                  e.Message),
375                 static_cast< cppu::OWeakObject * >(this));
376         }
377     }
378     return loc;
379 }
380 
flushModifications() const381 void Service::flushModifications() const {
382     Components * components;
383     {
384         osl::MutexGuard guard(lock);
385         components = &Components::getSingleton(context_);
386     }
387     components->flushModifications();
388 }
389 
390 class Factory:
391     public cppu::WeakImplHelper2<
392         css::lang::XSingleComponentFactory, css::lang::XServiceInfo >,
393     private boost::noncopyable
394 {
395 public:
Factory()396     Factory() {}
397 
398 private:
~Factory()399     virtual ~Factory() {}
400 
401     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
402     createInstanceWithContext(
403         css::uno::Reference< css::uno::XComponentContext > const & Context)
404         throw (css::uno::Exception, css::uno::RuntimeException);
405 
406     virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
407     createInstanceWithArgumentsAndContext(
408         css::uno::Sequence< css::uno::Any > const & Arguments,
409         css::uno::Reference< css::uno::XComponentContext > const & Context)
410         throw (css::uno::Exception, css::uno::RuntimeException);
411 
getImplementationName()412     virtual rtl::OUString SAL_CALL getImplementationName()
413         throw (css::uno::RuntimeException)
414     { return configuration_provider::getImplementationName(); }
415 
supportsService(rtl::OUString const & ServiceName)416     virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName)
417         throw (css::uno::RuntimeException)
418     { return ServiceName == getSupportedServiceNames()[0]; } //TODO
419 
420     virtual css::uno::Sequence< rtl::OUString > SAL_CALL
getSupportedServiceNames()421     getSupportedServiceNames() throw (css::uno::RuntimeException)
422     { return configuration_provider::getSupportedServiceNames(); }
423 };
424 
createInstanceWithContext(css::uno::Reference<css::uno::XComponentContext> const & Context)425 css::uno::Reference< css::uno::XInterface > Factory::createInstanceWithContext(
426     css::uno::Reference< css::uno::XComponentContext > const & Context)
427     throw (css::uno::Exception, css::uno::RuntimeException)
428 {
429     return createInstanceWithArgumentsAndContext(
430         css::uno::Sequence< css::uno::Any >(), Context);
431 }
432 
433 css::uno::Reference< css::uno::XInterface >
createInstanceWithArgumentsAndContext(css::uno::Sequence<css::uno::Any> const & Arguments,css::uno::Reference<css::uno::XComponentContext> const & Context)434 Factory::createInstanceWithArgumentsAndContext(
435     css::uno::Sequence< css::uno::Any > const & Arguments,
436     css::uno::Reference< css::uno::XComponentContext > const & Context)
437     throw (css::uno::Exception, css::uno::RuntimeException)
438 {
439     if (Arguments.getLength() == 0) {
440         css::uno::Reference< css::uno::XInterface > instance;
441         if (!(Context->getValueByName(
442                   rtl::OUString(
443                       RTL_CONSTASCII_USTRINGPARAM(
444                           "/singletons/"
445                           "com.sun.star.configuration.theDefaultProvider")))
446               >>= instance) ||
447             !instance.is())
448         {
449             throw css::uno::DeploymentException(
450                 rtl::OUString(
451                     RTL_CONSTASCII_USTRINGPARAM(
452                         "component context fails to supply singleton"
453                         " com.sun.star.configuration.theDefaultProvider")),
454                 Context);
455         }
456         return instance;
457     } else {
458         rtl::OUString locale;
459         for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
460             css::beans::NamedValue v1;
461             css::beans::PropertyValue v2;
462             rtl::OUString name;
463             css::uno::Any value;
464             if (Arguments[i] >>= v1) {
465                 name = v1.Name;
466                 value = v1.Value;
467             } else if (Arguments[i] >>= v2) {
468                 name = v2.Name;
469                 value = v2.Value;
470             } else {
471                 throw css::uno::Exception(
472                     rtl::OUString(
473                         RTL_CONSTASCII_USTRINGPARAM(
474                             "com.sun.star.configuration.ConfigurationProvider"
475                             " factory expects NamedValue or PropertyValue"
476                             " arguments")),
477                     0);
478             }
479             // For backwards compatibility, allow "Locale" and (ignored)
480             // "EnableAsync" in any case:
481             if (name.equalsIgnoreAsciiCaseAsciiL(
482                     RTL_CONSTASCII_STRINGPARAM("locale")))
483             {
484                 if (locale.getLength() != 0 || !(value >>= locale) ||
485                     locale.getLength() == 0)
486                 {
487                     throw css::uno::Exception(
488                         rtl::OUString(
489                             RTL_CONSTASCII_USTRINGPARAM(
490                                 "com.sun.star.configuration."
491                                 "ConfigurationProvider factory expects at most"
492                                 " one, non-empty, string Locale argument")),
493                         0);
494                 }
495             } else if (!name.equalsIgnoreAsciiCaseAsciiL(
496                            RTL_CONSTASCII_STRINGPARAM("enableasync")))
497             {
498                 throw css::uno::Exception(
499                     rtl::OUString(
500                         RTL_CONSTASCII_USTRINGPARAM(
501                             "com.sun.star.configuration.ConfigurationProvider"
502                             " factory: unknown argument ")) + name,
503                     0);
504             }
505         }
506         return static_cast< cppu::OWeakObject * >(new Service(Context, locale));
507     }
508 }
509 
510 }
511 
createDefault(css::uno::Reference<css::uno::XComponentContext> const & context)512 css::uno::Reference< css::uno::XInterface > createDefault(
513     css::uno::Reference< css::uno::XComponentContext > const & context)
514 {
515     return static_cast< cppu::OWeakObject * >(
516         new Service(context, rtl::OUString()));
517 }
518 
getImplementationName()519 rtl::OUString getImplementationName() {
520     return rtl::OUString(
521         RTL_CONSTASCII_USTRINGPARAM(
522             "com.sun.star.comp.configuration.ConfigurationProvider"));
523 }
524 
getSupportedServiceNames()525 css::uno::Sequence< rtl::OUString > getSupportedServiceNames() {
526     rtl::OUString name(
527         RTL_CONSTASCII_USTRINGPARAM(
528             "com.sun.star.configuration.ConfigurationProvider"));
529     return css::uno::Sequence< rtl::OUString >(&name, 1);
530 }
531 
532 css::uno::Reference< css::lang::XSingleComponentFactory >
createFactory(cppu::ComponentFactoryFunc,rtl::OUString const &,css::uno::Sequence<rtl::OUString> const &,rtl_ModuleCount *)533 createFactory(
534     cppu::ComponentFactoryFunc, rtl::OUString const &,
535     css::uno::Sequence< rtl::OUString > const &, rtl_ModuleCount *)
536     SAL_THROW(())
537 {
538     return new Factory;
539 }
540 
541 } }
542