/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: */ |