/** * @file fsdata.cpp * @brief Downloadable dymatic xml data for viewer features. * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ * Phoenix Firestorm Viewer Source Code * Copyright (C) 2011-2013 Techwolf Lupindo * Portions Copyright (C) * 2011 Wolfspirit Magic * 2012 Ansariel Hiller @ Second Life * * 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 * * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA * http://www.firestormviewer.org * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "fsdata.h" #include "fscommon.h" #include "fscorehttputil.h" #include "fsassetblacklist.h" /* boost: will not compile unless equivalent is undef'd, beware. */ #include "fix_macros.h" #include #include "llappviewer.h" #include "llagent.h" #include "llagentui.h" #include "llfloaterabout.h" #include "llimview.h" #include "llmutelist.h" #include "llnotifications.h" #include "llsdserialize.h" #include "lltrans.h" #include "llversioninfo.h" #include "llviewercontrol.h" #include "llviewermedia.h" #include "llviewernetwork.h" #include "llxorcipher.h" #include "llfilesystem.h" #include "message.h" // [RLVa:KB] #include "rlvactions.h" #include "rlvhelper.h" // [/RLVa:KB] const std::string LEGACY_CLIENT_LIST_URL = "http://phoenixviewer.com/app/client_tags/client_list_v2.xml"; const LLUUID MAGIC_ID("3c115e51-04f4-523c-9fa6-98aff1034730"); FSData::FSData() : mLegacySearch(true), mFSDataDone(false), mAgentsDone(false) { mHeaders.insert("User-Agent", LLViewerMedia::getInstance()->getCurrentUserAgent()); mHeaders.insert("viewer-version", LLVersionInfo::getInstance()->getChannelAndVersionFS()); mBaseURL = gSavedSettings.getBOOL("FSdataQAtest") ? "http://phoenixviewer.com/app/fsdatatest" : "http://phoenixviewer.com/app/fsdata"; mFSDataURL = mBaseURL + "/" + "data.xml"; } FSData::~FSData() { for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) { if (it->second.connected()) { it->second.disconnect(); } } mAvatarNameCacheConnections.clear(); } void FSData::processResponder(const LLSD& content, const std::string& url, bool save_to_file, const LLDate& last_modified) { if (url == mFSDataURL) { if (!save_to_file) { LLSD data; LL_DEBUGS("fsdata") << "Loading fsdata.xml from " << mFSdataFilename << LL_ENDL; if (loadFromFile(data, mFSdataFilename)) { processData(data); } else { LL_WARNS("fsdata") << "Unable to download or load fsdata.xml" << LL_ENDL; } } else { processData(content); saveLLSD(content, mFSdataFilename, last_modified); } mFSDataDone = true; } else if (url == mAssetsURL) { if (!save_to_file) { LLSD data; LL_DEBUGS("fsdata") << "Loading assets.xml from " << mAssetsFilename << LL_ENDL; if (loadFromFile(data, mAssetsFilename)) { processAssets(data); } else { LL_WARNS("fsdata") << "Unable to download or load assets.xml" << LL_ENDL; } } else { processAssets(content); saveLLSD(content, mAssetsFilename, last_modified); } } else if (url == mAgentsURL) { if (!save_to_file) { LLSD data; LL_DEBUGS("fsdata") << "Loading agents.xml from " << mAgentsFilename << LL_ENDL; if (loadFromFile(data, mAgentsFilename)) { processAgents(data); } else { LL_WARNS("fsdata") << "Unable to download or load agents.xml" << LL_ENDL; } } else { processAgents(content); saveLLSD(content, mAgentsFilename, last_modified); } mAgentsDone = true; addAgents(); } else if (url == LEGACY_CLIENT_LIST_URL) { if (!save_to_file) { updateClientTagsLocal(); } else { processClientTags(content); saveLLSD(content, mClientTagsFilename, last_modified); } } else if (url == mFSdataDefaultsUrl) { if (!save_to_file) { // do nothing as this file is loaded during app startup. } else { saveLLSD(content, mFSdataDefaultsFilename, last_modified); } } } bool FSData::loadFromFile(LLSD& data, std::string filename) { llifstream file(filename.c_str()); if (file.is_open()) { if (LLSDSerialize::fromXML(data, file) != LLSDParser::PARSE_FAILURE) { file.close(); return true; } else { // error reading file file.close(); return false; } } else { // file does not exist or other error return false; } } void downloadComplete(LLSD const &aData, std::string const &aURL, bool success) { LL_DEBUGS("fsdata") << aURL << ": " << aData << " - success = " << success << LL_ENDL; LLDate lastModified; LLSD data; if (success) { LLSD header = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS][LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; if (header.has("last-modified")) { lastModified.secondsSinceEpoch(FSCommon::secondsSinceEpochFromString("%a, %d %b %Y %H:%M:%S %ZP", header["last-modified"].asString())); } const LLSD::Binary& binary = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); std::string content; content.assign(binary.begin(), binary.end()); std::istringstream raw(content); LLPointer parser = new LLSDXMLParser(); if (parser->parse(raw, data, content.size()) == LLSDParser::PARSE_FAILURE) { LL_WARNS("fsdata") << "Error parsing data received from " << aURL << ":" << LL_NEWLINE << content << LL_ENDL; } LL_DEBUGS("fsdata") << "data: " << data << LL_ENDL; } FSData::getInstance()->processResponder(data, aURL, success, lastModified); } #ifdef OPENSIM static void downloadCompleteScript(LLSD const &aData, std::string const &aURL, std::string const &aFilename) { LL_DEBUGS("fsdata") << aURL << ": " << aData << LL_ENDL; LLSD header = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS][LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; LLDate lastModified; if (header.has("last-modified")) { lastModified.secondsSinceEpoch(FSCommon::secondsSinceEpochFromString("%a, %d %b %Y %H:%M:%S %ZP", header["last-modified"].asString())); } const LLSD::Binary &rawData = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); if (rawData.size() <= 0) { LL_WARNS("fsdata") << "Received zero data for " << aURL << LL_ENDL; return; } // basic check for valid data received LLXMLNodePtr xml_root; std::string stringData; stringData.assign( rawData.begin(), rawData.end() ); // LLXMLNode::parseBuffer wants a U8*, not a const U8*, so need to copy here just to be safe if ( (!LLXMLNode::parseBuffer( reinterpret_cast ( &stringData[0] ), (U64)stringData.size(), xml_root, NULL)) || (xml_root.isNull()) || (!xml_root->hasName("script_library")) ) { LL_WARNS("fsdata") << "Could not read the script library data from "<< aURL << LL_ENDL; return; } LLAPRFile outfile ; outfile.open(aFilename, LL_APR_WB); if (!outfile.getFileHandle()) { LL_WARNS("fsdata") << "Unable to open file for writing: " << aFilename << LL_ENDL; } else { LL_INFOS("fsdata") << "Saving " << aFilename << LL_ENDL; outfile.write( &rawData[0], (S32)rawData.size() ); outfile.close() ; } } static void downloadError(LLSD const &aData, std::string const &aURL) { LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(aData); if (status.getType() == HTTP_NOT_MODIFIED) { LL_INFOS("fsdata") << "Didn't download " << aURL << " - no newer version available" << LL_ENDL; } else { LL_WARNS("fsdata") << "Failed to download " << aURL << ": " << aData << LL_ENDL; } } #endif // call this just before the login screen and after the LLProxy has been setup. void FSData::startDownload() { mFSdataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "fsdata.xml"); mFSdataDefaultsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str())); mClientTagsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "client_list_v2.xml"); { // Stat the file to see if it exists and when it was last modified. time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(mFSdataFilename, &stat_data)) { last_modified = stat_data.st_mtime; } LL_INFOS("fsdata") << "Downloading data.xml from " << mFSDataURL << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw(mFSDataURL, boost::bind(downloadComplete, _1, mFSDataURL, true), boost::bind(downloadComplete, _1, mFSDataURL, false), LLCore::HttpHeaders::ptr_t(), httpOpts); } { time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(mFSdataDefaultsFilename, &stat_data)) { last_modified = stat_data.st_mtime; } std::string filename = llformat("defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str()); mFSdataDefaultsUrl = mBaseURL + "/" + filename; LL_INFOS("fsdata") << "Downloading defaults.xml from " << mFSdataDefaultsUrl << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw(mFSdataDefaultsUrl, boost::bind(downloadComplete, _1, mFSdataDefaultsUrl, true), boost::bind(downloadComplete, _1, mFSdataDefaultsUrl, false), LLCore::HttpHeaders::ptr_t(), httpOpts); } #ifdef OPENSIM std::string filenames[] = { "scriptlibrary_ossl.xml", "scriptlibrary_aa.xml" }; for (auto const& script_name : filenames) { std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, script_name); time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(filename, &stat_data)) { last_modified = stat_data.st_mtime; } std::string url = mBaseURL + "/" + script_name; LL_INFOS("fsdata") << "Downloading " << script_name << " from " << url << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw( url, boost::bind( downloadCompleteScript, _1, url, filename ), boost::bind( downloadError, _1, url ), LLCore::HttpHeaders::ptr_t(), httpOpts); } #endif } // call this _after_ the login screen to pick up grid data. void FSData::downloadAgents() { #ifdef OPENSIM std::string filename_prefix = LLGridManager::getInstance()->getGridId(); #else std::string filename_prefix = "second_life"; #endif #ifdef OPENSIM if (!LLGridManager::getInstance()->isInSecondLife()) { // TODO: Let the opensim devs and opensim group figure out the best way // to add "agents.xml" URL to the gridinfo protocol. //getAgentsURL(); // there is no need for assets.xml URL for opensim grids as the grid owner can just delete // the bad asset itself. } else #endif { mAgentsURL = mBaseURL + "/" + "agents.xml"; mAssetsURL = mBaseURL + "/" + "assets.xml"; } if (!mAgentsURL.empty()) { mAgentsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_prefix + "_agents.xml"); time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(mAgentsFilename, &stat_data)) { last_modified = stat_data.st_mtime; } LL_INFOS("fsdata") << "Downloading agents.xml from " << mAgentsURL << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw(mAgentsURL, boost::bind(downloadComplete, _1, mAgentsURL, true), boost::bind(downloadComplete, _1, mAgentsURL, false), LLCore::HttpHeaders::ptr_t(), httpOpts); } if (!mAssetsURL.empty()) { mAssetsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_prefix + "_assets.xml"); time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(mAssetsFilename, &stat_data)) { last_modified = stat_data.st_mtime; } LL_INFOS("fsdata") << "Downloading assets.xml from " << mAssetsURL << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw(mAssetsURL, boost::bind(downloadComplete, _1, mAssetsURL, true), boost::bind(downloadComplete, _1, mAssetsURL, false), LLCore::HttpHeaders::ptr_t(), httpOpts); } } void FSData::processData(const LLSD& fs_data) { // Set Message Of The Day if present if (fs_data.has("MOTD") && !fs_data["MOTD"].asString().empty()) { mSecondLifeMOTD = fs_data["MOTD"].asString(); gAgent.mMOTD.assign(mSecondLifeMOTD); } else if (fs_data.has("RandomMOTD") && fs_data["RandomMOTD"].isArray() && fs_data["RandomMOTD"].size() > 0) // only used if MOTD is not present or empty in the xml file. { mRandomMOTDs = fs_data["RandomMOTD"]; LLSD::array_const_iterator iter = mRandomMOTDs.beginArray(); gAgent.mMOTD.assign((iter + (ll_rand((S32)mRandomMOTDs.size())))->asString()); } // If the event falls withen the current date, use that for MOTD instead. if (fs_data.has("EventsMOTD")) { const LLSD& events = fs_data["EventsMOTD"]; for(LLSD::map_const_iterator iter = events.beginMap(); iter != events.endMap(); ++iter) { std::string name = iter->first; const LLSD& content = iter->second; LL_DEBUGS("fsdata") << "Found event MOTD: " << name << LL_ENDL; if (content["startDate"].asDate() < LLDate::now() && content["endDate"].asDate() > LLDate::now()) { LL_INFOS("fsdata") << "Setting MOTD to " << name << LL_ENDL; gAgent.mMOTD.assign(content["EventMOTD"]); // note singler instead of plural above break; // Only use the first one found. } } } if (fs_data.has("OpensimMOTD")) { mOpenSimMOTD.assign(fs_data["OpensimMOTD"]); } if (fs_data.has("BlockedReleases")) { const LLSD& blocked = fs_data["BlockedReleases"]; for (LLSD::map_const_iterator iter = blocked.beginMap(); iter != blocked.endMap(); ++iter) { std::string version = iter->first; const LLSD& content = iter->second; mBlockedVersions[version] = content; LL_DEBUGS("fsdata") << "Added " << version << " to mBlockedVersions" << LL_ENDL; } } processAgents(fs_data); processAssets(fs_data); // FSUseLegacyClienttags: 0=Off, 1=Local Clienttags, 2=Download Clienttags static LLCachedControl use_legacy_tags(gSavedSettings, "FSUseLegacyClienttags"); if (use_legacy_tags > 1) { time_t last_modified = 0; llstat stat_data; if (!LLFile::stat(mClientTagsFilename, &stat_data)) { last_modified = stat_data.st_mtime; } LL_INFOS("fsdata") << "Downloading client_list_v2.xml from " << LEGACY_CLIENT_LIST_URL << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setWantHeaders(true); httpOpts->setLastModified((long)last_modified); FSCoreHttpUtil::callbackHttpGetRaw(LEGACY_CLIENT_LIST_URL, boost::bind(downloadComplete, _1, LEGACY_CLIENT_LIST_URL, true), boost::bind(downloadComplete, _1, LEGACY_CLIENT_LIST_URL, false), LLCore::HttpHeaders::ptr_t(), httpOpts); } else if (use_legacy_tags) { updateClientTagsLocal(); } // [RLVa:KB] if ((RlvActions::isRlvEnabled()) && (fs_data.has("rlva_compat_list"))) { RlvSettings::initCompatibilityMode(fs_data["rlva_compat_list"].asString()); } // [/RLVa:KB] } void FSData::processAssets(const LLSD& assets) { if (!assets.has("assets")) { return; } const LLSD& asset = assets["assets"]; for (LLSD::map_const_iterator itr = asset.beginMap(); itr != asset.endMap(); ++itr) { LLUUID uid = LLUUID(itr->first); LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); cipher.decrypt(uid.mData, UUID_BYTES); LLSD data = itr->second; data["asset_permanent"] = false; // Don't save these locally! if (uid.isNull()) { continue; } FSAssetBlacklist::instance().addNewItemToBlacklistData(uid, data, false); LL_DEBUGS("fsdata") << "Added " << uid << " to assets list." << LL_ENDL; } } void FSData::processAgents(const LLSD& data) { if (data.has("Agents")) { const LLSD& agents = data["Agents"]; for (LLSD::map_const_iterator iter = agents.beginMap(); iter != agents.endMap(); ++iter) { LLUUID key = LLUUID(iter->first); mTeamAgents[key] = iter->second.asInteger(); LL_DEBUGS("fsdata") << "Added " << key << " with " << mTeamAgents[key] << " flag mask to mSupportAgentList" << LL_ENDL; } } else if (data.has("SupportAgents")) // Legacy format { const LLSD& support_agents = data["SupportAgents"]; std::string newFormat; for (LLSD::map_const_iterator iter = support_agents.beginMap(); iter != support_agents.endMap(); ++iter) { LLUUID key = LLUUID(iter->first); mTeamAgents[key] = 0; const LLSD& content = iter->second; if(content.has("support")) { mTeamAgents[key] |= SUPPORT; } if(content.has("developer")) { mTeamAgents[key] |= DEVELOPER; } LL_DEBUGS("fsdata") << "Added Legacy " << key << " with " << mTeamAgents[key] << " flag mask to mSupportAgentList" << LL_ENDL; std::string text = llformat("%s\n %d\n", key.asString().c_str(), content["name"].asString().c_str(), mTeamAgents[key]); newFormat.append(text); } LL_DEBUGS("fsdata") << "New format for copy paste:\n" << newFormat << LL_ENDL; } if (data.has("SupportGroups")) { const LLSD& support_groups = data["SupportGroups"]; for (LLSD::map_const_iterator itr = support_groups.beginMap(); itr != support_groups.endMap(); ++itr) { mSupportGroup.insert(LLUUID(itr->first)); LL_DEBUGS("fsdata") << "Added " << itr->first << " to mSupportGroup" << LL_ENDL; } } if (data.has("TestingGroups")) { const LLSD& testing_groups = data["TestingGroups"]; for (LLSD::map_const_iterator itr = testing_groups.beginMap(); itr != testing_groups.endMap(); ++itr) { mTestingGroup.insert(LLUUID(itr->first)); LL_DEBUGS("fsdata") << "Added " << itr->first << " to mTestingGroup" << LL_ENDL; } } // The presence of just the key is enough to determine that legacy search needs to be disabled on this grid. if (data.has("DisableLegacySearch")) { mLegacySearch = false; LL_WARNS("fsdata") << "Legacy Search has been disabled." << LL_ENDL; } } void FSData::processClientTags(const LLSD& tags) { if (tags.has("isComplete")) { mLegacyClientList = tags; } } // Selection rules for MOTD: // * if main MOTD is defined, show this at login and during TPs // * if random MOTDs are defined and main MOTD not set, show a random MOTD // at login and every TP // * if event MOTD is defined, show event MOTD at login. For TPs, either // show main MOTD if defined, or show a random MOTD if defined void FSData::selectNextMOTD() { if (LLGridManager::instance().isInSLMain()) { if (!mSecondLifeMOTD.empty()) { gAgent.mMOTD.assign(mSecondLifeMOTD); } else if (mRandomMOTDs.isArray() && mRandomMOTDs.size() > 0) { LLSD::array_const_iterator iter = mRandomMOTDs.beginArray(); gAgent.mMOTD.assign((iter + (ll_rand((S32)mRandomMOTDs.size())))->asString()); } } } //WS: Create a new LLSD based on the data from the mLegacyClientList if LLSD FSData::resolveClientTag(const LLUUID& id, bool new_system, const LLColor4& color) const { LLSD curtag; curtag["uuid"] = id.asString(); curtag["id_based"] = new_system; curtag["tex_color"] = color.getValue(); static const LLUUID id_singularity("f25263b7-6167-4f34-a4ef-af65213b2e39"); static const LLUUID id_kokua("4b6f6b75-bf77-d1ff-0000-000000000000"); static const LLUUID id_radegast("b748af88-58e2-995b-cf26-9486dea8e830"); static const LLUUID id_imprudence("cc7a030f-282f-c165-44d2-b5ee572e72bf"); static const LLUUID id_teapot("7eab0700-f000-0000-0000-546561706f7"); static const LLUUID id_lindenlab("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); static LLCachedControl client_tag_visibility(gSavedSettings, "FSClientTagsVisibility"); static LLCachedControl use_legacy_client_tags(gSavedSettings, "FSUseLegacyClienttags"); static LLCachedControl color_client_tags(gSavedSettings, "FSColorClienttags"); // If we don't want to display anything...return if (!client_tag_visibility) { return curtag; } //WS: Do we want to use Legacy Clienttags? if (use_legacy_client_tags) { if (mLegacyClientList.has(id.asString())) { curtag = mLegacyClientList[id.asString()]; } else { if (id == id_singularity) { curtag["name"] = "Singularity"; } else if (id == id_kokua) { curtag["name"] = "Kokua"; } else if (id == id_radegast) { curtag["name"] = "Radegast"; } else if (id == id_imprudence) { curtag["name"] = "Imprudence"; } else if (id == id_teapot) { curtag["name"] = "Teapot"; } /// [FS:CR] Since SL Viewer can't connect to Opensim, and client tags only work on OpenSim /// it doesn't make much sense to tag V3-based viewers as SL Viewer. /*if (id == id_lindenlab) { curtag["name"] = "SL Viewer"; }*/ if (curtag.has("name")) curtag["tpvd"] = true; } } // Filtering starts here: //WS: If we have a tag using the new system, check if we want to display it's name and/or color if (new_system) { if (client_tag_visibility >= 3) { auto tag_len = strnlen((const char*)&id.mData[0], UUID_BYTES); std::string clienttagname = std::string((const char*)&id.mData[0], tag_len); LLStringFn::replace_ascii_controlchars(clienttagname, LL_UNKNOWN_CHAR); curtag["name"] = clienttagname; } if (color_client_tags >= 3 || curtag["tpvd"].asBoolean()) { if (curtag["tpvd"].asBoolean() && color_client_tags < 3) { if (color == LLColor4::blue || color == LLColor4::yellow || color == LLColor4::purple || color == LLColor4(0.99f, 0.39f, 0.12f, 1.f) || color == LLColor4::red || color == LLColor4(0.99f, 0.56f, 0.65f, 1.f) || color == LLColor4::white || color == LLColor4::green) { curtag["color"] = color.getValue(); } } else { curtag["color"] = color.getValue(); } } } //If we only want to display tpvd viewer. And "tpvd" is not available or false, then // clear the data, but keep the basedata (like uuid, id_based and tex_color) for (maybe) later displaying. if (client_tag_visibility <= 1 && (!curtag.has("tpvd") || !curtag["tpvd"].asBoolean())) { curtag.clear(); } curtag["uuid"] = id.asString(); curtag["id_based"] = new_system; curtag["tex_color"] = color.getValue(); return curtag; } void FSData::updateClientTagsLocal() { LLSD data; LL_DEBUGS("fsdata") << "Loading client_list_v2.xml from " << mClientTagsFilename << LL_ENDL; if (loadFromFile(data, mClientTagsFilename)) { processClientTags(data); } else { LL_WARNS("fsdata") << "Unable to download or load client_list_v2.xml" << LL_ENDL; } } void FSData::saveLLSD(const LLSD& data, const std::string& filename, const LLDate& last_modified) { LL_INFOS("fsdata") << "Saving " << filename << LL_ENDL; llofstream file; file.open(filename.c_str()); if (!file.is_open()) { LL_WARNS("fsdata") << "Unable to open " << filename << LL_ENDL; return; } if (!LLSDSerialize::toPrettyXML(data, file)) { LL_WARNS("fsdata") << "Failed to save LLSD for " << filename << LL_ENDL; } file.close(); const std::time_t new_time = (std::time_t)last_modified.secondsSinceEpoch(); #ifdef LL_WINDOWS boost::filesystem::last_write_time(boost::filesystem::path(utf8str_to_utf16str(filename)), new_time); #else boost::filesystem::last_write_time(boost::filesystem::path(filename), new_time); #endif } S32 FSData::getAgentFlags(const LLUUID& avatar_id) const { std::map::const_iterator iter = mTeamAgents.find(avatar_id); if (iter == mTeamAgents.end()) { return -1; } return iter->second; } bool FSData::isSupport(const LLUUID& avatar_id) const { S32 flags = getAgentFlags(avatar_id); return (flags != -1 && (flags & SUPPORT)); } bool FSData::isDeveloper(const LLUUID& avatar_id) const { S32 flags = getAgentFlags(avatar_id); return (flags != -1 && (flags & DEVELOPER)); } bool FSData::isQA(const LLUUID& avatar_id) const { S32 flags = getAgentFlags(avatar_id); return (flags != -1 && (flags & QA)); } LLSD FSData::allowedLogin() const { std::map::const_iterator iter = mBlockedVersions.find(LLVersionInfo::getInstance()->getChannelAndVersionFS()); if (iter == mBlockedVersions.end()) { return LLSD(); } else { LLSD block = iter->second; bool blocked = true; // default is to block all unless there is a gridtype or grids present. if (block.has("gridtype")) { blocked = false; #ifdef OPENSIM if ((block["gridtype"].asString() == "opensim") && LLGridManager::getInstance()->isInOpenSim()) { return block; } #endif if ((block["gridtype"].asString() == "secondlife") && LLGridManager::getInstance()->isInSecondLife()) { return block; } } if (block.has("grids")) { blocked = false; LLSD grids = block["grids"]; for (LLSD::array_iterator grid_iter = grids.beginArray(); grid_iter != grids.endArray(); ++grid_iter) { if ((*grid_iter).asString() == LLGridManager::getInstance()->getGrid()) { return block; } } } return blocked ? block : LLSD(); } } bool FSData::isFirestormGroup(const LLUUID& id) const { return isSupportGroup(id) || isTestingGroup(id); } bool FSData::isSupportGroup(const LLUUID& id) const { return mSupportGroup.count(id); } bool FSData::isTestingGroup(const LLUUID& id) const { return mTestingGroup.count(id); } bool FSData::isAgentFlag(const LLUUID& agent_id, flags_t flag) const { std::map::const_iterator iter = mTeamAgents.find(agent_id); if (iter == mTeamAgents.end()) { return false; } return (iter->second & flag); } void FSData::onNameCache(const LLUUID& av_id, const LLAvatarName& av_name) { avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(av_id); if (it != mAvatarNameCacheConnections.end()) { if (it->second.connected()) { it->second.disconnect(); } mAvatarNameCacheConnections.erase(it); } LLMute mute(av_id, av_name.getUserName(), LLMute::EXTERNAL); LLMuteList::getInstance()->add(mute); } // this is called in two different places due to can recieved .xml before gCacheName is created and vice versa. void FSData::addAgents() { if (!gCacheName) { return; } for (std::map::iterator iter = mTeamAgents.begin(); iter != mTeamAgents.end(); ++iter) { if (iter->second & NO_SPAM) { LLUUID id = iter->first; avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); if (it != mAvatarNameCacheConnections.end()) { if (it->second.connected()) { it->second.disconnect(); } mAvatarNameCacheConnections.erase(it); } LLAvatarNameCache::callback_connection_t cb = LLAvatarNameCache::get(id, boost::bind(&FSData::onNameCache, this, _1, _2)); mAvatarNameCacheConnections.insert(std::make_pair(id, cb)); } } } std::string FSData::processRequestForInfo(const LLUUID& requester, const std::string& message, const std::string& name, const LLUUID& sessionid) { const std::string detectstring = "/reqsysinfo"; if (message.find(detectstring) != 0) { return message; } if (!isSupport(requester) && !isDeveloper(requester) && !isQA(requester)) { return message; } std::string outmessage = LLTrans::getString("Reqsysinfo_Chat_NoReason"); std::string reason(""); if (message.length() > detectstring.length()) { //there is more to it! reason = message.substr(detectstring.length()); LLStringUtil::format_map_t reason_args; reason_args["REASON"] = reason; outmessage = LLTrans::getString("Reqsysinfo_Chat_Reason", reason_args); reason = LLTrans::getString("Reqsysinfo_Reason", reason_args); } LLSD args; args["REASON"] = reason; args["NAME"] = name; LLNotifications::instance().add("FireStormReqInfo", args, LLSD().with("from_id", requester).with("session_id", sessionid), callbackReqInfo); return outmessage; } //static void FSData::sendInfo(const LLUUID& destination, const LLUUID& sessionid, const std::string& my_name, EInstantMessage dialog) { const LLSD system_info = getSystemInfo(); const std::string part1 = system_info["Part1"].asString(); const std::string part2 = system_info["Part2"].asString(); pack_instant_message( gMessageSystem, gAgentID, false, gAgentSessionID, destination, my_name, part1, IM_ONLINE, dialog, sessionid ); gAgent.sendReliableMessage(); pack_instant_message( gMessageSystem, gAgentID, false, gAgentSessionID, destination, my_name, part2, IM_ONLINE, dialog, sessionid ); gAgent.sendReliableMessage(); LLStringUtil::format_map_t args; args["DATA"] = part1 + "\n" + part2; gIMMgr->addMessage(gIMMgr->computeSessionID(dialog, destination), destination, my_name, LLTrans::getString("Reqsysinfo_Chat_Information_sent", args)); } //static void FSData::callbackReqInfo(const LLSD ¬ification, const LLSD &response) { S32 option = LLNotification::getSelectedOption(notification, response); std::string my_name; LLUUID from_id = notification["payload"]["from_id"].asUUID(); LLUUID session_id = notification["payload"]["session_id"].asUUID(); LLAgentUI::buildFullname(my_name); if (option == 0) //yes { sendInfo(from_id, session_id, my_name, IM_NOTHING_SPECIAL); } else { pack_instant_message( gMessageSystem, gAgentID, false, gAgentSessionID, from_id, my_name, "Request Denied.", // Left English intentionally as it gets sent to the support staff IM_ONLINE, IM_NOTHING_SPECIAL, session_id ); gAgent.sendReliableMessage(); gIMMgr->addMessage(session_id, from_id, my_name, LLTrans::getString("Reqsysinfo_Chat_Request_Denied")); } } //static LLSD FSData::getSystemInfo() { LLSD info = LLAppViewer::instance()->getViewerInfo(); std::string sysinfo1("\n"); sysinfo1 += llformat("%s %s (%d) %s %s (%s %dbit / %s) %s\n\n", LLAppViewer::instance()->getSecondLifeTitle().c_str(), LLVersionInfo::getInstance()->getShortVersion().c_str(), LLVersionInfo::getInstance()->getBuild(), info["BUILD_DATE"].asString().c_str(), info["BUILD_TIME"].asString().c_str(), LLVersionInfo::getInstance()->getChannel().c_str(), info["ADDRESS_SIZE"].asInteger(), info["SIMD"].asString().c_str(), info["BUILD_TYPE"].asString().c_str()); sysinfo1 += llformat("Build with %s version %s\n\n", info["COMPILER"].asString().c_str(), info["COMPILER_VERSION"].asString().c_str()); sysinfo1 += llformat("Location: %s (%s)\n", info["REGION"].asString().c_str(), info["HOSTNAME"].asString().c_str(), info["HOSTIP"].asString().c_str()); sysinfo1 += llformat("%s\n\n", info["SERVER_VERSION"].asString().c_str()); sysinfo1 += llformat("CPU: %s\n", info["CPU"].asString().c_str()); sysinfo1 += llformat("Memory: %d MB (Used: %d MB)\n", info["MEMORY_MB"].asInteger(), info["USED_RAM"].asInteger()); sysinfo1 += llformat("OS: %s\n", info["OS_VERSION"].asString().c_str()); sysinfo1 += llformat("Graphics Card Vendor: %s\n", info["GRAPHICS_CARD_VENDOR"].asString().c_str()); sysinfo1 += llformat("Graphics Card: %s\n", info["GRAPHICS_CARD"].asString().c_str()); sysinfo1 += llformat("VRAM: %d MB\n", info["GRAPHICS_CARD_MEMORY"].asInteger()); sysinfo1 += llformat("VRAM (Detected): %d MB\n", info["GRAPHICS_CARD_MEMORY_DETECTED"].asInteger()); sysinfo1 += llformat("VRAM (Budget): %s\n", info["VRAM_BUDGET_ENGLISH"].asString().c_str()); if (info.has("GRAPHICS_DRIVER_VERSION")) { sysinfo1 += llformat("Graphics Card Driver Version: %s\n", info["GRAPHICS_DRIVER_VERSION"].asString().c_str()); } std::string sysinfo2("\n"); sysinfo2 += llformat("OpenGL Version: %s\n\n", info["OPENGL_VERSION"].asString().c_str()); sysinfo2 += llformat("libcurl Version: %s\n", info["LIBCURL_VERSION"].asString().c_str()); sysinfo2 += llformat("J2C Decoder Version: %s\n", info["J2C_VERSION"].asString().c_str()); sysinfo2 += llformat("Audio Driver Version: %s\n", info["AUDIO_DRIVER_VERSION"].asString().c_str()); sysinfo2 += llformat("%s\n", info["LIBCEF_VERSION"].asString().c_str()); sysinfo2 += llformat("LibVLC Version: %s\n", info["LIBVLC_VERSION"].asString().c_str()); sysinfo2 += llformat("Vivox Version: %s\n", info["VOICE_VERSION"].asString().c_str()); sysinfo2 += llformat("Packets Lost: %.0f/%.0f (%.1f%%)\n\n", info["PACKETS_LOST"].asReal(), info["PACKETS_IN"].asReal(), info["PACKETS_PCT"].asReal()); sysinfo2 += llformat("RLVa: %s\n", info["RLV_VERSION"].asString().c_str()); sysinfo2 += llformat("Mode: %s\n", info["MODE"].asString().c_str()); sysinfo2 += llformat("Skin: %s (%s)\n", info["SKIN"].asString().c_str(), info["THEME"].asString().c_str()); sysinfo2 += llformat("Window Size: %sx%s px\n", info["WINDOW_WIDTH"].asString().c_str(), info["WINDOW_HEIGHT"].asString().c_str()); #if LL_DARWIN sysinfo2 += llformat("HiDPI: %s\n", info["HIDPI"].asBoolean() ? "Enabled" : "Disabled"); #endif sysinfo2 += llformat("Font: %s\n", info["FONT"].asString().c_str()); sysinfo2 += llformat("Font Size Adjustment: %d pt\n", info["FONT_SIZE"].asInteger()); sysinfo2 += llformat("Font Screen DPI: %d\n", info["FONT_SCREEN_DPI"].asInteger()); sysinfo2 += llformat("UI Scaling: %.3f\n", info["UI_SCALE_FACTOR"].asReal()); sysinfo2 += llformat("Draw Distance: %d m\n", info["DRAW_DISTANCE"].asInteger()); sysinfo2 += llformat("Bandwidth: %d kbit/s\n", info["BANDWIDTH"].asInteger()); sysinfo2 += llformat("LOD Factor: %.3f\n", info["LOD"].asReal()); sysinfo2 += llformat("Render quality: %s\n", info["RENDERQUALITY_FSDATA_ENGLISH"].asString().c_str()); sysinfo2 += "Disk cache: " + info["DISK_CACHE_INFO"].asString(); LLSD sysinfos; sysinfos["Part1"] = sysinfo1; sysinfos["Part2"] = sysinfo2; return sysinfos; }