/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ #include "stdafx.h" #include "V8DataModel/GlobalSettings.h" #include "FastLog.h" #include "V8Xml/XmlSerializer.h" #include "V8Xml/Serializer.h" #include "Util/FileSystem.h" #include "V8DataModel/Selection.h" #include "util/standardout.h" #include #include "StringConv.h" FASTFLAGVARIABLE(DisableGlobalSettingsParentChange, true) const char* const RBX::sGlobalAdvancedSettings = "GlobalSettings"; const char* const RBX::sGlobalBasicSettings = "UserSettings"; const char* const RBX::sSettings = "GenericSettings"; const char* const RBX::sSettingsItem = NULL; using namespace RBX; REFLECTION_BEGIN(); static Reflection::BoundFuncDesc fun_getFastVariable(&GlobalAdvancedSettings::getFVariable, "GetFVariable", "name", Security::None); static Reflection::BoundFuncDesc()> fun_getFastVariables(&GlobalAdvancedSettings::getFVariables, "GetFVariables", Security::RobloxScript); static Reflection::BoundFuncDesc func_getFFlag(&GlobalAdvancedSettings::getFFlag, "GetFFlag", "name", Security::None); REFLECTION_END(); Settings::Settings(const std::string& settingsFile) :settingsFile(settingsFile) ,settingsErased(false) { } Settings::InvalidDescendentDetector::InvalidDescendentDetector() : anyInvalid(false) {} Settings::InvalidDescendentCollector::InvalidDescendentCollector() {} bool Settings::InvalidDescendentDetector::invalid(const Instance* instance) { return (dynamic_cast(instance) == 0 && dynamic_cast(instance) == 0 && dynamic_cast(instance) == 0); } void Settings::InvalidDescendentDetector::operator()(shared_ptr descendant) { anyInvalid |= invalid(descendant.get()); } void Settings::InvalidDescendentCollector::operator()(shared_ptr descendant) { if (InvalidDescendentDetector::invalid(descendant.get())) invalidInstances.push_back(descendant); } void Settings::eraseSettingsStore() { settingsErased = true; #ifdef _WIN32 ::DeleteFile(settingsFile.c_str()); #else std::remove(settingsFile.c_str()); #endif } void Settings::loadState(const std::string& optGlobalSettingsFile) { std::string filePath = (optGlobalSettingsFile.empty()) ? settingsFile : optGlobalSettingsFile; if (filePath.size()==0) return; try { std::ifstream stream(utf8_decode(filePath).c_str(), std::ios_base::in | std::ios_base::binary); if (stream) { TextXmlParser machine(stream.rdbuf()); std::auto_ptr root(machine.parse()); MergeBinder binder; readChildren(root.get(), binder, EngineCreator); binder.resolveRefs(); } } catch (RBX::base_exception& e) { StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Exception thrown in loadState %s", e.what()); } } void Settings::saveState() { InvalidDescendentDetector invalidDetector; visitDescendants(invalidDetector); if(!settingsErased && !invalidDetector.anyInvalid){ std::string filePath = settingsFile; if (filePath.size()==0) return; boost::scoped_ptr root(Serializer::newRootElement()); writeChildren(root.get(), EngineCreator); std::ofstream stream(filePath.c_str(), std::ios_base::out | std::ios_base::binary); TextXmlWriter machine(stream); machine.serialize(root.get()); } } void Settings::removeInvalidChildren() { InvalidDescendentCollector invalidCollector; visitDescendants(invalidCollector); for (unsigned int i = 0; i < invalidCollector.invalidInstances.size(); i++) invalidCollector.invalidInstances[i]->setLockedParent(NULL); } void Settings::verifyAddDescendant(const Instance* newParent, const Instance* instanceGettingNewParent) const { bool invalid = InvalidDescendentDetector::invalid(instanceGettingNewParent); if (!invalid) { InvalidDescendentDetector invalidDetector; instanceGettingNewParent->visitDescendants(invalidDetector); invalid = invalidDetector.anyInvalid; } if (invalid) { throw RBX::runtime_error("Not allowed to add that under settings"); } Super::verifyAddDescendant(newParent, instanceGettingNewParent); } static std::string globalAdvancedSettingsFile() { boost::filesystem::path file = RBX::FileSystem::getUserDirectory(true, RBX::DirAppData); if (!file.string().empty()) file /= "GlobalSettings_13.xml"; return file.string(); } GlobalAdvancedSettings* g_sing = 0; static shared_ptr doAdvancedSingleton() { static shared_ptr sing = Creatable::create(); g_sing = sing.get(); return sing; } void initAdvancedSingleton() { doAdvancedSingleton(); } shared_ptr GlobalAdvancedSettings::singleton() { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&initAdvancedSingleton, flag); return doAdvancedSingleton(); } GlobalAdvancedSettings* GlobalAdvancedSettings::raw_singleton() { RBXASSERT(g_sing); return g_sing; } GlobalAdvancedSettings::GlobalAdvancedSettings() :Super(globalAdvancedSettingsFile()) { setName("Global Settings"); // During the unit tests (but only on Macs) the construction order (and consequently the // destruction order) needs to be done in the correct order. Without the following line // the selection will be created after the static shared_ptr // meaning the Selection gets destroyed first resulting in the destruction of // GlobalAdvancedSettings raising SIGABRT as the Selection has already been destroyed // but the GlobalAdvancedSettings destruction accesses it. create(); } GlobalAdvancedSettings::~GlobalAdvancedSettings() { } /*override*/ void GlobalAdvancedSettings::verifySetParent(const Instance* instance) const { if (FFlag::DisableGlobalSettingsParentChange && (RBX::Security::Context::current().identity != RBX::Security::Anonymous)) { try { RBX::Security::Context::current().requirePermission(RBX::Security::RobloxScript, "set settings parent"); } catch (RBX::base_exception& e) { RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Insufficient permissions to set settings parent"); throw e; } } Super::verifySetParent(instance); } static std::string globalBasicSettingsFile() { boost::filesystem::path file = RBX::FileSystem::getUserDirectory(true, RBX::DirAppData); if (!file.string().empty()) file /= "GlobalBasicSettings_13.xml"; return file.string(); } static shared_ptr doBasicSingleton() { static shared_ptr sing = Creatable::create(); return sing; } void initBasicSingleton() { doBasicSingleton(); } shared_ptr GlobalBasicSettings::singleton() { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&initBasicSingleton, flag); return doBasicSingleton(); } REFLECTION_BEGIN(); static Reflection::BoundFuncDesc func_reset(&GlobalBasicSettings::reset, "Reset", Security::None); static Reflection::BoundFuncDesc func_isUserFeatureEnabled(&GlobalBasicSettings::isUserFeatureEnabled, "IsUserFeatureEnabled", "name", Security::None); REFLECTION_END(); GlobalBasicSettings::GlobalBasicSettings() :Super(globalBasicSettingsFile()) { setName("GlobalBasicSettings"); } static void resetChild(shared_ptr instance) { if(GlobalBasicSettings::Item* item = Instance::fastDynamicCast(instance.get())){ item->reset(); } } void GlobalBasicSettings::reset() { visitChildren(&resetChild); } bool RBX::GlobalBasicSettings::isUserFeatureEnabled( std::string name ) { std::string user("User"); if (name.compare(0, user.length(), user) != 0) { throw std::runtime_error(RBX::format("Flag %s does not exist", name.c_str())); } std::string result; if (FLog::GetValue(name.c_str(), result, true)) { return boost::iequals(result, "true"); } throw std::runtime_error(RBX::format("Flag %s does not exist", name.c_str())); } /*override*/ void GlobalBasicSettings::verifySetParent(const Instance* instance) const { if (RBX::Security::Context::current().identity != RBX::Security::Anonymous) { try { RBX::Security::Context::current().requirePermission(RBX::Security::RobloxScript, "set UserSettings parent"); } catch (RBX::base_exception& e) { RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Insufficient permissions to set UserSettings parent"); throw e; } } Super::verifySetParent(instance); } static void visit(const std::string& name, const std::string& value, void* context) { RBX::Reflection::ValueTable* table = reinterpret_cast(context); (*table)[name] = value; } shared_ptr RBX::GlobalAdvancedSettings::getFVariables() { shared_ptr values(rbx::make_shared()); FLog::ForEachVariable(&visit, values.get(), FASTVARTYPE_ANY); return values; } bool RBX::GlobalAdvancedSettings::getFFlag( std::string name ) { std::string result; if (FLog::GetValue(name.c_str(), result, true)) { return boost::iequals(result, "true"); } throw std::runtime_error(RBX::format("Flag %s does not exist", name.c_str())); } std::string RBX::GlobalAdvancedSettings::getFVariable( std::string name ) { std::string result; if (FLog::GetValue(name.c_str(), result)) return result; throw std::runtime_error(RBX::format("Flag %s does not exist", name.c_str())); }