Add-ons: new Addon methods to access the storage area beneath $FG_HOME/Export
New methods Addon::createStorageDir() and Addon::getStoragePath() with corresponding Nasal bindings in the addons.Addon ghost: createStorageDir() method (returns the dir, doesn't fail if it already exists) storagePath read-only attribute to get the dir The directory reserved for each add-on is $FG_HOME/Export/Addons/ADDON_ID, but please use the above methods (or the corresponding C++ ones) to avoid hardcoding such paths in your code. Also create directory $FG_HOME/Export/Addons in fgInitConfig() as a way of reserving the namespace, in order to prevent future failures in case someone would have the strange idea to create it as a file...
This commit is contained in:
parent
10caef48b1
commit
9a044a474b
5 changed files with 80 additions and 1 deletions
|
@ -24,6 +24,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <simgear/misc/sg_dir.hxx>
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
|
#include <Main/util.hxx>
|
||||||
#include <Scripting/NasalSys.hxx>
|
#include <Scripting/NasalSys.hxx>
|
||||||
|
|
||||||
#include "addon_fwd.hxx"
|
#include "addon_fwd.hxx"
|
||||||
|
@ -93,6 +95,7 @@ Addon::Addon(std::string id, AddonVersion version, SGPath basePath,
|
||||||
_version(
|
_version(
|
||||||
shared_ptr_traits<AddonVersionRef>::makeStrongRef(std::move(version))),
|
shared_ptr_traits<AddonVersionRef>::makeStrongRef(std::move(version))),
|
||||||
_basePath(std::move(basePath)),
|
_basePath(std::move(basePath)),
|
||||||
|
_storagePath(globals->get_fg_home() / ("Export/Addons/" + _id)),
|
||||||
_minFGVersionRequired(std::move(minFGVersionRequired)),
|
_minFGVersionRequired(std::move(minFGVersionRequired)),
|
||||||
_maxFGVersionRequired(std::move(maxFGVersionRequired)),
|
_maxFGVersionRequired(std::move(maxFGVersionRequired)),
|
||||||
_addonNode(addonNode)
|
_addonNode(addonNode)
|
||||||
|
@ -187,6 +190,48 @@ SGPath Addon::getBasePath() const
|
||||||
void Addon::setBasePath(const SGPath& addonBasePath)
|
void Addon::setBasePath(const SGPath& addonBasePath)
|
||||||
{ _basePath = addonBasePath; }
|
{ _basePath = addonBasePath; }
|
||||||
|
|
||||||
|
SGPath Addon::getStoragePath() const
|
||||||
|
{ return _storagePath; }
|
||||||
|
|
||||||
|
SGPath Addon::createStorageDir() const
|
||||||
|
{
|
||||||
|
if (_storagePath.exists()) {
|
||||||
|
if (!_storagePath.isDir()) {
|
||||||
|
string msg =
|
||||||
|
"Unable to create add-on storage directory because the entry already "
|
||||||
|
"exists, but is not a directory: '" + _storagePath.utf8Str() + "'";
|
||||||
|
// Log + throw, because if called from Nasal, only throwing would cause
|
||||||
|
// the exception message to 1) be truncated and 2) only appear in the
|
||||||
|
// log, not stopping the sim. Then users would have to figure out why
|
||||||
|
// their add-on doesn't work...
|
||||||
|
SG_LOG(SG_GENERAL, SG_POPUP, msg);
|
||||||
|
throw errors::unable_to_create_addon_storage_dir(msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SGPath authorizedPath = fgValidatePath(_storagePath, true /* write */);
|
||||||
|
|
||||||
|
if (authorizedPath.isNull()) {
|
||||||
|
string msg =
|
||||||
|
"Unable to create add-on storage directory because of the FlightGear "
|
||||||
|
"security policy (refused by fgValidatePath()): '" +
|
||||||
|
_storagePath.utf8Str() + "'";
|
||||||
|
SG_LOG(SG_GENERAL, SG_POPUP, msg);
|
||||||
|
throw errors::unable_to_create_addon_storage_dir(msg);
|
||||||
|
} else {
|
||||||
|
simgear::Dir(authorizedPath).create(0777);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sensitive operation (creating the directory) is behind us; return
|
||||||
|
// _storagePath instead of authorizedPath for consistency with the
|
||||||
|
// getStoragePath() method (_storagePath and authorizedPath could be
|
||||||
|
// different in case the former contains symlink components). Further
|
||||||
|
// sensitive operations beneath _storagePath must use fgValidatePath() again
|
||||||
|
// every time, of course (otherwise attackers could use symlinks in
|
||||||
|
// _storagePath to bypass the security policy).
|
||||||
|
return _storagePath;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Addon::resourcePath(const std::string& relativePath) const
|
std::string Addon::resourcePath(const std::string& relativePath) const
|
||||||
{
|
{
|
||||||
if (strutils::starts_with(relativePath, "/")) {
|
if (strutils::starts_with(relativePath, "/")) {
|
||||||
|
@ -448,6 +493,8 @@ void Addon::setupGhost(nasal::Hash& addonsModule)
|
||||||
.member("licenseUrl", &Addon::getLicenseUrl)
|
.member("licenseUrl", &Addon::getLicenseUrl)
|
||||||
.member("tags", &Addon::getTags)
|
.member("tags", &Addon::getTags)
|
||||||
.member("basePath", &Addon::getBasePath)
|
.member("basePath", &Addon::getBasePath)
|
||||||
|
.member("storagePath", &Addon::getStoragePath)
|
||||||
|
.method("createStorageDir", &Addon::createStorageDir)
|
||||||
.method("resourcePath", &Addon::resourcePath)
|
.method("resourcePath", &Addon::resourcePath)
|
||||||
.member("minFGVersionRequired", &Addon::getMinFGVersionRequired)
|
.member("minFGVersionRequired", &Addon::getMinFGVersionRequired)
|
||||||
.member("maxFGVersionRequired", &Addon::getMaxFGVersionRequired)
|
.member("maxFGVersionRequired", &Addon::getMaxFGVersionRequired)
|
||||||
|
|
|
@ -127,6 +127,13 @@ public:
|
||||||
SGPath getBasePath() const;
|
SGPath getBasePath() const;
|
||||||
void setBasePath(const SGPath& addonBasePath);
|
void setBasePath(const SGPath& addonBasePath);
|
||||||
|
|
||||||
|
// Return $FG_HOME/Export/Addons/ADDON_ID as an SGPath instance.
|
||||||
|
SGPath getStoragePath() const;
|
||||||
|
// Create directory $FG_HOME/Export/Addons/ADDON_ID, including any parent,
|
||||||
|
// if it doesn't already exist. Throw an exception in case of problems.
|
||||||
|
// Return an SGPath instance for the directory (same as getStoragePath()).
|
||||||
|
SGPath createStorageDir() const;
|
||||||
|
|
||||||
// Return a resource path suitable for use with the simgear::ResourceManager.
|
// Return a resource path suitable for use with the simgear::ResourceManager.
|
||||||
// 'relativePath' is relative to the add-on base path, and should not start
|
// 'relativePath' is relative to the add-on base path, and should not start
|
||||||
// with a '/'.
|
// with a '/'.
|
||||||
|
@ -218,6 +225,8 @@ private:
|
||||||
|
|
||||||
std::vector<std::string> _tags;
|
std::vector<std::string> _tags;
|
||||||
SGPath _basePath;
|
SGPath _basePath;
|
||||||
|
// $FG_HOME/Export/Addons/ADDON_ID
|
||||||
|
const SGPath _storagePath;
|
||||||
|
|
||||||
// To be used with simgear::strutils::compare_versions()
|
// To be used with simgear::strutils::compare_versions()
|
||||||
std::string _minFGVersionRequired;
|
std::string _minFGVersionRequired;
|
||||||
|
|
|
@ -61,6 +61,7 @@ class duplicate_registration_attempt;
|
||||||
class fg_version_too_old;
|
class fg_version_too_old;
|
||||||
class fg_version_too_recent;
|
class fg_version_too_recent;
|
||||||
class invalid_resource_path;
|
class invalid_resource_path;
|
||||||
|
class unable_to_create_addon_storage_dir;
|
||||||
|
|
||||||
} // of namespace errors
|
} // of namespace errors
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,9 @@ class fg_version_too_recent : public error
|
||||||
class invalid_resource_path : public error
|
class invalid_resource_path : public error
|
||||||
{ using error::error; };
|
{ using error::error; };
|
||||||
|
|
||||||
|
class unable_to_create_addon_storage_dir : public error
|
||||||
|
{ using error::error; };
|
||||||
|
|
||||||
} // of namespace errors
|
} // of namespace errors
|
||||||
|
|
||||||
} // of namespace addons
|
} // of namespace addons
|
||||||
|
|
|
@ -488,6 +488,21 @@ bool fgInitHome()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void createBaseStorageDirForAddons(const SGPath& exportDir)
|
||||||
|
{
|
||||||
|
SGPath addonStorageBasePath = exportDir / "Addons";
|
||||||
|
if (addonStorageBasePath.exists()) {
|
||||||
|
if (!addonStorageBasePath.isDir()) {
|
||||||
|
throw sg_error(
|
||||||
|
"Unable to create add-on storage base directory, because the entry "
|
||||||
|
"already exists but is not a directory: '" +
|
||||||
|
addonStorageBasePath.utf8Str() + "'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
simgear::Dir(addonStorageBasePath).create(0777); // respect user's umask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read in configuration (file and command line)
|
// Read in configuration (file and command line)
|
||||||
int fgInitConfig ( int argc, char **argv, bool reinit )
|
int fgInitConfig ( int argc, char **argv, bool reinit )
|
||||||
{
|
{
|
||||||
|
@ -497,7 +512,11 @@ int fgInitConfig ( int argc, char **argv, bool reinit )
|
||||||
if (!exportDir.exists()) {
|
if (!exportDir.exists()) {
|
||||||
exportDir.create(0755);
|
exportDir.create(0755);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reserve a directory where add-ons can write. There will be a subdir for
|
||||||
|
// each add-on, see Addon::createStorageDir() and Addon::getStoragePath().
|
||||||
|
createBaseStorageDirForAddons(exportDir.path());
|
||||||
|
|
||||||
// Set /sim/fg-home. Use FG_HOME if necessary.
|
// Set /sim/fg-home. Use FG_HOME if necessary.
|
||||||
// deliberately not a tied property, for fgValidatePath security
|
// deliberately not a tied property, for fgValidatePath security
|
||||||
// write-protect to avoid accidents
|
// write-protect to avoid accidents
|
||||||
|
|
Loading…
Add table
Reference in a new issue