1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <config_folders.h>
26 #include <unotools/bootstrap.hxx>
28 #include <rtl/ustring.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <sal/log.hxx>
31 #include <osl/file.hxx>
32 #include <osl/diagnose.h>
34 #include <rtl/bootstrap.hxx>
35 #include <osl/process.h>
37 // #define this to true, if remembering defaults is not supported properly
38 #define RTL_BOOTSTRAP_DEFAULTS_BROKEN true
40 constexpr OUString BOOTSTRAP_ITEM_PRODUCT_KEY
= u
"ProductKey"_ustr
;
41 constexpr OUStringLiteral BOOTSTRAP_ITEM_VERSIONFILE
= u
"Location";
42 constexpr OUStringLiteral BOOTSTRAP_ITEM_BUILDID
= u
"buildid";
44 constexpr OUStringLiteral BOOTSTRAP_ITEM_BASEINSTALLATION
= u
"BRAND_BASE_DIR";
45 constexpr OUStringLiteral BOOTSTRAP_ITEM_USERINSTALLATION
= u
"UserInstallation";
47 constexpr OUString BOOTSTRAP_ITEM_USERDIR
= u
"UserDataDir"_ustr
;
49 constexpr OUStringLiteral BOOTSTRAP_DEFAULT_BASEINSTALL
= u
"$SYSBINDIR/..";
51 constexpr OUString BOOTSTRAP_DIRNAME_USERDIR
= u
"user"_ustr
;
53 typedef char const * AsciiString
;
58 // Implementation class: Bootstrap::Impl
60 static OUString
makeImplName()
63 rtl::Bootstrap::get( u
"BRAND_BASE_DIR"_ustr
, uri
);
64 return uri
+ "/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("bootstrap");
69 const OUString m_aImplName
;
70 public: // struct to cache the result of a path lookup
77 : status(DATA_UNKNOWN
)
80 public: // data members
82 PathData aBaseInstall_
;
85 PathData aUserInstall_
;
88 PathData aBootstrapINI_
;
89 PathData aVersionINI_
;
94 public: // construction and initialization
95 Impl() : m_aImplName(makeImplName())
103 OUString
getBootstrapValue(OUString
const& _sName
, OUString
const& _sDefault
) const;
105 const OUString
& getImplName() const { return m_aImplName
; }
107 private: // implementation
108 bool initBaseInstallationData(rtl::Bootstrap
const & _rData
);
109 bool initUserInstallationData(rtl::Bootstrap
const & _rData
);
114 Bootstrap::Impl
& theImpl()
116 static Bootstrap::Impl SINGLETON
;
121 const Bootstrap::Impl
& Bootstrap::data()
126 bool Bootstrap::getProcessWorkingDir(OUString
&rUrl
)
129 OUString
s(u
"$OOO_CWD"_ustr
);
130 rtl::Bootstrap::expandMacros(s
);
133 if (osl_getProcessWorkingDir(&rUrl
.pData
) == osl_Process_E_None
)
136 else if (s
[0] == '1')
141 else if (s
[0] == '2' &&
142 (osl::FileBase::getFileURLFromSystemPath(s
.copy(1), rUrl
) ==
143 osl::FileBase::E_None
))
150 void Bootstrap::reloadData()
152 theImpl().initialize();
157 typedef Bootstrap::PathStatus PathStatus
;
159 sal_Unicode
const cURLSeparator
= '/';
161 // path status utility function
162 static PathStatus
implCheckStatusOfURL(OUString
const& _sURL
, osl::DirectoryItem
& aDirItem
)
166 PathStatus eStatus
= Bootstrap::DATA_UNKNOWN
;
168 if (!_sURL
.isEmpty())
170 switch( DirectoryItem::get(_sURL
, aDirItem
) )
172 case DirectoryItem::E_None
: // Success
173 eStatus
= Bootstrap::PATH_EXISTS
;
176 case DirectoryItem::E_NOENT
: // No such file or directory
177 eStatus
= Bootstrap::PATH_VALID
;
180 case DirectoryItem::E_INVAL
: // the format of the parameters was not valid
181 case DirectoryItem::E_NAMETOOLONG
: // File name too long
182 case DirectoryItem::E_NOTDIR
: // A component of the path prefix of path is not a directory
183 eStatus
= Bootstrap::DATA_INVALID
;
186 // how to handle these ?
187 case DirectoryItem::E_LOOP
: // Too many symbolic links encountered
188 case DirectoryItem::E_ACCES
: // permission denied
189 // any other error - what to do ?
191 eStatus
= Bootstrap::DATA_UNKNOWN
;
197 eStatus
= Bootstrap::DATA_MISSING
;
203 static bool implNormalizeURL(OUString
& _sURL
, osl::DirectoryItem
& aDirItem
)
207 OSL_PRECOND(aDirItem
.is(), "Opened DirItem required");
209 static const sal_uInt32 cosl_FileStatus_Mask
= osl_FileStatus_Mask_FileURL
;
211 FileStatus
aFileStatus(cosl_FileStatus_Mask
);
213 if (aDirItem
.getFileStatus(aFileStatus
) != DirectoryItem::E_None
)
216 OUString aNormalizedURL
= aFileStatus
.getFileURL();
218 if (aNormalizedURL
.isEmpty())
221 // #109863# sal/osl returns final slash for file URLs contradicting
223 if ( !aNormalizedURL
.endsWith(OUStringChar(cURLSeparator
)) )
224 _sURL
= aNormalizedURL
;
226 _sURL
= aNormalizedURL
.copy( 0, aNormalizedURL
.getLength()-1 );
231 static bool implEnsureAbsolute(OUString
& _rsURL
) // also strips embedded dots !!
236 OSL_VERIFY(Bootstrap::getProcessWorkingDir(sBasePath
));
239 if ( File::E_None
== File::getAbsoluteFileURL(sBasePath
, _rsURL
, sAbsolute
))
246 OSL_FAIL("Could not get absolute file URL for URL");
251 static bool implMakeAbsoluteURL(OUString
& _rsPathOrURL
)
258 // check if it already was normalized
259 if ( File::E_None
== File::getSystemPathFromFileURL(_rsPathOrURL
, sOther
) )
264 else if ( File::E_None
== File::getFileURLFromSystemPath(_rsPathOrURL
, sOther
) )
266 _rsPathOrURL
= sOther
;
272 return bURL
&& implEnsureAbsolute(_rsPathOrURL
);
275 static PathStatus
dbgCheckStatusOfURL(OUString
const& _sURL
)
279 DirectoryItem aDirItem
;
281 return implCheckStatusOfURL(_sURL
,aDirItem
);
284 static PathStatus
checkStatusAndNormalizeURL(OUString
& _sURL
)
288 PathStatus eStatus
= Bootstrap::DATA_UNKNOWN
;
291 eStatus
= Bootstrap::DATA_MISSING
;
293 else if ( !implMakeAbsoluteURL(_sURL
) )
294 eStatus
= Bootstrap::DATA_INVALID
;
298 DirectoryItem aDirItem
;
300 eStatus
= implCheckStatusOfURL(_sURL
,aDirItem
);
302 if (eStatus
== Bootstrap::PATH_EXISTS
&& !implNormalizeURL(_sURL
,aDirItem
))
303 OSL_FAIL("Unexpected failure getting actual URL for existing object");
308 // helpers to build and check a nested URL
309 static PathStatus
getDerivedPath(
311 OUString
const& _aBaseURL
, PathStatus _aBaseStatus
,
312 std::u16string_view _sRelativeURL
,
313 rtl::Bootstrap
const & _rData
, OUString
const& _sBootstrapParameter
316 OUString sDerivedURL
;
317 OSL_PRECOND(!_rData
.getFrom(_sBootstrapParameter
,sDerivedURL
),"Setting for derived path is already defined");
318 OSL_PRECOND(!_sRelativeURL
.empty() && _sRelativeURL
[0] != cURLSeparator
,"Invalid Relative URL");
320 PathStatus aStatus
= _aBaseStatus
;
322 // do we have a base path ?
323 if (!_aBaseURL
.isEmpty())
325 OSL_PRECOND(!_aBaseURL
.endsWith(OUStringChar(cURLSeparator
)), "Unexpected: base URL ends in slash");
327 sDerivedURL
= _aBaseURL
+ OUStringChar(cURLSeparator
) + _sRelativeURL
;
329 // a derived (nested) URL can only exist or have a lesser status, if the parent exists
330 if (aStatus
== Bootstrap::PATH_EXISTS
)
331 aStatus
= checkStatusAndNormalizeURL(sDerivedURL
);
333 else // the relative appendix must be valid
334 OSL_ASSERT(aStatus
!= Bootstrap::PATH_VALID
|| dbgCheckStatusOfURL(sDerivedURL
) == Bootstrap::PATH_VALID
);
336 _rData
.getFrom(_sBootstrapParameter
, _rURL
, sDerivedURL
);
338 OSL_ENSURE(sDerivedURL
== _rURL
,"Could not set derived URL via Bootstrap default parameter");
339 SAL_WARN_IF( !(RTL_BOOTSTRAP_DEFAULTS_BROKEN
|| (_rData
.getFrom(_sBootstrapParameter
,sDerivedURL
) && sDerivedURL
==_rURL
)),
341 "Use of default did not affect bootstrap value");
348 // if we have no data it can't be a valid path
349 OSL_ASSERT( aStatus
> Bootstrap::PATH_VALID
);
355 static PathStatus
getDerivedPath(
357 Bootstrap::Impl::PathData
const& _aBaseData
,
358 std::u16string_view _sRelativeURL
,
359 rtl::Bootstrap
const & _rData
, OUString
const& _sBootstrapParameter
362 return getDerivedPath(_rURL
,_aBaseData
.path
,_aBaseData
.status
,_sRelativeURL
,_rData
,_sBootstrapParameter
);
365 static OUString
getExecutableBaseName()
367 OUString sExecutable
;
369 if (osl_Process_E_None
== osl_getExecutableFile(&sExecutable
.pData
))
371 // split the executable name
372 sal_Int32 nSepIndex
= sExecutable
.lastIndexOf(cURLSeparator
);
374 sExecutable
= sExecutable
.copy(nSepIndex
+ 1);
376 // ... and get the basename (strip the extension)
377 sal_Unicode
const cExtensionSep
= '.';
379 sal_Int32
const nExtIndex
= sExecutable
.lastIndexOf(cExtensionSep
);
380 sal_Int32
const nExtLength
= sExecutable
.getLength() - nExtIndex
- 1;
381 if (0 < nExtIndex
&& nExtLength
< 4)
382 sExecutable
= sExecutable
.copy(0,nExtIndex
);
385 SAL_WARN("unotools.config", "Cannot get executable name: osl_getExecutableFile failed");
390 static Bootstrap::PathStatus
updateStatus(Bootstrap::Impl::PathData
& _rResult
)
392 _rResult
.status
= checkStatusAndNormalizeURL(_rResult
.path
);
393 return _rResult
.status
;
396 static Bootstrap::PathStatus
implGetBootstrapFile(rtl::Bootstrap
const & _rData
, Bootstrap::Impl::PathData
& _rBootstrapFile
)
398 _rData
.getIniName(_rBootstrapFile
.path
);
400 return updateStatus(_rBootstrapFile
);
403 static Bootstrap::PathStatus
implGetVersionFile(rtl::Bootstrap
const & _rData
, Bootstrap::Impl::PathData
& _rVersionFile
)
405 _rData
.getFrom(BOOTSTRAP_ITEM_VERSIONFILE
, _rVersionFile
.path
);
407 return updateStatus(_rVersionFile
);
412 char const IS_MISSING
[] = "is missing";
413 char const IS_INVALID
[] = "is corrupt";
414 char const PERIOD
[] = ". ";
416 static void addFileError(OUStringBuffer
& _rBuf
, std::u16string_view _aPath
, AsciiString _sWhat
)
418 const std::string_view::size_type nLastSep
= _aPath
.rfind(cURLSeparator
);
419 const auto nAfterSep
= (nLastSep
!= std::string_view::npos
) ? (nLastSep
+ 1) : 0;
420 std::u16string_view sSimpleFileName
= _aPath
.substr(nAfterSep
);
422 _rBuf
.append("The configuration file");
423 _rBuf
.append(OUString::Concat(" '") + sSimpleFileName
+ "' ");
424 _rBuf
.appendAscii(_sWhat
).append(PERIOD
);
427 static void addMissingDirectoryError(OUStringBuffer
& _rBuf
, std::u16string_view _aPath
)
429 _rBuf
.append(OUString::Concat("The configuration directory '") + _aPath
+ "' " +
430 IS_MISSING
+ PERIOD
);
433 static void addUnexpectedError(OUStringBuffer
& _rBuf
, AsciiString _sExtraInfo
= nullptr)
435 if (nullptr == _sExtraInfo
)
436 _sExtraInfo
= "An internal failure occurred";
438 _rBuf
.appendAscii(_sExtraInfo
).append(PERIOD
);
441 static Bootstrap::FailureCode
describeError(OUStringBuffer
& _rBuf
, Bootstrap::Impl
const& _rData
)
443 Bootstrap::FailureCode eErrCode
= Bootstrap::INVALID_BOOTSTRAP_DATA
;
445 _rBuf
.append("The program cannot be started. ");
447 switch (_rData
.aUserInstall_
.status
)
449 case Bootstrap::PATH_EXISTS
:
450 switch (_rData
.aBaseInstall_
.status
)
452 case Bootstrap::PATH_VALID
:
453 addMissingDirectoryError(_rBuf
, _rData
.aBaseInstall_
.path
);
454 eErrCode
= Bootstrap::MISSING_INSTALL_DIRECTORY
;
457 case Bootstrap::DATA_INVALID
:
458 addUnexpectedError(_rBuf
,"The installation path is invalid");
461 case Bootstrap::DATA_MISSING
:
462 addUnexpectedError(_rBuf
,"The installation path is not available");
465 case Bootstrap::PATH_EXISTS
: // seems to be all fine (?)
466 addUnexpectedError(_rBuf
,"");
469 default: OSL_ASSERT(false);
470 addUnexpectedError(_rBuf
);
475 case Bootstrap::PATH_VALID
:
476 addMissingDirectoryError(_rBuf
, _rData
.aUserInstall_
.path
);
477 eErrCode
= Bootstrap::MISSING_USER_DIRECTORY
;
481 case Bootstrap::DATA_INVALID
:
482 if (_rData
.aVersionINI_
.status
== Bootstrap::PATH_EXISTS
)
484 addFileError(_rBuf
, _rData
.aVersionINI_
.path
, IS_INVALID
);
485 eErrCode
= Bootstrap::INVALID_VERSION_FILE_ENTRY
;
490 case Bootstrap::DATA_MISSING
:
491 switch (_rData
.aVersionINI_
.status
)
493 case Bootstrap::PATH_EXISTS
:
494 addFileError(_rBuf
, _rData
.aVersionINI_
.path
, "does not support the current version");
495 eErrCode
= Bootstrap::MISSING_VERSION_FILE_ENTRY
;
498 case Bootstrap::PATH_VALID
:
499 addFileError(_rBuf
, _rData
.aVersionINI_
.path
, IS_MISSING
);
500 eErrCode
= Bootstrap::MISSING_VERSION_FILE
;
504 switch (_rData
.aBootstrapINI_
.status
)
506 case Bootstrap::PATH_EXISTS
:
507 addFileError(_rBuf
, _rData
.aBootstrapINI_
.path
, IS_INVALID
);
509 if (_rData
.aVersionINI_
.status
== Bootstrap::DATA_MISSING
)
510 eErrCode
= Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY
;
512 eErrCode
= Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY
;
515 case Bootstrap::DATA_INVALID
: OSL_ASSERT(false); [[fallthrough
]];
516 case Bootstrap::PATH_VALID
:
517 addFileError(_rBuf
, _rData
.aBootstrapINI_
.path
, IS_MISSING
);
518 eErrCode
= Bootstrap::MISSING_BOOTSTRAP_FILE
;
522 addUnexpectedError(_rBuf
);
529 default: OSL_ASSERT(false);
530 addUnexpectedError(_rBuf
);
538 OUString
Bootstrap::getProductKey()
540 OUString
const sDefaultProductKey
= getExecutableBaseName();
542 return data().getBootstrapValue( BOOTSTRAP_ITEM_PRODUCT_KEY
, sDefaultProductKey
);
545 OUString
Bootstrap::getProductKey(OUString
const& _sDefault
)
547 return data().getBootstrapValue( BOOTSTRAP_ITEM_PRODUCT_KEY
, _sDefault
);
550 OUString
Bootstrap::getBuildIdData(OUString
const& _sDefault
)
552 // try to open version.ini (versionrc)
554 rtl::Bootstrap::get( u
"BRAND_BASE_DIR"_ustr
, uri
);
555 rtl::Bootstrap
aData( uri
+ "/" LIBO_ETC_FOLDER
"/" SAL_CONFIGFILE("version") );
556 if ( aData
.getHandle() == nullptr )
557 // version.ini (versionrc) doesn't exist
562 aData
.getFrom(BOOTSTRAP_ITEM_BUILDID
,sBuildId
,_sDefault
);
566 Bootstrap::PathStatus
Bootstrap::locateBaseInstallation(OUString
& _rURL
)
568 Impl::PathData
const& aPathData
= data().aBaseInstall_
;
570 _rURL
= aPathData
.path
;
571 return aPathData
.status
;
574 Bootstrap::PathStatus
Bootstrap::locateUserInstallation(OUString
& _rURL
)
576 Impl::PathData
const& aPathData
= data().aUserInstall_
;
578 _rURL
= aPathData
.path
;
579 return aPathData
.status
;
582 Bootstrap::PathStatus
Bootstrap::locateUserData(OUString
& _rURL
)
584 OUString
const csUserDirItem(BOOTSTRAP_ITEM_USERDIR
);
586 rtl::Bootstrap
aData( data().getImplName() );
588 if ( aData
.getFrom(csUserDirItem
, _rURL
) )
590 return checkStatusAndNormalizeURL(_rURL
);
594 return getDerivedPath(_rURL
, data().aUserInstall_
,BOOTSTRAP_DIRNAME_USERDIR
, aData
, csUserDirItem
);
598 Bootstrap::PathStatus
Bootstrap::locateBootstrapFile(OUString
& _rURL
)
600 Impl::PathData
const& aPathData
= data().aBootstrapINI_
;
602 _rURL
= aPathData
.path
;
603 return aPathData
.status
;
606 Bootstrap::PathStatus
Bootstrap::locateVersionFile(OUString
& _rURL
)
608 Impl::PathData
const& aPathData
= data().aVersionINI_
;
610 _rURL
= aPathData
.path
;
611 return aPathData
.status
;
614 Bootstrap::Status
Bootstrap::checkBootstrapStatus(OUString
& _rDiagnosticMessage
, FailureCode
& _rErrCode
)
616 Impl
const& aData
= data();
618 Status result
= aData
.status_
;
620 // maybe do further checks here
622 OUStringBuffer sErrorBuffer
;
623 if (result
!= DATA_OK
)
624 _rErrCode
= describeError(sErrorBuffer
,aData
);
627 _rErrCode
= NO_FAILURE
;
629 _rDiagnosticMessage
= sErrorBuffer
.makeStringAndClear();
635 bool Bootstrap::Impl::initBaseInstallationData(rtl::Bootstrap
const & _rData
)
637 _rData
.getFrom(BOOTSTRAP_ITEM_BASEINSTALLATION
, aBaseInstall_
.path
, BOOTSTRAP_DEFAULT_BASEINSTALL
);
639 bool bResult
= (PATH_EXISTS
== updateStatus(aBaseInstall_
));
641 implGetBootstrapFile(_rData
, aBootstrapINI_
);
646 bool Bootstrap::Impl::initUserInstallationData(rtl::Bootstrap
const & _rData
)
648 if (_rData
.getFrom(BOOTSTRAP_ITEM_USERINSTALLATION
, aUserInstall_
.path
))
650 updateStatus(aUserInstall_
);
654 // should we do just this
655 aUserInstall_
.status
= DATA_MISSING
;
657 // ... or this - look for a single-user user directory ?
658 OUString
const csUserDirItem(BOOTSTRAP_ITEM_USERDIR
);
660 // look for $BASEINSTALLATION/user only if default UserDir setting is used
661 if (! _rData
.getFrom(csUserDirItem
, sDummy
))
663 if ( PATH_EXISTS
== getDerivedPath(sDummy
, aBaseInstall_
, BOOTSTRAP_DIRNAME_USERDIR
, _rData
, csUserDirItem
) )
664 aUserInstall_
= aBaseInstall_
;
668 bool bResult
= (PATH_EXISTS
== aUserInstall_
.status
);
670 implGetVersionFile(_rData
, aVersionINI_
);
675 void Bootstrap::Impl::initialize()
677 rtl::Bootstrap
aData( m_aImplName
);
679 if (!initBaseInstallationData(aData
))
681 status_
= INVALID_BASE_INSTALL
;
683 else if (!initUserInstallationData(aData
))
685 status_
= INVALID_USER_INSTALL
;
687 if (aUserInstall_
.status
>= DATA_MISSING
)
689 switch (aVersionINI_
.status
)
693 status_
= MISSING_USER_INSTALL
;
698 status_
= INVALID_BASE_INSTALL
;
711 OUString
Bootstrap::Impl::getBootstrapValue(OUString
const& _sName
, OUString
const& _sDefault
) const
713 rtl::Bootstrap
aData( m_aImplName
);
716 aData
.getFrom(_sName
,sResult
,_sDefault
);
722 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */