Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/services/substitutepathvars.cxx
Line
Count
Source
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 <config_folders.h>
21
22
#include <comphelper/lok.hxx>
23
#include <comphelper/compbase.hxx>
24
#include <cppuhelper/supportsservice.hxx>
25
26
#include <unotools/bootstrap.hxx>
27
#include <unotools/configmgr.hxx>
28
#include <o3tl/environment.hxx>
29
#include <osl/file.hxx>
30
#include <osl/process.h>
31
#include <osl/security.hxx>
32
#include <osl/thread.hxx>
33
#include <i18nlangtag/languagetag.hxx>
34
#include <tools/urlobj.hxx>
35
#include <rtl/ustrbuf.hxx>
36
#include <rtl/bootstrap.hxx>
37
38
#include <officecfg/Office/Paths.hxx>
39
40
#include <com/sun/star/lang/XInitialization.hpp>
41
#include <com/sun/star/lang/XServiceInfo.hpp>
42
#include <com/sun/star/container/NoSuchElementException.hpp>
43
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
44
#include <com/sun/star/container/XNameAccess.hpp>
45
#include <com/sun/star/util/XStringSubstitution.hpp>
46
47
#include <unordered_map>
48
49
using namespace com::sun::star::uno;
50
using namespace com::sun::star::container;
51
52
namespace {
53
54
enum PreDefVariable
55
{
56
    PREDEFVAR_INST,
57
    PREDEFVAR_PROG,
58
    PREDEFVAR_USER,
59
    PREDEFVAR_WORK,
60
    PREDEFVAR_HOME,
61
    PREDEFVAR_TEMP,
62
    PREDEFVAR_PATH,
63
    PREDEFVAR_USERNAME,
64
    PREDEFVAR_LANGID,
65
    PREDEFVAR_VLANG,
66
    PREDEFVAR_INSTPATH,
67
    PREDEFVAR_PROGPATH,
68
    PREDEFVAR_USERPATH,
69
    PREDEFVAR_INSTURL,
70
    PREDEFVAR_PROGURL,
71
    PREDEFVAR_USERURL,
72
    PREDEFVAR_WORKDIRURL,
73
    // New variable of hierarchy service (#i32656#)
74
    PREDEFVAR_BASEINSTURL,
75
    PREDEFVAR_USERDATAURL,
76
    PREDEFVAR_BRANDBASEURL,
77
    PREDEFVAR_COUNT
78
};
79
80
struct FixedVariable
81
{
82
    OUString     pVarName;
83
    bool         bAbsPath;
84
};
85
86
// Table with all fixed/predefined variables supported.
87
constexpr FixedVariable aFixedVarTable[PREDEFVAR_COUNT] =
88
{
89
    { u"$(inst)"_ustr,         true  }, // PREDEFVAR_INST
90
    { u"$(prog)"_ustr,         true  }, // PREDEFVAR_PROG
91
    { u"$(user)"_ustr,         true  }, // PREDEFVAR_USER
92
    { u"$(work)"_ustr,         true  }, // PREDEFVAR_WORK, special variable
93
                                  //  (transient)
94
    { u"$(home)"_ustr,         true  }, // PREDEFVAR_HOME
95
    { u"$(temp)"_ustr,         true  }, // PREDEFVAR_TEMP
96
    { u"$(path)"_ustr,         true  }, // PREDEFVAR_PATH
97
    { u"$(username)"_ustr,     false }, // PREDEFVAR_USERNAME
98
    { u"$(langid)"_ustr,       false }, // PREDEFVAR_LANGID
99
    { u"$(vlang)"_ustr,        false }, // PREDEFVAR_VLANG
100
    { u"$(instpath)"_ustr,     true  }, // PREDEFVAR_INSTPATH
101
    { u"$(progpath)"_ustr,     true  }, // PREDEFVAR_PROGPATH
102
    { u"$(userpath)"_ustr,     true  }, // PREDEFVAR_USERPATH
103
    { u"$(insturl)"_ustr,      true  }, // PREDEFVAR_INSTURL
104
    { u"$(progurl)"_ustr,      true  }, // PREDEFVAR_PROGURL
105
    { u"$(userurl)"_ustr,      true  }, // PREDEFVAR_USERURL
106
    { u"$(workdirurl)"_ustr,   true  }, // PREDEFVAR_WORKDIRURL, special variable
107
                                  //  (transient) and don't use for
108
                                  //  resubstitution
109
    { u"$(baseinsturl)"_ustr,  true  }, // PREDEFVAR_BASEINSTURL
110
    { u"$(userdataurl)"_ustr,  true  }, // PREDEFVAR_USERDATAURL
111
    { u"$(brandbaseurl)"_ustr, true  }  // PREDEFVAR_BRANDBASEURL
112
};
113
114
struct PredefinedPathVariables
115
{
116
    // Predefined variables supported by substitute variables
117
    LanguageType    m_eLanguageType;               // Language type of Office
118
    OUString   m_FixedVar[ PREDEFVAR_COUNT ];      // Variable value access by PreDefVariable
119
    OUString   m_FixedVarNames[ PREDEFVAR_COUNT ]; // Variable name access by PreDefVariable
120
};
121
122
struct ReSubstFixedVarOrder
123
{
124
    sal_Int32       nVarValueLength;
125
    PreDefVariable  eVariable;
126
127
    bool operator< ( const ReSubstFixedVarOrder& aFixedVarOrder ) const
128
0
    {
129
        // Reverse operator< to have high to low ordering
130
0
        return ( nVarValueLength > aFixedVarOrder.nVarValueLength );
131
0
    }
132
};
133
134
typedef comphelper::WeakComponentImplHelper<
135
    css::util::XStringSubstitution,
136
    css::lang::XServiceInfo,
137
    css::lang::XInitialization > SubstitutePathVariables_BASE;
138
139
class SubstitutePathVariables : public SubstitutePathVariables_BASE
140
{
141
public:
142
    explicit SubstitutePathVariables();
143
144
    virtual OUString SAL_CALL getImplementationName() override
145
0
    {
146
0
        return u"com.sun.star.comp.framework.PathSubstitution"_ustr;
147
0
    }
148
149
    virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
150
0
    {
151
0
        return cppu::supportsService(this, ServiceName);
152
0
    }
153
154
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
155
0
    {
156
0
        return {u"com.sun.star.util.PathSubstitution"_ustr};
157
0
    }
158
159
    // XStringSubstitution
160
    virtual OUString SAL_CALL substituteVariables( const OUString& aText, sal_Bool bSubstRequired ) override;
161
    virtual OUString SAL_CALL reSubstituteVariables( const OUString& aText ) override;
162
    virtual OUString SAL_CALL getSubstituteVariableValue( const OUString& variable ) override;
163
164
    // XInitialization
165
    virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& /*rArguments*/) override;
166
167
private:
168
    void            impl_initialize();
169
170
    void            SetPredefinedPathVariables();
171
172
    // Special case (transient) values can change during runtime!
173
    // Don't store them in the pre defined struct
174
    static OUString GetWorkPath();
175
    static OUString GetWorkVariableValue();
176
    static OUString GetPathVariableValue();
177
178
    static OUString GetHomeVariableValue();
179
180
    // XStringSubstitution implementation methods
181
    /// @throws css::container::NoSuchElementException
182
    /// @throws css::uno::RuntimeException
183
    OUString impl_substituteVariable( const OUString& aText, bool bSustRequired );
184
    /// @throws css::uno::RuntimeException
185
    OUString impl_reSubstituteVariables( const OUString& aText );
186
    /// @throws css::container::NoSuchElementException
187
    /// @throws css::uno::RuntimeException
188
    OUString const & impl_getSubstituteVariableValue( const OUString& variable );
189
190
private:
191
    typedef std::unordered_map<OUString, PreDefVariable>
192
        VarNameToIndexMap;
193
194
    VarNameToIndexMap            m_aPreDefVarMap;         // Mapping from pre-def variable names to enum for array access
195
    PredefinedPathVariables      m_aPreDefVars;           // All predefined variables
196
    std::vector<ReSubstFixedVarOrder> m_aReSubstFixedVarOrder; // To speed up resubstitution fixed variables (order for lookup)
197
};
198
199
SubstitutePathVariables::SubstitutePathVariables()
200
18.5k
{
201
18.5k
    impl_initialize();
202
18.5k
}
203
204
void SubstitutePathVariables::impl_initialize()
205
18.5k
{
206
18.5k
    SetPredefinedPathVariables();
207
208
    // Init the predefined/fixed variable to index hash map
209
18.5k
    for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
210
0
    {
211
        // Store variable name into struct of predefined/fixed variables
212
0
        m_aPreDefVars.m_FixedVarNames[i] = aFixedVarTable[i].pVarName;
213
214
        // Create hash map entry
215
0
        m_aPreDefVarMap.emplace( m_aPreDefVars.m_FixedVarNames[i], PreDefVariable(i) );
216
0
    }
217
218
    // Sort predefined/fixed variable to path length
219
18.5k
    for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
220
0
    {
221
0
        if (( i != PREDEFVAR_WORKDIRURL ) && ( i != PREDEFVAR_PATH ))
222
0
        {
223
            // Special path variables, don't include into automatic resubstitution search!
224
            // $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry
225
            // and it could be possible that it will be resubstituted by itself!!
226
            // Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted!
227
0
            ReSubstFixedVarOrder aFixedVar;
228
0
            aFixedVar.eVariable       = PreDefVariable(i);
229
0
            aFixedVar.nVarValueLength = m_aPreDefVars.m_FixedVar[static_cast<sal_Int32>(aFixedVar.eVariable)].getLength();
230
0
            m_aReSubstFixedVarOrder.push_back( aFixedVar );
231
0
        }
232
0
    }
233
18.5k
    sort(m_aReSubstFixedVarOrder.begin(),m_aReSubstFixedVarOrder.end());
234
18.5k
}
235
236
void SAL_CALL SubstitutePathVariables::initialize(const css::uno::Sequence<css::uno::Any>& /*rArguments*/)
237
0
{
238
0
    std::unique_lock g(m_aMutex);
239
0
    impl_initialize();
240
0
}
241
242
// XStringSubstitution
243
OUString SAL_CALL SubstitutePathVariables::substituteVariables( const OUString& aText, sal_Bool bSubstRequired )
244
0
{
245
0
    std::unique_lock g(m_aMutex);
246
0
    return impl_substituteVariable( aText, bSubstRequired );
247
0
}
248
249
OUString SAL_CALL SubstitutePathVariables::reSubstituteVariables( const OUString& aText )
250
0
{
251
0
    std::unique_lock g(m_aMutex);
252
0
    return impl_reSubstituteVariables( aText );
253
0
}
254
255
OUString SAL_CALL SubstitutePathVariables::getSubstituteVariableValue( const OUString& aVariable )
256
0
{
257
0
    std::unique_lock g(m_aMutex);
258
0
    return impl_getSubstituteVariableValue( aVariable );
259
0
}
260
261
// static
262
OUString SubstitutePathVariables::GetWorkPath()
263
18.5k
{
264
18.5k
    OUString aWorkPath;
265
18.5k
    css::uno::Reference< css::container::XHierarchicalNameAccess > xPaths(officecfg::Office::Paths::Paths::get(), css::uno::UNO_QUERY_THROW);
266
18.5k
    if (!(xPaths->getByHierarchicalName(u"['Work']/WritePath"_ustr) >>= aWorkPath))
267
        // fallback in case config layer does not return a usable work dir value.
268
0
        aWorkPath = GetWorkVariableValue();
269
270
18.5k
    return aWorkPath;
271
18.5k
}
272
273
// static
274
OUString SubstitutePathVariables::GetWorkVariableValue()
275
18.5k
{
276
18.5k
    OUString aWorkPath;
277
18.5k
    std::optional<OUString> x(officecfg::Office::Paths::Variables::Work::get());
278
18.5k
    if (!x)
279
18.5k
    {
280
        // fallback to $HOME in case platform dependent config layer does not return
281
        // a usable work dir value.
282
18.5k
        osl::Security aSecurity;
283
18.5k
        aSecurity.getHomeDir( aWorkPath );
284
18.5k
    }
285
0
    else
286
0
        aWorkPath = *x;
287
18.5k
    return aWorkPath;
288
18.5k
}
289
290
// static
291
OUString SubstitutePathVariables::GetHomeVariableValue()
292
18.5k
{
293
18.5k
    osl::Security   aSecurity;
294
18.5k
    OUString   aHomePath;
295
296
18.5k
    aSecurity.getHomeDir( aHomePath );
297
18.5k
    return aHomePath;
298
18.5k
}
299
300
// static
301
OUString SubstitutePathVariables::GetPathVariableValue()
302
0
{
303
0
    OUString aRetStr;
304
0
    OUString aPathList = o3tl::getEnvironment(u"PATH"_ustr);
305
306
0
    if (!aPathList.isEmpty())
307
0
    {
308
0
        const int PATH_EXTEND_FACTOR = 200;
309
0
        OUString       aTmp;
310
0
        OUStringBuffer aPathStrBuffer( aPathList.getLength() * PATH_EXTEND_FACTOR / 100 );
311
312
0
        bool      bAppendSep = false;
313
0
        sal_Int32 nToken = 0;
314
0
        do
315
0
        {
316
0
            OUString sToken = aPathList.getToken(0, SAL_PATHSEPARATOR, nToken);
317
0
            if (!sToken.isEmpty() &&
318
0
                osl::FileBase::getFileURLFromSystemPath( sToken, aTmp ) ==
319
0
                osl::FileBase::RC::E_None )
320
0
            {
321
0
                if ( bAppendSep )
322
0
                    aPathStrBuffer.append( ";" ); // Office uses ';' as path separator
323
0
                aPathStrBuffer.append( aTmp );
324
0
                bAppendSep = true;
325
0
            }
326
0
        }
327
0
        while(nToken>=0);
328
329
0
        aRetStr = aPathStrBuffer.makeStringAndClear();
330
0
    }
331
332
0
    return aRetStr;
333
0
}
334
335
OUString SubstitutePathVariables::impl_substituteVariable( const OUString& rText, bool bSubstRequired )
336
0
{
337
    // This is maximal recursive depth supported!
338
0
    const sal_Int32 nMaxRecursiveDepth = 8;
339
340
0
    OUString   aWorkText = rText;
341
0
    OUString   aResult;
342
343
    // Use vector with strings to detect endless recursions!
344
0
    std::vector< OUString > aEndlessRecursiveDetector;
345
346
    // Search for first occurrence of "$(...".
347
0
    sal_Int32   nDepth = 0;
348
0
    bool        bSubstitutionCompleted = false;
349
0
    sal_Int32   nPosition = aWorkText.indexOf( "$(" );
350
0
    sal_Int32   nLength = 0; // = count of letters from "$(" to ")" in string
351
0
    bool        bVarNotSubstituted = false;
352
353
    // Have we found any variable like "$(...)"?
354
0
    if ( nPosition != -1 )
355
0
    {
356
        // Yes; Get length of found variable.
357
        // If no ")" was found - nLength is set to 0 by default! see before.
358
0
        sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
359
0
        if ( nEndPosition != -1 )
360
0
            nLength = nEndPosition - nPosition + 1;
361
0
    }
362
363
    // Is there something to replace ?
364
0
    bool bWorkRetrieved       = false;
365
0
    bool bWorkDirURLRetrieved = false;
366
0
    while (nDepth < nMaxRecursiveDepth)
367
0
    {
368
0
        while ( ( nPosition != -1 ) && ( nLength > 3 ) ) // "$(" ")"
369
0
        {
370
            // YES; Get the next variable for replace.
371
0
            sal_Int32     nReplaceLength  = 0;
372
0
            OUString aReplacement;
373
0
            OUString aSubString      = aWorkText.copy( nPosition, nLength );
374
375
            // Path variables are not case sensitive!
376
0
            OUString aSubVarString = aSubString.toAsciiLowerCase();
377
0
            VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString );
378
0
            if ( pNTOIIter != m_aPreDefVarMap.end() )
379
0
            {
380
                // Fixed/Predefined variable found
381
0
                PreDefVariable nIndex = pNTOIIter->second;
382
383
                // Determine variable value and length from array/table
384
0
                if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved )
385
0
                {
386
                    // Transient value, retrieve it again
387
0
                    m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkVariableValue();
388
0
                    bWorkRetrieved = true;
389
0
                }
390
0
                else if ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved )
391
0
                {
392
                    // Transient value, retrieve it again
393
0
                    m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkPath();
394
0
                    bWorkDirURLRetrieved = true;
395
0
                }
396
397
                // Check preconditions to substitute path variables.
398
                // 1. A path variable can only be substituted if it follows a ';'!
399
                // 2. It's located exactly at the start of the string being substituted!
400
0
                if (( aFixedVarTable[ int( nIndex ) ].bAbsPath && (( nPosition == 0 ) || (( nPosition > 0 ) && ( aWorkText[nPosition-1] == ';')))) ||
401
0
                    ( !aFixedVarTable[ int( nIndex ) ].bAbsPath ))
402
0
                {
403
0
                    aReplacement = m_aPreDefVars.m_FixedVar[ nIndex ];
404
0
                    nReplaceLength = nLength;
405
0
                }
406
0
            }
407
408
            // Have we found something to replace?
409
0
            if ( nReplaceLength > 0 )
410
0
            {
411
                // Yes ... then do it.
412
0
                aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement );
413
0
            }
414
0
            else
415
0
            {
416
                // Variable not known
417
0
                bVarNotSubstituted = true;
418
0
                nPosition += nLength;
419
0
            }
420
421
            // Step after replaced text! If no text was replaced (unknown variable!),
422
            // length of aReplacement is 0 ... and we don't step then.
423
0
            nPosition += aReplacement.getLength();
424
425
            // We must control index in string before call something at OUString!
426
            // The OUString-implementation don't do it for us :-( but the result is not defined otherwise.
427
0
            if ( nPosition + 1 > aWorkText.getLength() )
428
0
            {
429
                // Position is out of range. Break loop!
430
0
                nPosition = -1;
431
0
                nLength = 0;
432
0
            }
433
0
            else
434
0
            {
435
                // Else; Position is valid. Search for next variable to replace.
436
0
                nPosition = aWorkText.indexOf( "$(", nPosition );
437
                // Have we found any variable like "$(...)"?
438
0
                if ( nPosition != -1 )
439
0
                {
440
                    // Yes; Get length of found variable. If no ")" was found - nLength must set to 0!
441
0
                    nLength = 0;
442
0
                    sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
443
0
                    if ( nEndPosition != -1 )
444
0
                        nLength = nEndPosition - nPosition + 1;
445
0
                }
446
0
            }
447
0
        }
448
449
0
        nPosition = aWorkText.indexOf( "$(" );
450
0
        if ( nPosition == -1 )
451
0
        {
452
0
            bSubstitutionCompleted = true;
453
0
            break; // All variables are substituted
454
0
        }
455
0
        else
456
0
        {
457
            // Check for recursion
458
0
            const sal_uInt32 nCount = aEndlessRecursiveDetector.size();
459
0
            for ( sal_uInt32 i=0; i < nCount; i++ )
460
0
            {
461
0
                if ( aEndlessRecursiveDetector[i] == aWorkText )
462
0
                {
463
0
                    if ( bVarNotSubstituted )
464
0
                        break; // Not all variables could be substituted!
465
0
                    else
466
0
                    {
467
0
                        nDepth = nMaxRecursiveDepth;
468
0
                        break; // Recursion detected!
469
0
                    }
470
0
                }
471
0
            }
472
473
0
            aEndlessRecursiveDetector.push_back( aWorkText );
474
475
            // Initialize values for next
476
0
            sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
477
0
            if ( nEndPosition != -1 )
478
0
                nLength = nEndPosition - nPosition + 1;
479
0
            bVarNotSubstituted = false;
480
0
            ++nDepth;
481
0
        }
482
0
    }
483
484
    // Fill return value with result
485
0
    if ( bSubstitutionCompleted )
486
0
    {
487
        // Substitution successful!
488
0
        aResult = aWorkText;
489
0
    }
490
0
    else
491
0
    {
492
        // Substitution not successful!
493
0
        if ( nDepth == nMaxRecursiveDepth )
494
0
        {
495
            // recursion depth reached!
496
0
            if ( bSubstRequired )
497
0
            {
498
0
                throw NoSuchElementException( u"Endless recursion detected. Cannot substitute variables!"_ustr, static_cast<cppu::OWeakObject *>(this) );
499
0
            }
500
0
            aResult = rText;
501
0
        }
502
0
        else
503
0
        {
504
            // variable in text but unknown!
505
0
            if ( bSubstRequired )
506
0
            {
507
0
                throw NoSuchElementException( u"Unknown variable found!"_ustr, static_cast<cppu::OWeakObject *>(this) );
508
0
            }
509
0
            aResult = aWorkText;
510
0
        }
511
0
    }
512
513
0
    return aResult;
514
0
}
515
516
OUString SubstitutePathVariables::impl_reSubstituteVariables( const OUString& rURL )
517
0
{
518
0
    OUString aURL;
519
520
0
    INetURLObject aUrl( rURL );
521
0
    if ( !aUrl.HasError() )
522
0
        aURL = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE );
523
0
    else
524
0
    {
525
        // Convert a system path to a UCB compliant URL before resubstitution
526
0
        OUString aTemp;
527
0
        if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None )
528
0
        {
529
0
            aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
530
0
            if( aURL.isEmpty() )
531
0
                return rURL;
532
0
        }
533
0
        else
534
0
        {
535
            // rURL is not a valid URL nor a osl system path. Give up and return error!
536
0
            return rURL;
537
0
        }
538
0
    }
539
540
    // Get transient predefined path variable $(work) value before starting resubstitution
541
0
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
542
543
0
    for (;;)
544
0
    {
545
0
        bool bVariableFound = false;
546
547
0
        for (auto const & i: m_aReSubstFixedVarOrder)
548
0
        {
549
0
            OUString aValue = m_aPreDefVars.m_FixedVar[i.eVariable];
550
0
            sal_Int32 nPos = aURL.indexOf( aValue );
551
0
            if ( nPos >= 0 )
552
0
            {
553
0
                bool bMatch = true;
554
0
                if ( !aFixedVarTable[i.eVariable].bAbsPath )
555
0
                {
556
                    // Special path variables as they can occur in the middle of a path. Only match if they
557
                    // describe a whole directory and not only a substring of a directory!
558
                    // (Ideally, all substitutions should stick to syntactical
559
                    // boundaries within the given URL, like not covering only
560
                    // part of a URL path segment; however, at least when saving
561
                    // an Impress document, one URL that is passed in is of the
562
                    // form <file:///.../share/palette%3Bfile:///.../user/
563
                    // config/standard.sob>, re-substituted to
564
                    // <$(inst)/share/palette%3B$(user)/config/standard.sob>.)
565
0
                    const sal_Unicode* pStr = aURL.getStr();
566
567
0
                    if ( nPos > 0 )
568
0
                        bMatch = ( aURL[ nPos-1 ] == '/' );
569
570
0
                    if ( bMatch )
571
0
                    {
572
0
                        if ( nPos + aValue.getLength() < aURL.getLength() )
573
0
                            bMatch = ( pStr[ nPos + aValue.getLength() ] == '/' );
574
0
                    }
575
0
                }
576
577
0
                if ( bMatch )
578
0
                {
579
0
                    aURL = aURL.replaceAt(
580
0
                        nPos, aValue.getLength(),
581
0
                        m_aPreDefVars.m_FixedVarNames[i.eVariable]);
582
0
                    bVariableFound = true; // Resubstitution not finished yet!
583
0
                    break;
584
0
                }
585
0
            }
586
0
        }
587
588
0
        if ( !bVariableFound )
589
0
        {
590
0
            return aURL;
591
0
        }
592
0
    }
593
0
}
594
595
// This method support both request schemes "$("<varname>")" or "<varname>".
596
OUString const & SubstitutePathVariables::impl_getSubstituteVariableValue( const OUString& rVariable )
597
0
{
598
0
    OUString aVariable;
599
600
0
    sal_Int32 nPos = rVariable.indexOf( "$(" );
601
0
    if ( nPos == -1 )
602
0
    {
603
        // Prepare variable name before hash map access
604
0
        aVariable = "$(" + rVariable + ")";
605
0
    }
606
607
0
    VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( ( nPos == -1 ) ? aVariable : rVariable );
608
609
    // Fixed/Predefined variable
610
0
    if ( pNTOIIter == m_aPreDefVarMap.end() )
611
0
    {
612
0
        throw NoSuchElementException(u"Unknown variable!"_ustr, static_cast<cppu::OWeakObject *>(this));
613
0
    }
614
0
    PreDefVariable nIndex = pNTOIIter->second;
615
0
    return m_aPreDefVars.m_FixedVar[static_cast<sal_Int32>(nIndex)];
616
0
}
617
618
void SubstitutePathVariables::SetPredefinedPathVariables()
619
18.5k
{
620
18.5k
    m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] = "$BRAND_BASE_DIR";
621
18.5k
    rtl::Bootstrap::expandMacros(
622
18.5k
        m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL]);
623
624
    // Get inspath and userpath from bootstrap mechanism in every case as file URL
625
18.5k
    ::utl::Bootstrap::PathStatus aState;
626
18.5k
    OUString              sVal;
627
628
18.5k
    aState = utl::Bootstrap::locateUserData( sVal );
629
    //There can be the valid case that there is no user installation.
630
    //TODO: Is that still the case? (With OOo 3.4, "unopkg sync" was run as part
631
    // of the setup. Then no user installation was required.)
632
    //Therefore we do not assert here.
633
    // It's not possible to detect when an empty value would actually be used.
634
    // (note: getenv is a hack to detect if we're running in a unit test)
635
    // Also, it's okay to have an empty user installation path in case of LOK
636
18.5k
    if (aState == ::utl::Bootstrap::PATH_EXISTS || getenv("SRC_ROOT") ||
637
18.5k
        (comphelper::LibreOfficeKit::isActive() && aState == ::utl::Bootstrap::PATH_VALID))
638
0
    {
639
0
        m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ] = sVal;
640
0
    }
641
642
    // Set $(inst), $(instpath), $(insturl)
643
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ] = m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL];
644
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTURL ]    = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
645
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_INST ]       = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
646
    // New variable of hierarchy service (#i32656#)
647
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
648
649
    // Set $(user), $(userpath), $(userurl)
650
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERURL ]    = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
651
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_USER ]       = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
652
    // New variable of hierarchy service (#i32656#)
653
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERDATAURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
654
655
    // Detect the program directory
656
    // Set $(prog), $(progpath), $(progurl)
657
18.5k
    INetURLObject aProgObj(
658
18.5k
        m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] );
659
18.5k
    if ( !aProgObj.HasError() && aProgObj.insertName( u"" LIBO_BIN_FOLDER ) )
660
18.5k
    {
661
18.5k
        m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ] = aProgObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
662
18.5k
        m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGURL ]  = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ];
663
18.5k
        m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROG ]     = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ];
664
18.5k
    }
665
666
    // Set $(username)
667
18.5k
    OUString aSystemUser;
668
18.5k
    ::osl::Security aSecurity;
669
18.5k
    aSecurity.getUserName( aSystemUser, false );
670
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERNAME ]   = aSystemUser;
671
672
    // Detect the language type of the current office
673
18.5k
    m_aPreDefVars.m_eLanguageType = LANGUAGE_ENGLISH_US;
674
18.5k
    OUString aLocaleStr( utl::ConfigManager::getUILocale() );
675
18.5k
    m_aPreDefVars.m_eLanguageType = LanguageTag::convertToLanguageTypeWithFallback( aLocaleStr );
676
    // We used to have an else branch here with a SAL_WARN, but that
677
    // always fired in some unit tests when this code was built with
678
    // debug=t, so it seems fairly pointless, especially as
679
    // m_aPreDefVars.m_eLanguageType has been initialized to a
680
    // default value above anyway.
681
682
    // Set $(vlang)
683
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr;
684
685
    // Set $(langid)
686
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_LANGID ] = OUString::number( static_cast<sal_uInt16>(m_aPreDefVars.m_eLanguageType) );
687
688
    // Set the other pre defined path variables
689
    // Set $(work)
690
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
691
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue();
692
693
    // Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense
694
    // anymore because the path settings service has this value! It can deliver this value more
695
    // quickly than the substitution service!
696
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath();
697
698
    // Set $(path) variable
699
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue();
700
701
    // Set $(temp)
702
18.5k
    OUString aTmp;
703
18.5k
    osl::FileBase::getTempDirURL( aTmp );
704
18.5k
    m_aPreDefVars.m_FixedVar[ PREDEFVAR_TEMP ] = aTmp;
705
18.5k
}
706
707
}
708
709
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
710
com_sun_star_comp_framework_PathSubstitution_get_implementation(
711
    css::uno::XComponentContext *,
712
    css::uno::Sequence<css::uno::Any> const &)
713
18.5k
{
714
18.5k
    return cppu::acquire(new SubstitutePathVariables());
715
18.5k
}
716
717
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */