diff options
Diffstat (limited to 'xbmc/settings/MediaSourceSettings.cpp')
-rw-r--r-- | xbmc/settings/MediaSourceSettings.cpp | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/xbmc/settings/MediaSourceSettings.cpp b/xbmc/settings/MediaSourceSettings.cpp new file mode 100644 index 0000000..183131f --- /dev/null +++ b/xbmc/settings/MediaSourceSettings.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2013-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "MediaSourceSettings.h" + +#include "ServiceBroker.h" +#include "URL.h" +#include "Util.h" +#include "media/MediaLockState.h" +#include "network/WakeOnAccess.h" +#include "profiles/ProfileManager.h" +#include "settings/SettingsComponent.h" +#include "utils/FileUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/XMLUtils.h" +#include "utils/log.h" + +#include <cstdlib> +#include <string> + +#define SOURCES_FILE "sources.xml" +#define XML_SOURCES "sources" +#define XML_SOURCE "source" + +CMediaSourceSettings::CMediaSourceSettings() +{ + Clear(); +} + +CMediaSourceSettings::~CMediaSourceSettings() = default; + +CMediaSourceSettings& CMediaSourceSettings::GetInstance() +{ + static CMediaSourceSettings sMediaSourceSettings; + return sMediaSourceSettings; +} + +std::string CMediaSourceSettings::GetSourcesFile() +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + std::string file; + if (profileManager->GetCurrentProfile().hasSources()) + file = profileManager->GetProfileUserDataFolder(); + else + file = profileManager->GetUserDataFolder(); + + return URIUtils::AddFileToFolder(file, SOURCES_FILE); +} + +void CMediaSourceSettings::OnSettingsLoaded() +{ + Load(); +} + +void CMediaSourceSettings::OnSettingsUnloaded() +{ + Clear(); +} + +bool CMediaSourceSettings::Load() +{ + return Load(GetSourcesFile()); +} + +bool CMediaSourceSettings::Load(const std::string &file) +{ + Clear(); + + if (!CFileUtils::Exists(file)) + return false; + + CLog::Log(LOGINFO, "CMediaSourceSettings: loading media sources from {}", file); + + // load xml file + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(file)) + { + CLog::Log(LOGERROR, "CMediaSourceSettings: error loading {}: Line {}, {}", file, + xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); + return false; + } + + TiXmlElement *pRootElement = xmlDoc.RootElement(); + if (pRootElement == NULL || !StringUtils::EqualsNoCase(pRootElement->ValueStr(), XML_SOURCES)) + CLog::Log(LOGERROR, "CMediaSourceSettings: sources.xml file does not contain <sources>"); + + // parse sources + std::string dummy; + GetSources(pRootElement, "video", m_videoSources, dummy); + GetSources(pRootElement, "programs", m_programSources, m_defaultProgramSource); + GetSources(pRootElement, "pictures", m_pictureSources, m_defaultPictureSource); + GetSources(pRootElement, "files", m_fileSources, m_defaultFileSource); + GetSources(pRootElement, "music", m_musicSources, m_defaultMusicSource); + GetSources(pRootElement, "games", m_gameSources, dummy); + + return true; +} + +bool CMediaSourceSettings::Save() +{ + return Save(GetSourcesFile()); +} + +bool CMediaSourceSettings::Save(const std::string &file) const +{ + //! @todo Should we be specifying utf8 here?? + CXBMCTinyXML doc; + TiXmlElement xmlRootElement(XML_SOURCES); + TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement); + if (pRoot == NULL) + return false; + + // ok, now run through and save each sources section + SetSources(pRoot, "programs", m_programSources, m_defaultProgramSource); + SetSources(pRoot, "video", m_videoSources, ""); + SetSources(pRoot, "music", m_musicSources, m_defaultMusicSource); + SetSources(pRoot, "pictures", m_pictureSources, m_defaultPictureSource); + SetSources(pRoot, "files", m_fileSources, m_defaultFileSource); + SetSources(pRoot, "games", m_gameSources, ""); + + CWakeOnAccess::GetInstance().QueueMACDiscoveryForAllRemotes(); + + return doc.SaveFile(file); +} + +void CMediaSourceSettings::Clear() +{ + m_programSources.clear(); + m_pictureSources.clear(); + m_fileSources.clear(); + m_musicSources.clear(); + m_videoSources.clear(); + m_gameSources.clear(); +} + +VECSOURCES* CMediaSourceSettings::GetSources(const std::string &type) +{ + if (type == "programs" || type == "myprograms") + return &m_programSources; + else if (type == "files") + return &m_fileSources; + else if (type == "music") + return &m_musicSources; + else if (type == "video" || type == "videos") + return &m_videoSources; + else if (type == "pictures") + return &m_pictureSources; + else if (type == "games") + return &m_gameSources; + + return NULL; +} + +const std::string& CMediaSourceSettings::GetDefaultSource(const std::string &type) const +{ + if (type == "programs" || type == "myprograms") + return m_defaultProgramSource; + else if (type == "files") + return m_defaultFileSource; + else if (type == "music") + return m_defaultMusicSource; + else if (type == "pictures") + return m_defaultPictureSource; + + return StringUtils::Empty; +} + +void CMediaSourceSettings::SetDefaultSource(const std::string &type, const std::string &source) +{ + if (type == "programs" || type == "myprograms") + m_defaultProgramSource = source; + else if (type == "files") + m_defaultFileSource = source; + else if (type == "music") + m_defaultMusicSource = source; + else if (type == "pictures") + m_defaultPictureSource = source; +} + +// NOTE: This function does NOT save the sources.xml file - you need to call SaveSources() separately. +bool CMediaSourceSettings::UpdateSource(const std::string &strType, const std::string &strOldName, const std::string &strUpdateChild, const std::string &strUpdateValue) +{ + VECSOURCES *pShares = GetSources(strType); + if (pShares == NULL) + return false; + + for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++) + { + if (it->strName == strOldName) + { + if (strUpdateChild == "name") + it->strName = strUpdateValue; + else if (strUpdateChild == "lockmode") + it->m_iLockMode = (LockType)std::strtol(strUpdateValue.c_str(), NULL, 10); + else if (strUpdateChild == "lockcode") + it->m_strLockCode = strUpdateValue; + else if (strUpdateChild == "badpwdcount") + it->m_iBadPwdCount = (int)std::strtol(strUpdateValue.c_str(), NULL, 10); + else if (strUpdateChild == "thumbnail") + it->m_strThumbnailImage = strUpdateValue; + else if (strUpdateChild == "path") + { + it->vecPaths.clear(); + it->strPath = strUpdateValue; + it->vecPaths.push_back(strUpdateValue); + } + else + return false; + + return true; + } + } + + return false; +} + +bool CMediaSourceSettings::DeleteSource(const std::string &strType, const std::string &strName, const std::string &strPath, bool virtualSource /* = false */) +{ + VECSOURCES *pShares = GetSources(strType); + if (pShares == NULL) + return false; + + bool found = false; + + for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++) + { + if (it->strName == strName && it->strPath == strPath) + { + CLog::Log(LOGDEBUG, "CMediaSourceSettings: found share, removing!"); + pShares->erase(it); + found = true; + break; + } + } + + if (virtualSource) + return found; + + return Save(); +} + +bool CMediaSourceSettings::AddShare(const std::string &type, const CMediaSource &share) +{ + VECSOURCES *pShares = GetSources(type); + if (pShares == NULL) + return false; + + // translate dir and add to our current shares + std::string strPath1 = share.strPath; + if (strPath1.empty()) + { + CLog::Log(LOGERROR, "CMediaSourceSettings: unable to add empty path"); + return false; + } + StringUtils::ToUpper(strPath1); + + CMediaSource shareToAdd = share; + if (strPath1.at(0) == '$') + { + shareToAdd.strPath = CUtil::TranslateSpecialSource(strPath1); + if (!share.strPath.empty()) + CLog::Log(LOGDEBUG, "CMediaSourceSettings: translated ({}) to path ({})", strPath1, + shareToAdd.strPath); + else + { + CLog::Log(LOGDEBUG, "CMediaSourceSettings: skipping invalid special directory token ({})", + strPath1); + return false; + } + } + pShares->push_back(shareToAdd); + + if (!share.m_ignore) + return Save(); + + return true; +} + +bool CMediaSourceSettings::UpdateShare(const std::string &type, const std::string &oldName, const CMediaSource &share) +{ + VECSOURCES *pShares = GetSources(type); + if (pShares == NULL) + return false; + + // update our current share list + CMediaSource* pShare = NULL; + for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++) + { + if (it->strName == oldName) + { + it->strName = share.strName; + it->strPath = share.strPath; + it->vecPaths = share.vecPaths; + pShare = &(*it); + break; + } + } + + if (pShare == NULL) + return false; + + // Update our XML file as well + return Save(); +} + +bool CMediaSourceSettings::GetSource(const std::string &category, const TiXmlNode *source, CMediaSource &share) +{ + const TiXmlNode *pNodeName = source->FirstChild("name"); + std::string strName; + if (pNodeName && pNodeName->FirstChild()) + strName = pNodeName->FirstChild()->ValueStr(); + + // get multiple paths + std::vector<std::string> vecPaths; + const TiXmlElement *pPathName = source->FirstChildElement("path"); + while (pPathName != NULL) + { + if (pPathName->FirstChild()) + { + std::string strPath = pPathName->FirstChild()->ValueStr(); + + // make sure there are no virtualpaths or stack paths defined in sources.xml + if (!URIUtils::IsStack(strPath)) + { + // translate special tags + if (!strPath.empty() && strPath.at(0) == '$') + strPath = CUtil::TranslateSpecialSource(strPath); + + // need to check path validity again as CUtil::TranslateSpecialSource() may have failed + if (!strPath.empty()) + { + URIUtils::AddSlashAtEnd(strPath); + vecPaths.push_back(strPath); + } + } + else + CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) in source", strPath); + } + + pPathName = pPathName->NextSiblingElement("path"); + } + + const TiXmlNode *pLockMode = source->FirstChild("lockmode"); + const TiXmlNode *pLockCode = source->FirstChild("lockcode"); + const TiXmlNode *pBadPwdCount = source->FirstChild("badpwdcount"); + const TiXmlNode *pThumbnailNode = source->FirstChild("thumbnail"); + + if (strName.empty() || vecPaths.empty()) + return false; + + std::vector<std::string> verifiedPaths; + // disallowed for files, or there's only a single path in the vector + if (StringUtils::EqualsNoCase(category, "files") || vecPaths.size() == 1) + verifiedPaths.push_back(vecPaths[0]); + // multiple paths? + else + { + // validate the paths + for (std::vector<std::string>::const_iterator path = vecPaths.begin(); path != vecPaths.end(); ++path) + { + CURL url(*path); + bool bIsInvalid = false; + + // for my programs + if (StringUtils::EqualsNoCase(category, "programs") || StringUtils::EqualsNoCase(category, "myprograms")) + { + // only allow HD and plugins + if (url.IsLocal() || url.IsProtocol("plugin")) + verifiedPaths.push_back(*path); + else + bIsInvalid = true; + } + // for others allow everything (if the user does something silly, we can't stop them) + else + verifiedPaths.push_back(*path); + + // error message + if (bIsInvalid) + CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) for multipath source", + path->c_str()); + } + + // no valid paths? skip to next source + if (verifiedPaths.empty()) + { + CLog::Log(LOGERROR,"CMediaSourceSettings: missing or invalid <name> and/or <path> in source"); + return false; + } + } + + share.FromNameAndPaths(category, strName, verifiedPaths); + + share.m_iBadPwdCount = 0; + if (pLockMode) + { + share.m_iLockMode = (LockType)std::strtol(pLockMode->FirstChild()->Value(), NULL, 10); + share.m_iHasLock = LOCK_STATE_LOCKED; + } + + if (pLockCode && pLockCode->FirstChild()) + share.m_strLockCode = pLockCode->FirstChild()->Value(); + + if (pBadPwdCount && pBadPwdCount->FirstChild()) + share.m_iBadPwdCount = (int)std::strtol(pBadPwdCount->FirstChild()->Value(), NULL, 10); + + if (pThumbnailNode && pThumbnailNode->FirstChild()) + share.m_strThumbnailImage = pThumbnailNode->FirstChild()->Value(); + + XMLUtils::GetBoolean(source, "allowsharing", share.m_allowSharing); + + return true; +} + +void CMediaSourceSettings::GetSources(const TiXmlNode* pRootElement, const std::string& strTagName, VECSOURCES& items, std::string& strDefault) +{ + strDefault = ""; + items.clear(); + + const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str()); + if (pChild == NULL) + { + CLog::Log(LOGDEBUG, "CMediaSourceSettings: <{}> tag is missing or sources.xml is malformed", + strTagName); + return; + } + + pChild = pChild->FirstChild(); + while (pChild != NULL) + { + std::string strValue = pChild->ValueStr(); + if (strValue == XML_SOURCE || strValue == "bookmark") // "bookmark" left in for backwards compatibility + { + CMediaSource share; + if (GetSource(strTagName, pChild, share)) + items.push_back(share); + else + CLog::Log(LOGERROR, "CMediaSourceSettings: Missing or invalid <name> and/or <path> in source"); + } + else if (strValue == "default") + { + const TiXmlNode *pValueNode = pChild->FirstChild(); + if (pValueNode) + { + std::string pszText = pChild->FirstChild()->ValueStr(); + if (!pszText.empty()) + strDefault = pszText; + CLog::Log(LOGDEBUG, "CMediaSourceSettings: Setting <default> source to : {}", + strDefault); + } + } + + pChild = pChild->NextSibling(); + } +} + +bool CMediaSourceSettings::SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const std::string &defaultPath) const +{ + TiXmlElement sectionElement(section); + TiXmlNode *sectionNode = root->InsertEndChild(sectionElement); + if (sectionNode == NULL) + return false; + + XMLUtils::SetPath(sectionNode, "default", defaultPath); + for (CIVECSOURCES it = shares.begin(); it != shares.end(); it++) + { + const CMediaSource &share = *it; + if (share.m_ignore) + continue; + + TiXmlElement source(XML_SOURCE); + XMLUtils::SetString(&source, "name", share.strName); + + for (unsigned int i = 0; i < share.vecPaths.size(); i++) + XMLUtils::SetPath(&source, "path", share.vecPaths[i]); + + if (share.m_iHasLock) + { + XMLUtils::SetInt(&source, "lockmode", share.m_iLockMode); + XMLUtils::SetString(&source, "lockcode", share.m_strLockCode); + XMLUtils::SetInt(&source, "badpwdcount", share.m_iBadPwdCount); + } + + if (!share.m_strThumbnailImage.empty()) + XMLUtils::SetPath(&source, "thumbnail", share.m_strThumbnailImage); + + XMLUtils::SetBoolean(&source, "allowsharing", share.m_allowSharing); + + sectionNode->InsertEndChild(source); + } + + return true; +} |