diff options
Diffstat (limited to 'indra/newview/llsyntaxid.cpp')
-rw-r--r-- | indra/newview/llsyntaxid.cpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/indra/newview/llsyntaxid.cpp b/indra/newview/llsyntaxid.cpp new file mode 100644 index 0000000000..a763d42a8d --- /dev/null +++ b/indra/newview/llsyntaxid.cpp @@ -0,0 +1,329 @@ +/** + * @file LLSyntaxId + * @brief Handles downloading, saving, and checking of LSL keyword/syntax files + * for each region. + * @author Ima Mechanique, Cinder Roxley + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llsyntaxid.h" +#include "llagent.h" +#include "llappviewer.h" +#include "llhttpclient.h" +#include "llsdserialize.h" +#include "llviewerregion.h" + +//----------------------------------------------------------------------------- +// fetchKeywordsFileResponder +//----------------------------------------------------------------------------- +class fetchKeywordsFileResponder : public LLHTTPClient::Responder +{ +public: + fetchKeywordsFileResponder(const std::string& filespec) + : mFileSpec(filespec) + { + LL_DEBUGS("SyntaxLSL") << "Instantiating with file saving to: '" << filespec << "'" << LL_ENDL; + } + + /* virtual */ void httpFailure() + { + LL_WARNS("SyntaxLSL") << "failed to fetch syntax file [status:" << getStatus() << "]: " << getContent() << LL_ENDL; + } + + /* virtual */ void httpSuccess() + { + // Continue only if a valid LLSD object was returned. + const LLSD& content = getContent(); + if (content.isMap()) + { + if (LLSyntaxIdLSL::getInstance()->isSupportedVersion(content)) + { + LLSyntaxIdLSL::getInstance()->setKeywordsXml(content); + + cacheFile(content); + LLSyntaxIdLSL::getInstance()->handleFileFetched(mFileSpec); + } + else + { + LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; + } + } + else + { + LL_WARNS("SyntaxLSL") << "Syntax file '" << mFileSpec << "' contains invalid LLSD." << LL_ENDL; + } + } + + void cacheFile(const LLSD& content_ref) + { + std::stringstream str; + LLSDSerialize::toXML(content_ref, str); + const std::string xml = str.str(); + + // save the str to disk, usually to the cache. + llofstream file(mFileSpec, std::ios_base::out); + file.write(xml.c_str(), str.str().size()); + file.close(); + + LL_DEBUGS("SyntaxLSL") << "Syntax file received, saving as: '" << mFileSpec << "'" << LL_ENDL; + } + +private: + std::string mFileSpec; +}; + +//----------------------------------------------------------------------------- +// LLSyntaxIdLSL +//----------------------------------------------------------------------------- +const std::string SYNTAX_ID_CAPABILITY_NAME = "LSLSyntax"; +const std::string SYNTAX_ID_SIMULATOR_FEATURE = "LSLSyntaxId"; +const std::string FILENAME_DEFAULT = "keywords_lsl_default.xml"; + +/** + * @brief LLSyntaxIdLSL constructor + */ +LLSyntaxIdLSL::LLSyntaxIdLSL() +: mKeywordsXml(LLSD()) +, mCapabilityURL(std::string()) +, mFilePath(LL_PATH_APP_SETTINGS) +, mSyntaxId(LLUUID()) +{ + loadDefaultKeywordsIntoLLSD(); + mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLSyntaxIdLSL::handleRegionChanged, this)); + handleRegionChanged(); // Kick off an initial caps query and fetch +} + +void LLSyntaxIdLSL::buildFullFileSpec() +{ + ELLPath path = mSyntaxId.isNull() ? LL_PATH_APP_SETTINGS : LL_PATH_CACHE; + const std::string filename = mSyntaxId.isNull() ? FILENAME_DEFAULT : "keywords_lsl_" + mSyntaxId.asString() + ".llsd.xml"; + mFullFileSpec = gDirUtilp->getExpandedFilename(path, filename); +} + +//----------------------------------------------------------------------------- +// syntaxIdChange() +//----------------------------------------------------------------------------- +bool LLSyntaxIdLSL::syntaxIdChanged() +{ + LLViewerRegion* region = gAgent.getRegion(); + + if (region) + { + if (region->capabilitiesReceived()) + { + LLSD sim_features; + region->getSimulatorFeatures(sim_features); + + if (sim_features.has(SYNTAX_ID_SIMULATOR_FEATURE)) + { + // get and check the hash + LLUUID new_syntax_id = sim_features[SYNTAX_ID_SIMULATOR_FEATURE].asUUID(); + mCapabilityURL = region->getCapability(SYNTAX_ID_CAPABILITY_NAME); + LL_DEBUGS("SyntaxLSL") << SYNTAX_ID_SIMULATOR_FEATURE << " capability URL: " << mCapabilityURL << LL_ENDL; + if (new_syntax_id != mSyntaxId) + { + LL_DEBUGS("SyntaxLSL") << "New SyntaxID '" << new_syntax_id << "' found." << LL_ENDL; + mSyntaxId = new_syntax_id; + return true; + } + else + LL_DEBUGS("SyntaxLSL") << "SyntaxID matches what we have." << LL_ENDL; + } + } + else + { + region->setCapabilitiesReceivedCallback(boost::bind(&LLSyntaxIdLSL::handleCapsReceived, this, _1)); + LL_DEBUGS("SyntaxLSL") << "Region has not received capabilities. Waiting for caps..." << LL_ENDL; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// fetchKeywordsFile +//----------------------------------------------------------------------------- +void LLSyntaxIdLSL::fetchKeywordsFile(const std::string& filespec) +{ + mInflightFetches.push_back(filespec); + LLHTTPClient::get(mCapabilityURL, + new fetchKeywordsFileResponder(filespec), + LLSD(), 30.f); + LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is: " << mCapabilityURL << ". Filename to use is: '" << filespec << "'." << LL_ENDL; +} + + +//----------------------------------------------------------------------------- +// initialize +//----------------------------------------------------------------------------- +void LLSyntaxIdLSL::initialize() +{ + if (mSyntaxId.isNull()) + { + loadDefaultKeywordsIntoLLSD(); + } + else if (!mCapabilityURL.empty()) + { + LL_DEBUGS("SyntaxLSL") << "LSL version has changed, getting appropriate file." << LL_ENDL; + + // Need a full spec regardless of file source, so build it now. + buildFullFileSpec(); + if (mSyntaxId.notNull()) + { + if (!gDirUtilp->fileExists(mFullFileSpec)) + { // Does not exist, so fetch it from the capability + LL_DEBUGS("SyntaxLSL") << "LSL syntax not cached, attempting download." << LL_ENDL; + fetchKeywordsFile(mFullFileSpec); + } + else + { + LL_DEBUGS("SyntaxLSL") << "Found cached Syntax file: " << mFullFileSpec << " Loading keywords." << LL_ENDL; + loadKeywordsIntoLLSD(); + } + } + else + { + LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId is null. Loading default values" << LL_ENDL; + loadDefaultKeywordsIntoLLSD(); + } + } + else + { + LL_DEBUGS("SyntaxLSL") << "LSLSyntaxId capability URL is empty." << LL_ENDL; + loadDefaultKeywordsIntoLLSD(); + } +} + +//----------------------------------------------------------------------------- +// isSupportedVersion +//----------------------------------------------------------------------------- +const U32 LLSD_SYNTAX_LSL_VERSION_EXPECTED = 2; +const std::string LLSD_SYNTAX_LSL_VERSION_KEY("llsd-lsl-syntax-version"); + +bool LLSyntaxIdLSL::isSupportedVersion(const LLSD& content) +{ + bool is_valid = false; + /* + * If the schema used to store LSL keywords and hints changes, this value is incremented + * Note that it should _not_ be changed if the keywords and hints _content_ changes. + */ + + if (content.has(LLSD_SYNTAX_LSL_VERSION_KEY)) + { + LL_DEBUGS("SyntaxLSL") << "LSL syntax version: " << content[LLSD_SYNTAX_LSL_VERSION_KEY].asString() << LL_ENDL; + + if (content[LLSD_SYNTAX_LSL_VERSION_KEY].asInteger() == LLSD_SYNTAX_LSL_VERSION_EXPECTED) + { + is_valid = true; + } + } + else + { + LL_DEBUGS("SyntaxLSL") << "Missing LSL syntax version key." << LL_ENDL; + } + + return is_valid; +} + +//----------------------------------------------------------------------------- +// loadDefaultKeywordsIntoLLSD() +//----------------------------------------------------------------------------- +void LLSyntaxIdLSL::loadDefaultKeywordsIntoLLSD() +{ + mSyntaxId.setNull(); + buildFullFileSpec(); + loadKeywordsIntoLLSD(); +} + +//----------------------------------------------------------------------------- +// loadKeywordsFileIntoLLSD +//----------------------------------------------------------------------------- +/** + * @brief Load xml serialised LLSD + * @desc Opens the specified filespec and attempts to deserialise the + * contained data to the specified LLSD object. indicate success/failure with + * sLoaded/sLoadFailed members. + */ +void LLSyntaxIdLSL::loadKeywordsIntoLLSD() +{ + LLSD content; + llifstream file; + file.open(mFullFileSpec); + if (file.is_open()) + { + if (LLSDSerialize::fromXML(content, file) != LLSDParser::PARSE_FAILURE) + { + if (isSupportedVersion(content)) + { + LL_DEBUGS("SyntaxLSL") << "Deserialised: " << mFullFileSpec << LL_ENDL; + } + else + { + LL_WARNS("SyntaxLSL") << "Unknown or unsupported version of syntax file." << LL_ENDL; + } + } + } + else + { + LL_WARNS("SyntaxLSL") << "Failed to open: " << mFullFileSpec << LL_ENDL; + } + mKeywordsXml = content; + mSyntaxIDChangedSignal(); +} + +bool LLSyntaxIdLSL::keywordFetchInProgress() +{ + return !mInflightFetches.empty(); +} + +void LLSyntaxIdLSL::handleRegionChanged() +{ + if (syntaxIdChanged()) + { + buildFullFileSpec(); + fetchKeywordsFile(mFullFileSpec); + } +} + +void LLSyntaxIdLSL::handleCapsReceived(const LLUUID& region_uuid) +{ + LLViewerRegion* current_region = gAgent.getRegion(); + + if (region_uuid.notNull() + && current_region->getRegionID() == region_uuid) + { + syntaxIdChanged(); + } +} + +void LLSyntaxIdLSL::handleFileFetched(const std::string& filepath) +{ + mInflightFetches.remove(filepath); + loadKeywordsIntoLLSD(); +} + +boost::signals2::connection LLSyntaxIdLSL::addSyntaxIDCallback(const syntax_id_changed_signal_t::slot_type& cb) +{ + return mSyntaxIDChangedSignal.connect(cb); +} |