1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 #include <utility>
24 #include <unordered_map>
25 
26 #include <properties.h>
27 #include <helper/mischelper.hxx>
28 
29 #include <com/sun/star/beans/Property.hpp>
30 #include <com/sun/star/beans/XProperty.hpp>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/util/XChangesNotifier.hpp>
34 #include <com/sun/star/util/PathSubstitution.hpp>
35 #include <com/sun/star/container/XNameAccess.hpp>
36 #include <com/sun/star/lang/XServiceInfo.hpp>
37 #include <com/sun/star/util/XStringSubstitution.hpp>
38 #include <com/sun/star/util/XChangesListener.hpp>
39 #include <com/sun/star/util/XPathSettings.hpp>
40 
41 #include <tools/urlobj.hxx>
42 #include <rtl/ustrbuf.hxx>
43 #include <rtl/ref.hxx>
44 #include <sal/log.hxx>
45 
46 #include <cppuhelper/basemutex.hxx>
47 #include <cppuhelper/propshlp.hxx>
48 #include <cppuhelper/compbase.hxx>
49 #include <cppuhelper/supportsservice.hxx>
50 #include <comphelper/sequence.hxx>
51 #include <comphelper/configurationhelper.hxx>
52 #include <unotools/configpaths.hxx>
53 
54 using namespace framework;
55 
56 #define CFGPROP_USERPATHS "UserPaths"
57 #define CFGPROP_WRITEPATH "WritePath"
58 
59 /*
60     0 : old style              "Template"              string using ";" as separator
61     1 : internal paths         "Template_internal"     string list
62     2 : user paths             "Template_user"         string list
63     3 : write path             "Template_write"        string
64  */
65 
66 #define POSTFIX_INTERNAL_PATHS "_internal"
67 #define POSTFIX_USER_PATHS "_user"
68 #define POSTFIX_WRITE_PATH "_writable"
69 
70 namespace {
71 
72 const sal_Int32 IDGROUP_OLDSTYLE        = 0;
73 const sal_Int32 IDGROUP_INTERNAL_PATHS = 1;
74 const sal_Int32 IDGROUP_USER_PATHS     = 2;
75 const sal_Int32 IDGROUP_WRITE_PATH      = 3;
76 
77 const sal_Int32 IDGROUP_COUNT           = 4;
78 
impl_getPropGroup(sal_Int32 nID)79 sal_Int32 impl_getPropGroup(sal_Int32 nID)
80 {
81     return (nID % IDGROUP_COUNT);
82 }
83 
84 /* enable it if you wish to migrate old user settings (using the old cfg schema) on demand...
85    disable it in case only the new schema must be used.
86  */
87 
88 typedef ::cppu::WeakComponentImplHelper<
89             css::lang::XServiceInfo,
90             css::util::XChangesListener,    // => XEventListener
91             css::util::XPathSettings>       // => XPropertySet
92                 PathSettings_BASE;
93 
94 class PathSettings : private cppu::BaseMutex
95                    , public  PathSettings_BASE
96                    , public  ::cppu::OPropertySetHelper
97 {
98     struct PathInfo
99     {
100         public:
101 
PathInfo__anon002abc1c0111::PathSettings::PathInfo102             PathInfo()
103                 : sPathName     ()
104                 , lInternalPaths()
105                 , lUserPaths    ()
106                 , sWritePath    ()
107                 , bIsSinglePath (false)
108                 , bIsReadonly   (false)
109             {}
110 
111             /// an internal name describing this path
112             OUString sPathName;
113 
114             /// contains all paths, which are used internally - but are not visible for the user.
115             std::vector<OUString> lInternalPaths;
116 
117             /// contains all paths configured by the user
118             std::vector<OUString> lUserPaths;
119 
120             /// this special path is used to generate feature depending content there
121             OUString sWritePath;
122 
123             /// indicates real single paths, which uses WritePath property only
124             bool bIsSinglePath;
125 
126             /// simple handling of finalized/mandatory states ... => we know one state READONLY only .-)
127             bool bIsReadonly;
128     };
129 
130     typedef std::unordered_map<OUString, PathSettings::PathInfo> PathHash;
131 
132     enum EChangeOp
133     {
134         E_UNDEFINED,
135         E_ADDED,
136         E_CHANGED,
137         E_REMOVED
138     };
139 
140 private:
141 
142     /** reference to factory, which has create this instance. */
143     css::uno::Reference< css::uno::XComponentContext > m_xContext;
144 
145     /** list of all path variables and her corresponding values. */
146     PathSettings::PathHash m_lPaths;
147 
148     /** describes all properties available on our interface.
149         Will be generated on demand based on our path list m_lPaths. */
150     css::uno::Sequence< css::beans::Property > m_lPropDesc;
151 
152     /** helper needed to (re-)substitute all internal save path values. */
153     css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;
154 
155     /** provides access to the old configuration schema (which will be migrated on demand). */
156     css::uno::Reference< css::container::XNameAccess > m_xCfgOld;
157 
158     /** provides access to the new configuration schema. */
159     css::uno::Reference< css::container::XNameAccess > m_xCfgNew;
160 
161     /** helper to listen for configuration changes without ownership cycle problems */
162     css::uno::Reference< css::util::XChangesListener > m_xCfgNewListener;
163 
164     std::unique_ptr<::cppu::OPropertyArrayHelper> m_pPropHelp;
165 
166 public:
167 
168     /** initialize a new instance of this class.
169         Attention: It's necessary for right function of this class, that the order of base
170         classes is the right one. Because we transfer information from one base to another
171         during this ctor runs! */
172     explicit PathSettings(const css::uno::Reference< css::uno::XComponentContext >& xContext);
173 
174     /** free all used resources ... if it was not already done. */
175     virtual ~PathSettings() override;
176 
getImplementationName()177     virtual OUString SAL_CALL getImplementationName() override
178     {
179         return "com.sun.star.comp.framework.PathSettings";
180     }
181 
supportsService(OUString const & ServiceName)182     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
183     {
184         return cppu::supportsService(this, ServiceName);
185     }
186 
getSupportedServiceNames()187     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
188     {
189         return {"com.sun.star.util.PathSettings"};
190     }
191 
192     // XInterface
193     virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
acquire()194     virtual void SAL_CALL acquire() noexcept override
195         { OWeakObject::acquire(); }
release()196     virtual void SAL_CALL release() noexcept override
197         { OWeakObject::release(); }
198 
199     // XTypeProvider
200     virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes(  ) override;
201 
202     // css::util::XChangesListener
203     virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
204 
205     // css::lang::XEventListener
206     virtual void SAL_CALL disposing(const css::lang::EventObject& aSource) override;
207 
208     /**
209      * XPathSettings attribute methods
210      */
getAddin()211     virtual OUString SAL_CALL getAddin() override
212         { return getStringProperty("Addin"); }
setAddin(const OUString & p1)213     virtual void SAL_CALL setAddin(const OUString& p1) override
214         { setStringProperty("Addin", p1); }
getAutoCorrect()215     virtual OUString SAL_CALL getAutoCorrect() override
216         { return getStringProperty("AutoCorrect"); }
setAutoCorrect(const OUString & p1)217     virtual void SAL_CALL setAutoCorrect(const OUString& p1) override
218         { setStringProperty("AutoCorrect", p1); }
getAutoText()219     virtual OUString SAL_CALL getAutoText() override
220         { return getStringProperty("AutoText"); }
setAutoText(const OUString & p1)221     virtual void SAL_CALL setAutoText(const OUString& p1) override
222         { setStringProperty("AutoText", p1); }
getBackup()223     virtual OUString SAL_CALL getBackup() override
224         { return getStringProperty("Backup"); }
setBackup(const OUString & p1)225     virtual void SAL_CALL setBackup(const OUString& p1) override
226         { setStringProperty("Backup", p1); }
getBasic()227     virtual OUString SAL_CALL getBasic() override
228         { return getStringProperty("Basic"); }
setBasic(const OUString & p1)229     virtual void SAL_CALL setBasic(const OUString& p1) override
230         { setStringProperty("Basic", p1); }
getBitmap()231     virtual OUString SAL_CALL getBitmap() override
232         { return getStringProperty("Bitmap"); }
setBitmap(const OUString & p1)233     virtual void SAL_CALL setBitmap(const OUString& p1) override
234         { setStringProperty("Bitmap", p1); }
getConfig()235     virtual OUString SAL_CALL getConfig() override
236         { return getStringProperty("Config"); }
setConfig(const OUString & p1)237     virtual void SAL_CALL setConfig(const OUString& p1) override
238         { setStringProperty("Config", p1); }
getDictionary()239     virtual OUString SAL_CALL getDictionary() override
240         { return getStringProperty("Dictionary"); }
setDictionary(const OUString & p1)241     virtual void SAL_CALL setDictionary(const OUString& p1) override
242         { setStringProperty("Dictionary", p1); }
getFavorite()243     virtual OUString SAL_CALL getFavorite() override
244         { return getStringProperty("Favorite"); }
setFavorite(const OUString & p1)245     virtual void SAL_CALL setFavorite(const OUString& p1) override
246         { setStringProperty("Favorite", p1); }
getFilter()247     virtual OUString SAL_CALL getFilter() override
248         { return getStringProperty("Filter"); }
setFilter(const OUString & p1)249     virtual void SAL_CALL setFilter(const OUString& p1) override
250         { setStringProperty("Filter", p1); }
getGallery()251     virtual OUString SAL_CALL getGallery() override
252         { return getStringProperty("Gallery"); }
setGallery(const OUString & p1)253     virtual void SAL_CALL setGallery(const OUString& p1) override
254         { setStringProperty("Gallery", p1); }
getGraphic()255     virtual OUString SAL_CALL getGraphic() override
256         { return getStringProperty("Graphic"); }
setGraphic(const OUString & p1)257     virtual void SAL_CALL setGraphic(const OUString& p1) override
258         { setStringProperty("Graphic", p1); }
getHelp()259     virtual OUString SAL_CALL getHelp() override
260         { return getStringProperty("Help"); }
setHelp(const OUString & p1)261     virtual void SAL_CALL setHelp(const OUString& p1) override
262         { setStringProperty("Help", p1); }
getLinguistic()263     virtual OUString SAL_CALL getLinguistic() override
264         { return getStringProperty("Linguistic"); }
setLinguistic(const OUString & p1)265     virtual void SAL_CALL setLinguistic(const OUString& p1) override
266         { setStringProperty("Linguistic", p1); }
getModule()267     virtual OUString SAL_CALL getModule() override
268         { return getStringProperty("Module"); }
setModule(const OUString & p1)269     virtual void SAL_CALL setModule(const OUString& p1) override
270         { setStringProperty("Module", p1); }
getPalette()271     virtual OUString SAL_CALL getPalette() override
272         { return getStringProperty("Palette"); }
setPalette(const OUString & p1)273     virtual void SAL_CALL setPalette(const OUString& p1) override
274         { setStringProperty("Palette", p1); }
getPlugin()275     virtual OUString SAL_CALL getPlugin() override
276         { return getStringProperty("Plugin"); }
setPlugin(const OUString & p1)277     virtual void SAL_CALL setPlugin(const OUString& p1) override
278         { setStringProperty("Plugin", p1); }
getStorage()279     virtual OUString SAL_CALL getStorage() override
280         { return getStringProperty("Storage"); }
setStorage(const OUString & p1)281     virtual void SAL_CALL setStorage(const OUString& p1) override
282         { setStringProperty("Storage", p1); }
getTemp()283     virtual OUString SAL_CALL getTemp() override
284         { return getStringProperty("Temp"); }
setTemp(const OUString & p1)285     virtual void SAL_CALL setTemp(const OUString& p1) override
286         { setStringProperty("Temp", p1); }
getTemplate()287     virtual OUString SAL_CALL getTemplate() override
288         { return getStringProperty("Template"); }
setTemplate(const OUString & p1)289     virtual void SAL_CALL setTemplate(const OUString& p1) override
290         { setStringProperty("Template", p1); }
getUIConfig()291     virtual OUString SAL_CALL getUIConfig() override
292         { return getStringProperty("UIConfig"); }
setUIConfig(const OUString & p1)293     virtual void SAL_CALL setUIConfig(const OUString& p1) override
294         { setStringProperty("UIConfig", p1); }
getUserConfig()295     virtual OUString SAL_CALL getUserConfig() override
296         { return getStringProperty("UserConfig"); }
setUserConfig(const OUString & p1)297     virtual void SAL_CALL setUserConfig(const OUString& p1) override
298         { setStringProperty("UserConfig", p1); }
getUserDictionary()299     virtual OUString SAL_CALL getUserDictionary() override
300         { return getStringProperty("UserDictionary"); }
setUserDictionary(const OUString & p1)301     virtual void SAL_CALL setUserDictionary(const OUString& p1) override
302         { setStringProperty("UserDictionary", p1); }
getWork()303     virtual OUString SAL_CALL getWork() override
304         { return getStringProperty("Work"); }
setWork(const OUString & p1)305     virtual void SAL_CALL setWork(const OUString& p1) override
306         { setStringProperty("Work", p1); }
getBasePathShareLayer()307     virtual OUString SAL_CALL getBasePathShareLayer() override
308         { return getStringProperty("UIConfig"); }
setBasePathShareLayer(const OUString & p1)309     virtual void SAL_CALL setBasePathShareLayer(const OUString& p1) override
310         { setStringProperty("UIConfig", p1); }
getBasePathUserLayer()311     virtual OUString SAL_CALL getBasePathUserLayer() override
312         { return getStringProperty("UserConfig"); }
setBasePathUserLayer(const OUString & p1)313     virtual void SAL_CALL setBasePathUserLayer(const OUString& p1) override
314         { setStringProperty("UserConfig", p1); }
315 
316     /**
317      * overrides to resolve inheritance ambiguity
318      */
setPropertyValue(const OUString & p1,const css::uno::Any & p2)319     virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
320         { ::cppu::OPropertySetHelper::setPropertyValue(p1, p2); }
getPropertyValue(const OUString & p1)321     virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
322         { return ::cppu::OPropertySetHelper::getPropertyValue(p1); }
addPropertyChangeListener(const OUString & p1,const css::uno::Reference<css::beans::XPropertyChangeListener> & p2)323     virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
324         { ::cppu::OPropertySetHelper::addPropertyChangeListener(p1, p2); }
removePropertyChangeListener(const OUString & p1,const css::uno::Reference<css::beans::XPropertyChangeListener> & p2)325     virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
326         { ::cppu::OPropertySetHelper::removePropertyChangeListener(p1, p2); }
addVetoableChangeListener(const OUString & p1,const css::uno::Reference<css::beans::XVetoableChangeListener> & p2)327     virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
328         { ::cppu::OPropertySetHelper::addVetoableChangeListener(p1, p2); }
removeVetoableChangeListener(const OUString & p1,const css::uno::Reference<css::beans::XVetoableChangeListener> & p2)329     virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
330         { ::cppu::OPropertySetHelper::removeVetoableChangeListener(p1, p2); }
331     /** read all configured paths and create all needed internal structures. */
332     void impl_readAll();
333 
334 private:
335     virtual void SAL_CALL disposing() final override;
336 
337     /// @throws css::uno::RuntimeException
338     OUString getStringProperty(const OUString& p1);
339 
340     /// @throws css::uno::RuntimeException
341     void setStringProperty(const OUString& p1, const OUString& p2);
342 
343     /** read a path info using the old cfg schema.
344         This is needed for "migration on demand" reasons only.
345         Can be removed for next major release .-) */
346     std::vector<OUString> impl_readOldFormat(const OUString& sPath);
347 
348     /** read a path info using the new cfg schema. */
349     PathSettings::PathInfo impl_readNewFormat(const OUString& sPath);
350 
351     /** filter "real user defined paths" from the old configuration schema
352         and set it as UserPaths on the new schema.
353         Can be removed with new major release ... */
354 
355     void impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
356                                  const std::vector<OUString>& lOld );
357 
358     /** reload one path directly from the new configuration schema (because
359         it was updated by any external code) */
360     PathSettings::EChangeOp impl_updatePath(const OUString& sPath          ,
361                                                   bool         bNotifyListener);
362 
363     /** replace all might existing placeholder variables inside the given path ...
364         or check if the given path value uses paths, which can be replaced with predefined
365         placeholder variables ...
366      */
367     void impl_subst(std::vector<OUString>& lVals   ,
368                     const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
369                           bool                                               bReSubst);
370 
371     void impl_subst(PathSettings::PathInfo& aPath   ,
372                     bool                bReSubst);
373 
374     /** converts our new string list schema to the old ";" separated schema ... */
375     OUString impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath        ) const;
376     std::vector<OUString> impl_convertOldStyle2Path(const OUString&        sOldStylePath) const;
377 
378     /** remove still known paths from the given lList argument.
379         So real user defined paths can be extracted from the list of
380         fix internal paths !
381      */
382     void impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
383                               std::vector<OUString>& lList);
384 
385     /** rebuild the member m_lPropDesc using the path list m_lPaths. */
386     void impl_rebuildPropertyDescriptor();
387 
388     /** provides direct access to the list of path values
389         using its internal property id.
390      */
391     css::uno::Any impl_getPathValue(      sal_Int32      nID ) const;
392     void          impl_setPathValue(      sal_Int32      nID ,
393                                     const css::uno::Any& aVal);
394 
395     /** check the given handle and return the corresponding PathInfo reference.
396         These reference can be used then directly to manipulate these path. */
397           PathSettings::PathInfo* impl_getPathAccess     (sal_Int32 nHandle);
398     const PathSettings::PathInfo* impl_getPathAccessConst(sal_Int32 nHandle) const;
399 
400     /** it checks, if the given path value seems to be a valid URL or system path. */
401     bool impl_isValidPath(const OUString& sPath) const;
402     bool impl_isValidPath(const std::vector<OUString>& lPath) const;
403 
404     void impl_storePath(const PathSettings::PathInfo& aPath);
405 
406     css::uno::Sequence< sal_Int32 > impl_mapPathName2IDList(std::u16string_view sPath);
407 
408     void impl_notifyPropListener( std::u16string_view    sPath   ,
409                                   const PathSettings::PathInfo* pPathOld,
410                                   const PathSettings::PathInfo* pPathNew);
411 
412     //  OPropertySetHelper
413     virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any&  aConvertedValue,
414             css::uno::Any& aOldValue,
415             sal_Int32 nHandle,
416             const css::uno::Any& aValue ) override;
417     virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
418             const css::uno::Any&  aValue ) override;
419     virtual void SAL_CALL getFastPropertyValue( css::uno::Any&  aValue,
420             sal_Int32 nHandle ) const override;
421     // Avoid:
422     // warning: 'virtual css::uno::Any cppu::OPropertySetHelper::getFastPropertyValue(sal_Int32)' was hidden [-Woverloaded-virtual]
423     // warning:   by ‘virtual void {anonymous}::PathSettings::getFastPropertyValue(css::uno::Any&, sal_Int32) const’ [-Woverloaded-virtual]
424     using cppu::OPropertySetHelper::getFastPropertyValue;
425     virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
426     virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
427 
428     /** factory methods to guarantee right (but on demand) initialized members ... */
429     css::uno::Reference< css::util::XStringSubstitution > fa_getSubstitution();
430     css::uno::Reference< css::container::XNameAccess >    fa_getCfgOld();
431     css::uno::Reference< css::container::XNameAccess >    fa_getCfgNew();
432 };
433 
PathSettings(const css::uno::Reference<css::uno::XComponentContext> & xContext)434 PathSettings::PathSettings( const css::uno::Reference< css::uno::XComponentContext >& xContext )
435     : PathSettings_BASE(m_aMutex)
436     , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
437     ,   m_xContext (xContext)
438 {
439 }
440 
~PathSettings()441 PathSettings::~PathSettings()
442 {
443     disposing();
444 }
445 
disposing()446 void SAL_CALL PathSettings::disposing()
447 {
448     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
449 
450     css::uno::Reference< css::util::XChangesNotifier >
451         xBroadcaster(m_xCfgNew, css::uno::UNO_QUERY);
452     if (xBroadcaster.is())
453         xBroadcaster->removeChangesListener(m_xCfgNewListener);
454 
455     m_xSubstitution.clear();
456     m_xCfgOld.clear();
457     m_xCfgNew.clear();
458     m_xCfgNewListener.clear();
459 
460     m_pPropHelp.reset();
461 }
462 
queryInterface(const css::uno::Type & _rType)463 css::uno::Any SAL_CALL PathSettings::queryInterface( const css::uno::Type& _rType )
464 {
465     css::uno::Any aRet = PathSettings_BASE::queryInterface( _rType );
466     if ( !aRet.hasValue() )
467         aRet = ::cppu::OPropertySetHelper::queryInterface( _rType );
468     return aRet;
469 }
470 
getTypes()471 css::uno::Sequence< css::uno::Type > SAL_CALL PathSettings::getTypes(  )
472 {
473     return comphelper::concatSequences(
474         PathSettings_BASE::getTypes(),
475         ::cppu::OPropertySetHelper::getTypes()
476     );
477 }
478 
changesOccurred(const css::util::ChangesEvent & aEvent)479 void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent)
480 {
481     sal_Int32 c                 = aEvent.Changes.getLength();
482     sal_Int32 i                 = 0;
483     bool  bUpdateDescriptor = false;
484 
485     for (i=0; i<c; ++i)
486     {
487         const css::util::ElementChange& aChange = aEvent.Changes[i];
488 
489         OUString sChanged;
490         aChange.Accessor >>= sChanged;
491 
492         OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged);
493         if (!sPath.isEmpty())
494         {
495             PathSettings::EChangeOp eOp = impl_updatePath(sPath, true);
496             if (
497                 (eOp == PathSettings::E_ADDED  ) ||
498                 (eOp == PathSettings::E_REMOVED)
499                )
500                 bUpdateDescriptor = true;
501         }
502     }
503 
504     if (bUpdateDescriptor)
505         impl_rebuildPropertyDescriptor();
506 }
507 
disposing(const css::lang::EventObject & aSource)508 void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource)
509 {
510     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
511 
512     if (aSource.Source == m_xCfgNew)
513         m_xCfgNew.clear();
514 }
515 
getStringProperty(const OUString & p1)516 OUString PathSettings::getStringProperty(const OUString& p1)
517 {
518     css::uno::Any a = ::cppu::OPropertySetHelper::getPropertyValue(p1);
519     OUString s;
520     a >>= s;
521     return s;
522 }
523 
setStringProperty(const OUString & p1,const OUString & p2)524 void PathSettings::setStringProperty(const OUString& p1, const OUString& p2)
525 {
526     ::cppu::OPropertySetHelper::setPropertyValue(p1, css::uno::Any(p2));
527 }
528 
impl_readAll()529 void PathSettings::impl_readAll()
530 {
531     try
532     {
533         // TODO think about me
534         css::uno::Reference< css::container::XNameAccess > xCfg    = fa_getCfgNew();
535         css::uno::Sequence< OUString >              lPaths = xCfg->getElementNames();
536 
537         sal_Int32 c = lPaths.getLength();
538         for (sal_Int32 i = 0; i < c; ++i)
539         {
540             const OUString& sPath = lPaths[i];
541             impl_updatePath(sPath, false);
542         }
543     }
544     catch(const css::uno::RuntimeException& )
545     {
546     }
547 
548     impl_rebuildPropertyDescriptor();
549 }
550 
551 // NO substitution here ! It's done outside ...
impl_readOldFormat(const OUString & sPath)552 std::vector<OUString> PathSettings::impl_readOldFormat(const OUString& sPath)
553 {
554     css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() );
555     std::vector<OUString> aPathVal;
556 
557     if( xCfg->hasByName(sPath) )
558     {
559         css::uno::Any aVal( xCfg->getByName(sPath) );
560 
561         OUString                       sStringVal;
562         css::uno::Sequence< OUString > lStringListVal;
563 
564         if (aVal >>= sStringVal)
565         {
566             aPathVal.push_back(sStringVal);
567         }
568         else if (aVal >>= lStringListVal)
569         {
570             aPathVal = comphelper::sequenceToContainer<std::vector<OUString>>(lStringListVal);
571         }
572     }
573 
574     return aPathVal;
575 }
576 
577 // NO substitution here ! It's done outside ...
impl_readNewFormat(const OUString & sPath)578 PathSettings::PathInfo PathSettings::impl_readNewFormat(const OUString& sPath)
579 {
580     css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
581 
582     // get access to the "queried" path
583     css::uno::Reference< css::container::XNameAccess > xPath;
584     xCfg->getByName(sPath) >>= xPath;
585 
586     PathSettings::PathInfo aPathVal;
587 
588     // read internal path list
589     css::uno::Reference< css::container::XNameAccess > xIPath;
590     xPath->getByName("InternalPaths") >>= xIPath;
591     aPathVal.lInternalPaths = comphelper::sequenceToContainer<std::vector<OUString>>(xIPath->getElementNames());
592 
593     // read user defined path list
594     css::uno::Sequence<OUString> vTmpUserPathsSeq;
595     xPath->getByName(CFGPROP_USERPATHS) >>= vTmpUserPathsSeq;
596     aPathVal.lUserPaths = comphelper::sequenceToContainer<std::vector<OUString>>(vTmpUserPathsSeq);
597 
598     // read the writeable path
599     xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath;
600 
601     // avoid duplicates, by removing the writeable path from
602     // the user defined path list if it happens to be there too
603     std::vector<OUString>::iterator aI = std::find(aPathVal.lUserPaths.begin(), aPathVal.lUserPaths.end(), aPathVal.sWritePath);
604     if (aI != aPathVal.lUserPaths.end())
605         aPathVal.lUserPaths.erase(aI);
606 
607     // read state props
608     xPath->getByName("IsSinglePath") >>= aPathVal.bIsSinglePath;
609 
610     // analyze finalized/mandatory states
611     aPathVal.bIsReadonly = false;
612     css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY);
613     if (xInfo.is())
614     {
615         css::beans::Property aInfo = xInfo->getAsProperty();
616         bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY  ) == css::beans::PropertyAttribute::READONLY  );
617 
618         // Note: 'till we support finalized/mandatory on our API more in detail we handle
619         // all states simple as READONLY! But because all really needed paths are "mandatory" by default
620         // we have to handle "finalized" as the real "readonly" indicator.
621         aPathVal.bIsReadonly = bFinalized;
622     }
623 
624     return aPathVal;
625 }
626 
impl_storePath(const PathSettings::PathInfo & aPath)627 void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath)
628 {
629     css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew();
630     css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld();
631 
632     // try to replace path-parts with well known and supported variables.
633     // So an office can be moved easily to another location without losing
634     // its related paths.
635     PathInfo aResubstPath(aPath);
636     impl_subst(aResubstPath, true);
637 
638     // update new configuration
639     if (! aResubstPath.bIsSinglePath)
640     {
641         ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
642                                                             aResubstPath.sPathName,
643                                                             CFGPROP_USERPATHS,
644                             css::uno::makeAny(comphelper::containerToSequence(aResubstPath.lUserPaths)));
645     }
646 
647     ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
648                                                         aResubstPath.sPathName,
649                                                         CFGPROP_WRITEPATH,
650                                                         css::uno::makeAny(aResubstPath.sWritePath));
651 
652     ::comphelper::ConfigurationHelper::flush(xCfgNew);
653 
654     // remove the whole path from the old configuration!
655     // Otherwise we can't make sure that the diff between new and old configuration
656     // on loading time really represents a user setting!!!
657 
658     // Check if the given path exists inside the old configuration.
659     // Because our new configuration knows more than the list of old paths ... !
660     if (xCfgOld->hasByName(aResubstPath.sPathName))
661     {
662         css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW);
663         xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any());
664         ::comphelper::ConfigurationHelper::flush(xCfgOld);
665     }
666 }
667 
impl_mergeOldUserPaths(PathSettings::PathInfo & rPath,const std::vector<OUString> & lOld)668 void PathSettings::impl_mergeOldUserPaths(      PathSettings::PathInfo& rPath,
669                                           const std::vector<OUString>& lOld )
670 {
671     for (auto const& old : lOld)
672     {
673         if (rPath.bIsSinglePath)
674         {
675             SAL_WARN_IF(lOld.size()>1, "fwk", "PathSettings::impl_mergeOldUserPaths(): Single path has more than one path value inside old configuration (Common.xcu)!");
676             if ( rPath.sWritePath != old )
677                rPath.sWritePath = old;
678         }
679         else
680         {
681             if (
682                 (  std::find(rPath.lInternalPaths.begin(), rPath.lInternalPaths.end(), old) == rPath.lInternalPaths.end()) &&
683                 (  std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), old)     == rPath.lUserPaths.end()    ) &&
684                 (  rPath.sWritePath != old                                     )
685                )
686                rPath.lUserPaths.push_back(old);
687         }
688     }
689 }
690 
impl_updatePath(const OUString & sPath,bool bNotifyListener)691 PathSettings::EChangeOp PathSettings::impl_updatePath(const OUString& sPath          ,
692                                                             bool         bNotifyListener)
693 {
694     // SAFE ->
695     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
696 
697     PathSettings::PathInfo* pPathOld = nullptr;
698     PathSettings::PathInfo* pPathNew = nullptr;
699     PathSettings::EChangeOp eOp      = PathSettings::E_UNDEFINED;
700     PathSettings::PathInfo  aPath;
701 
702     try
703     {
704         aPath = impl_readNewFormat(sPath);
705         aPath.sPathName = sPath;
706         // replace all might existing variables with real values
707         // Do it before these old paths will be compared against the
708         // new path configuration. Otherwise some strings uses different variables ... but substitution
709         // will produce strings with same content (because some variables are redundant!)
710         impl_subst(aPath, false);
711     }
712     catch(const css::uno::RuntimeException&)
713         { throw; }
714     catch(const css::container::NoSuchElementException&)
715         { eOp = PathSettings::E_REMOVED; }
716     catch(const css::uno::Exception&)
717         { throw; }
718 
719     try
720     {
721         // migration of old user defined values on demand
722         // can be disabled for a new major
723         std::vector<OUString> lOldVals = impl_readOldFormat(sPath);
724         // replace all might existing variables with real values
725         // Do it before these old paths will be compared against the
726         // new path configuration. Otherwise some strings uses different variables ... but substitution
727         // will produce strings with same content (because some variables are redundant!)
728         impl_subst(lOldVals, fa_getSubstitution(), false);
729         impl_mergeOldUserPaths(aPath, lOldVals);
730     }
731     catch(const css::uno::RuntimeException&)
732         { throw; }
733     // Normal(!) exceptions can be ignored!
734     // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation
735     // we can't find a value for it inside the "old" configuration. So a NoSuchElementException
736     // will be normal .-)
737     catch(const css::uno::Exception&)
738         {}
739 
740     PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath);
741     if (eOp == PathSettings::E_UNDEFINED)
742     {
743         if (pPath != m_lPaths.end())
744             eOp = PathSettings::E_CHANGED;
745         else
746             eOp = PathSettings::E_ADDED;
747     }
748 
749     switch(eOp)
750     {
751         case PathSettings::E_ADDED :
752              {
753                 if (bNotifyListener)
754                 {
755                     pPathOld = nullptr;
756                     pPathNew = &aPath;
757                     impl_notifyPropListener(sPath, pPathOld, pPathNew);
758                 }
759                 m_lPaths[sPath] = aPath;
760              }
761              break;
762 
763         case PathSettings::E_CHANGED :
764              {
765                 if (bNotifyListener)
766                 {
767                     pPathOld = &(pPath->second);
768                     pPathNew = &aPath;
769                     impl_notifyPropListener(sPath, pPathOld, pPathNew);
770                 }
771                 m_lPaths[sPath] = aPath;
772              }
773              break;
774 
775         case PathSettings::E_REMOVED :
776              {
777                 if (pPath != m_lPaths.end())
778                 {
779                     if (bNotifyListener)
780                     {
781                         pPathOld = &(pPath->second);
782                         pPathNew = nullptr;
783                         impl_notifyPropListener(sPath, pPathOld, pPathNew);
784                     }
785                     m_lPaths.erase(pPath);
786                 }
787              }
788              break;
789 
790         default: // to let compiler be happy
791              break;
792     }
793 
794     return eOp;
795 }
796 
impl_mapPathName2IDList(std::u16string_view sPath)797 css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(std::u16string_view sPath)
798 {
799     OUString sInternalProp = OUString::Concat(sPath)+POSTFIX_INTERNAL_PATHS;
800     OUString sUserProp     = OUString::Concat(sPath)+POSTFIX_USER_PATHS;
801     OUString sWriteProp    = OUString::Concat(sPath)+POSTFIX_WRITE_PATH;
802 
803     // Attention: The default set of IDs is fix and must follow these schema.
804     // Otherwise the outside code ant work for new added properties.
805     // Why?
806     // The outside code must fire N events for every changed property.
807     // And the knowing about packaging of variables of the structure PathInfo
808     // follow these group IDs! But if such ID is not in the range of [0..IDGROUP_COUNT]
809     // the outside can't determine the right group ... and can not fire the right events .-)
810 
811     css::uno::Sequence< sal_Int32 > lIDs(IDGROUP_COUNT);
812     lIDs[0] = IDGROUP_OLDSTYLE;
813     lIDs[1] = IDGROUP_INTERNAL_PATHS;
814     lIDs[2] = IDGROUP_USER_PATHS;
815     lIDs[3] = IDGROUP_WRITE_PATH;
816 
817     sal_Int32 c = m_lPropDesc.getLength();
818     sal_Int32 i = 0;
819     for (i=0; i<c; ++i)
820     {
821         const css::beans::Property& rProp = m_lPropDesc[i];
822 
823         if (rProp.Name == sPath)
824             lIDs[IDGROUP_OLDSTYLE] = rProp.Handle;
825         else
826         if (rProp.Name == sInternalProp)
827             lIDs[IDGROUP_INTERNAL_PATHS] = rProp.Handle;
828         else
829         if (rProp.Name == sUserProp)
830             lIDs[IDGROUP_USER_PATHS] = rProp.Handle;
831         else
832         if (rProp.Name == sWriteProp)
833             lIDs[IDGROUP_WRITE_PATH] = rProp.Handle;
834     }
835 
836     return lIDs;
837 }
838 
impl_notifyPropListener(std::u16string_view sPath,const PathSettings::PathInfo * pPathOld,const PathSettings::PathInfo * pPathNew)839 void PathSettings::impl_notifyPropListener( std::u16string_view           sPath,
840                                             const PathSettings::PathInfo* pPathOld,
841                                             const PathSettings::PathInfo* pPathNew)
842 {
843     css::uno::Sequence< sal_Int32 >     lHandles(1);
844     css::uno::Sequence< css::uno::Any > lOldVals(1);
845     css::uno::Sequence< css::uno::Any > lNewVals(1);
846 
847     css::uno::Sequence< sal_Int32 > lIDs   = impl_mapPathName2IDList(sPath);
848     sal_Int32                       c      = lIDs.getLength();
849     sal_Int32                       i      = 0;
850     sal_Int32                       nMaxID = m_lPropDesc.getLength()-1;
851     for (i=0; i<c; ++i)
852     {
853         sal_Int32 nID = lIDs[i];
854 
855         if (
856             (nID < 0     ) ||
857             (nID > nMaxID)
858            )
859            continue;
860 
861         lHandles[0] = nID;
862         switch(impl_getPropGroup(nID))
863         {
864             case IDGROUP_OLDSTYLE :
865                  {
866                     if (pPathOld)
867                     {
868                         OUString sVal = impl_convertPath2OldStyle(*pPathOld);
869                         lOldVals[0] <<= sVal;
870                     }
871                     if (pPathNew)
872                     {
873                         OUString sVal = impl_convertPath2OldStyle(*pPathNew);
874                         lNewVals[0] <<= sVal;
875                     }
876                  }
877                  break;
878 
879             case IDGROUP_INTERNAL_PATHS :
880                  {
881                     if (pPathOld)
882                         lOldVals[0] <<= comphelper::containerToSequence(pPathOld->lInternalPaths);
883                     if (pPathNew)
884                         lNewVals[0] <<= comphelper::containerToSequence(pPathNew->lInternalPaths);
885                  }
886                  break;
887 
888             case IDGROUP_USER_PATHS :
889                  {
890                     if (pPathOld)
891                         lOldVals[0] <<= comphelper::containerToSequence(pPathOld->lUserPaths);
892                     if (pPathNew)
893                         lNewVals[0] <<= comphelper::containerToSequence(pPathNew->lUserPaths);
894                  }
895                  break;
896 
897             case IDGROUP_WRITE_PATH :
898                  {
899                     if (pPathOld)
900                         lOldVals[0] <<= pPathOld->sWritePath;
901                     if (pPathNew)
902                         lNewVals[0] <<= pPathNew->sWritePath;
903                  }
904                  break;
905         }
906 
907         fire(lHandles.getArray(),
908              lNewVals.getArray(),
909              lOldVals.getArray(),
910              1,
911              false);
912     }
913 }
914 
impl_subst(std::vector<OUString> & lVals,const css::uno::Reference<css::util::XStringSubstitution> & xSubst,bool bReSubst)915 void PathSettings::impl_subst(std::vector<OUString>& lVals   ,
916                               const css::uno::Reference< css::util::XStringSubstitution >& xSubst  ,
917                                     bool                                               bReSubst)
918 {
919     for (auto & old : lVals)
920     {
921         OUString  sNew;
922         if (bReSubst)
923             sNew = xSubst->reSubstituteVariables(old);
924         else
925             sNew = xSubst->substituteVariables(old, false);
926 
927         old = sNew;
928     }
929 }
930 
impl_subst(PathSettings::PathInfo & aPath,bool bReSubst)931 void PathSettings::impl_subst(PathSettings::PathInfo& aPath   ,
932                               bool                bReSubst)
933 {
934     css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution();
935 
936     impl_subst(aPath.lInternalPaths, xSubst, bReSubst);
937     impl_subst(aPath.lUserPaths    , xSubst, bReSubst);
938     if (bReSubst)
939         aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath);
940     else
941         aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, false);
942 }
943 
impl_convertPath2OldStyle(const PathSettings::PathInfo & rPath) const944 OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const
945 {
946     std::vector<OUString> lTemp;
947     lTemp.reserve(rPath.lInternalPaths.size() + rPath.lUserPaths.size() + 1);
948 
949     for (auto const& internalPath : rPath.lInternalPaths)
950     {
951         lTemp.push_back(internalPath);
952     }
953     for (auto const& userPath : rPath.lUserPaths)
954     {
955         lTemp.push_back(userPath);
956     }
957 
958     if (!rPath.sWritePath.isEmpty())
959         lTemp.push_back(rPath.sWritePath);
960 
961     OUStringBuffer sPathVal(256);
962     for (  auto pIt  = lTemp.begin();
963            pIt != lTemp.end();
964                                )
965     {
966         sPathVal.append(*pIt);
967         ++pIt;
968         if (pIt != lTemp.end())
969             sPathVal.append(";");
970     }
971 
972     return sPathVal.makeStringAndClear();
973 }
974 
impl_convertOldStyle2Path(const OUString & sOldStylePath) const975 std::vector<OUString> PathSettings::impl_convertOldStyle2Path(const OUString& sOldStylePath) const
976 {
977     std::vector<OUString> lList;
978     sal_Int32    nToken = 0;
979     do
980     {
981         OUString sToken = sOldStylePath.getToken(0, ';', nToken);
982         if (!sToken.isEmpty())
983             lList.push_back(sToken);
984     }
985     while(nToken >= 0);
986 
987     return lList;
988 }
989 
impl_purgeKnownPaths(PathSettings::PathInfo & rPath,std::vector<OUString> & lList)990 void PathSettings::impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
991                                         std::vector<OUString>& lList)
992 {
993     // Erase items in the internal path list from lList.
994     // Also erase items in the internal path list from the user path list.
995     for (auto const& internalPath : rPath.lInternalPaths)
996     {
997         std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), internalPath);
998         if (pItem != lList.end())
999             lList.erase(pItem);
1000         pItem = std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), internalPath);
1001         if (pItem != rPath.lUserPaths.end())
1002             rPath.lUserPaths.erase(pItem);
1003     }
1004 
1005     // Erase items not in lList from the user path list.
1006     rPath.lUserPaths.erase(std::remove_if(rPath.lUserPaths.begin(), rPath.lUserPaths.end(),
1007         [&lList](const OUString& rItem) {
1008             return std::find(lList.begin(), lList.end(), rItem) == lList.end();
1009         }),
1010         rPath.lUserPaths.end());
1011 
1012     // Erase items in the user path list from lList.
1013     for (auto const& userPath : rPath.lUserPaths)
1014     {
1015         std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), userPath);
1016         if (pItem != lList.end())
1017             lList.erase(pItem);
1018     }
1019 
1020     // Erase the write path from lList
1021     std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), rPath.sWritePath);
1022     if (pItem != lList.end())
1023         lList.erase(pItem);
1024 }
1025 
impl_rebuildPropertyDescriptor()1026 void PathSettings::impl_rebuildPropertyDescriptor()
1027 {
1028     // SAFE ->
1029     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1030 
1031     sal_Int32 c = static_cast<sal_Int32>(m_lPaths.size());
1032     sal_Int32 i = 0;
1033     m_lPropDesc.realloc(c*IDGROUP_COUNT);
1034 
1035     for (auto const& path : m_lPaths)
1036     {
1037         const PathSettings::PathInfo& rPath = path.second;
1038         css::beans::Property*   pProp = nullptr;
1039 
1040         pProp             = &(m_lPropDesc[i]);
1041         pProp->Name       = rPath.sPathName;
1042         pProp->Handle     = i;
1043         pProp->Type       = cppu::UnoType<OUString>::get();
1044         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1045         if (rPath.bIsReadonly)
1046             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1047         ++i;
1048 
1049         pProp             = &(m_lPropDesc[i]);
1050         pProp->Name       = rPath.sPathName+POSTFIX_INTERNAL_PATHS;
1051         pProp->Handle     = i;
1052         pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
1053         pProp->Attributes = css::beans::PropertyAttribute::BOUND   |
1054                             css::beans::PropertyAttribute::READONLY;
1055         ++i;
1056 
1057         pProp             = &(m_lPropDesc[i]);
1058         pProp->Name       = rPath.sPathName+POSTFIX_USER_PATHS;
1059         pProp->Handle     = i;
1060         pProp->Type       = cppu::UnoType<css::uno::Sequence< OUString >>::get();
1061         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1062         if (rPath.bIsReadonly)
1063             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1064         ++i;
1065 
1066         pProp             = &(m_lPropDesc[i]);
1067         pProp->Name       = rPath.sPathName+POSTFIX_WRITE_PATH;
1068         pProp->Handle     = i;
1069         pProp->Type       = cppu::UnoType<OUString>::get();
1070         pProp->Attributes = css::beans::PropertyAttribute::BOUND;
1071         if (rPath.bIsReadonly)
1072             pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
1073         ++i;
1074     }
1075 
1076     m_pPropHelp.reset(new ::cppu::OPropertyArrayHelper(m_lPropDesc, false)); // false => not sorted ... must be done inside helper
1077 
1078     // <- SAFE
1079 }
1080 
impl_getPathValue(sal_Int32 nID) const1081 css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const
1082 {
1083     const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID);
1084     if (! pPath)
1085         throw css::lang::IllegalArgumentException();
1086 
1087     css::uno::Any aVal;
1088     switch(impl_getPropGroup(nID))
1089     {
1090         case IDGROUP_OLDSTYLE :
1091              {
1092                 OUString sVal = impl_convertPath2OldStyle(*pPath);
1093                 aVal <<= sVal;
1094              }
1095              break;
1096 
1097         case IDGROUP_INTERNAL_PATHS :
1098              {
1099                 aVal <<= comphelper::containerToSequence(pPath->lInternalPaths);
1100              }
1101              break;
1102 
1103         case IDGROUP_USER_PATHS :
1104              {
1105                 aVal <<= comphelper::containerToSequence(pPath->lUserPaths);
1106              }
1107              break;
1108 
1109         case IDGROUP_WRITE_PATH :
1110              {
1111                 aVal <<= pPath->sWritePath;
1112              }
1113              break;
1114     }
1115 
1116     return aVal;
1117 }
1118 
impl_setPathValue(sal_Int32 nID,const css::uno::Any & aVal)1119 void PathSettings::impl_setPathValue(      sal_Int32      nID ,
1120                                      const css::uno::Any& aVal)
1121 {
1122     PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID);
1123     if (! pOrgPath)
1124         throw css::container::NoSuchElementException();
1125 
1126     // We work on a copied path ... so we can be sure that errors during this operation
1127     // does not make our internal cache invalid  .-)
1128     PathSettings::PathInfo aChangePath(*pOrgPath);
1129 
1130     switch(impl_getPropGroup(nID))
1131     {
1132         case IDGROUP_OLDSTYLE :
1133              {
1134                 OUString sVal;
1135                 aVal >>= sVal;
1136                 std::vector<OUString> lList = impl_convertOldStyle2Path(sVal);
1137                 impl_subst(lList, fa_getSubstitution(), false);
1138                 impl_purgeKnownPaths(aChangePath, lList);
1139                 if (! impl_isValidPath(lList))
1140                     throw css::lang::IllegalArgumentException();
1141 
1142                 if (aChangePath.bIsSinglePath)
1143                 {
1144                     SAL_WARN_IF(lList.size()>1, "fwk", "PathSettings::impl_setPathValue(): You try to set more than path value for a defined SINGLE_PATH!");
1145                     if ( !lList.empty() )
1146                         aChangePath.sWritePath = *(lList.begin());
1147                     else
1148                         aChangePath.sWritePath.clear();
1149                 }
1150                 else
1151                 {
1152                     for (auto const& elem : lList)
1153                     {
1154                         aChangePath.lUserPaths.push_back(elem);
1155                     }
1156                 }
1157              }
1158              break;
1159 
1160         case IDGROUP_INTERNAL_PATHS :
1161              {
1162                 if (aChangePath.bIsSinglePath)
1163                 {
1164                     throw css::uno::Exception(
1165                         "The path '" + aChangePath.sPathName
1166                         + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
1167                         static_cast< ::cppu::OWeakObject* >(this));
1168                 }
1169 
1170                 css::uno::Sequence<OUString> lTmpList;
1171                 aVal >>= lTmpList;
1172                 std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
1173                 if (! impl_isValidPath(lList))
1174                     throw css::lang::IllegalArgumentException();
1175                 aChangePath.lInternalPaths = lList;
1176              }
1177              break;
1178 
1179         case IDGROUP_USER_PATHS :
1180              {
1181                 if (aChangePath.bIsSinglePath)
1182                 {
1183                     throw css::uno::Exception(
1184                         "The path '" + aChangePath.sPathName
1185                         + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
1186                         static_cast< ::cppu::OWeakObject* >(this));
1187                 }
1188 
1189                 css::uno::Sequence<OUString> lTmpList;
1190                 aVal >>= lTmpList;
1191                 std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
1192                 if (! impl_isValidPath(lList))
1193                     throw css::lang::IllegalArgumentException();
1194                 aChangePath.lUserPaths = lList;
1195              }
1196              break;
1197 
1198         case IDGROUP_WRITE_PATH :
1199              {
1200                 OUString sVal;
1201                 aVal >>= sVal;
1202                 if (! impl_isValidPath(sVal))
1203                     throw css::lang::IllegalArgumentException();
1204                 aChangePath.sWritePath = sVal;
1205              }
1206              break;
1207     }
1208 
1209     // TODO check if path has at least one path value set
1210     // At least it depends from the feature using this path, if an empty path list is allowed.
1211 
1212     // first we should try to store the changed (copied!) path ...
1213     // In case an error occurs on saving time an exception is thrown ...
1214     // If no exception occurs we can update our internal cache (means
1215     // we can overwrite pOrgPath !
1216     impl_storePath(aChangePath);
1217     *pOrgPath = std::move(aChangePath);
1218 }
1219 
impl_isValidPath(const std::vector<OUString> & lPath) const1220 bool PathSettings::impl_isValidPath(const std::vector<OUString>& lPath) const
1221 {
1222     for (auto const& path : lPath)
1223     {
1224         if (! impl_isValidPath(path))
1225             return false;
1226     }
1227 
1228     return true;
1229 }
1230 
impl_isValidPath(const OUString & sPath) const1231 bool PathSettings::impl_isValidPath(const OUString& sPath) const
1232 {
1233     // allow empty path to reset a path.
1234 // idea by LLA to support empty paths
1235 //    if (sPath.getLength() == 0)
1236 //    {
1237 //        return sal_True;
1238 //    }
1239 
1240     return (! INetURLObject(sPath).HasError());
1241 }
1242 
impl_extractBaseFromPropName(const OUString & sPropName)1243 OUString impl_extractBaseFromPropName(const OUString& sPropName)
1244 {
1245     sal_Int32 i = sPropName.indexOf(POSTFIX_INTERNAL_PATHS);
1246     if (i > -1)
1247         return sPropName.copy(0, i);
1248     i = sPropName.indexOf(POSTFIX_USER_PATHS);
1249     if (i > -1)
1250         return sPropName.copy(0, i);
1251     i = sPropName.indexOf(POSTFIX_WRITE_PATH);
1252     if (i > -1)
1253         return sPropName.copy(0, i);
1254 
1255     return sPropName;
1256 }
1257 
impl_getPathAccess(sal_Int32 nHandle)1258 PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle)
1259 {
1260     // SAFE ->
1261     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1262 
1263     if (nHandle > (m_lPropDesc.getLength()-1))
1264         return nullptr;
1265 
1266     const css::beans::Property&            rProp = m_lPropDesc[nHandle];
1267     OUString                  sProp = impl_extractBaseFromPropName(rProp.Name);
1268     PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp);
1269 
1270     if (rPath != m_lPaths.end())
1271        return &(rPath->second);
1272 
1273     return nullptr;
1274     // <- SAFE
1275 }
1276 
impl_getPathAccessConst(sal_Int32 nHandle) const1277 const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const
1278 {
1279     // SAFE ->
1280     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1281 
1282     if (nHandle > (m_lPropDesc.getLength()-1))
1283         return nullptr;
1284 
1285     const css::beans::Property&     rProp = m_lPropDesc[nHandle];
1286     OUString                        sProp = impl_extractBaseFromPropName(rProp.Name);
1287     PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp);
1288 
1289     if (rPath != m_lPaths.end())
1290        return &(rPath->second);
1291 
1292     return nullptr;
1293     // <- SAFE
1294 }
1295 
convertFastPropertyValue(css::uno::Any & aConvertedValue,css::uno::Any & aOldValue,sal_Int32 nHandle,const css::uno::Any & aValue)1296 sal_Bool SAL_CALL PathSettings::convertFastPropertyValue(      css::uno::Any& aConvertedValue,
1297                                                                css::uno::Any& aOldValue      ,
1298                                                                sal_Int32      nHandle        ,
1299                                                          const css::uno::Any& aValue         )
1300 {
1301     // throws NoSuchElementException !
1302     css::uno::Any aCurrentVal = impl_getPathValue(nHandle);
1303 
1304     return PropHelper::willPropertyBeChanged(
1305                 aCurrentVal,
1306                 aValue,
1307                 aOldValue,
1308                 aConvertedValue);
1309 }
1310 
setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const css::uno::Any & aValue)1311 void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast(      sal_Int32      nHandle,
1312                                                              const css::uno::Any& aValue )
1313 {
1314     // throws NoSuchElement- and IllegalArgumentException !
1315     impl_setPathValue(nHandle, aValue);
1316 }
1317 
getFastPropertyValue(css::uno::Any & aValue,sal_Int32 nHandle) const1318 void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue ,
1319                                                  sal_Int32      nHandle) const
1320 {
1321     aValue = impl_getPathValue(nHandle);
1322 }
1323 
getInfoHelper()1324 ::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper()
1325 {
1326     return *m_pPropHelp;
1327 }
1328 
getPropertySetInfo()1329 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo()
1330 {
1331     return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
1332 }
1333 
fa_getSubstitution()1334 css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution()
1335 {
1336     css::uno::Reference< css::util::XStringSubstitution > xSubst;
1337     { // SAFE ->
1338     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1339     xSubst = m_xSubstitution;
1340     }
1341 
1342     if (! xSubst.is())
1343     {
1344         // create the needed substitution service.
1345         // We must replace all used variables inside read path values.
1346         // In case we can't do so... the whole office can't work really.
1347         // That's why it seems to be OK to throw a RuntimeException then.
1348         xSubst = css::util::PathSubstitution::create(m_xContext);
1349 
1350         { // SAFE ->
1351         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1352         m_xSubstitution = xSubst;
1353         }
1354     }
1355 
1356     return xSubst;
1357 }
1358 
fa_getCfgOld()1359 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld()
1360 {
1361     css::uno::Reference< css::container::XNameAccess > xCfg;
1362     { // SAFE ->
1363     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1364     xCfg = m_xCfgOld;
1365     } // <- SAFE
1366 
1367     if (! xCfg.is())
1368     {
1369         xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
1370                         m_xContext,
1371                         "org.openoffice.Office.Common/Path/Current",
1372                         ::comphelper::EConfigurationModes::Standard), // not readonly! Sometimes we need write access there !!!
1373                    css::uno::UNO_QUERY_THROW);
1374 
1375         { // SAFE ->
1376         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1377         m_xCfgOld = xCfg;
1378         }
1379     }
1380 
1381     return xCfg;
1382 }
1383 
fa_getCfgNew()1384 css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew()
1385 {
1386     css::uno::Reference< css::container::XNameAccess > xCfg;
1387     { // SAFE ->
1388     osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1389     xCfg = m_xCfgNew;
1390     } // <- SAFE
1391 
1392     if (! xCfg.is())
1393     {
1394         xCfg.set(  ::comphelper::ConfigurationHelper::openConfig(
1395                         m_xContext,
1396                         "org.openoffice.Office.Paths/Paths",
1397                         ::comphelper::EConfigurationModes::Standard),
1398                    css::uno::UNO_QUERY_THROW);
1399 
1400         { // SAFE ->
1401         osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1402         m_xCfgNew = xCfg;
1403         m_xCfgNewListener = new WeakChangesListener(this);
1404         }
1405 
1406         css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW);
1407         xBroadcaster->addChangesListener(m_xCfgNewListener);
1408     }
1409 
1410     return xCfg;
1411 }
1412 
1413 }
1414 
1415 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_framework_PathSettings_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)1416 com_sun_star_comp_framework_PathSettings_get_implementation(
1417     css::uno::XComponentContext *context,
1418     css::uno::Sequence<css::uno::Any> const &)
1419 {
1420     rtl::Reference<PathSettings> xPathSettings = new PathSettings(context);
1421     // fill cache
1422     xPathSettings->impl_readAll();
1423 
1424     return cppu::acquire(xPathSettings.get());
1425 }
1426 
1427 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1428