2 * Copyright (C) 2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "MediaSourceSettings.h"
24 #include "filesystem/File.h"
25 #include "profiles/ProfilesManager.h"
26 #include "utils/log.h"
27 #include "utils/StringUtils.h"
28 #include "utils/URIUtils.h"
29 #include "utils/XBMCTinyXML.h"
30 #include "utils/XMLUtils.h"
31 #include "network/WakeOnAccess.h"
33 #define SOURCES_FILE "sources.xml"
34 #define XML_SOURCES "sources"
35 #define XML_SOURCE "source"
38 using namespace XFILE;
40 CMediaSourceSettings::CMediaSourceSettings()
45 CMediaSourceSettings::~CMediaSourceSettings()
48 CMediaSourceSettings& CMediaSourceSettings::Get()
50 static CMediaSourceSettings sMediaSourceSettings;
51 return sMediaSourceSettings;
54 std::string CMediaSourceSettings::GetSourcesFile()
57 if (CProfilesManager::Get().GetCurrentProfile().hasSources())
58 file = CProfilesManager::Get().GetProfileUserDataFolder();
60 file = CProfilesManager::Get().GetUserDataFolder();
62 return URIUtils::AddFileToFolder(file, SOURCES_FILE);
65 void CMediaSourceSettings::OnSettingsLoaded()
70 void CMediaSourceSettings::OnSettingsUnloaded()
75 bool CMediaSourceSettings::Load()
77 return Load(GetSourcesFile());
80 bool CMediaSourceSettings::Load(const std::string &file)
84 if (!CFile::Exists(file))
87 CLog::Log(LOGNOTICE, "CMediaSourceSettings: loading media sources from %s", file.c_str());
91 if (!xmlDoc.LoadFile(file))
93 CLog::Log(LOGERROR, "CMediaSourceSettings: error loading %s: Line %d, %s", file.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
97 TiXmlElement *pRootElement = xmlDoc.RootElement();
98 if (pRootElement == NULL || !StringUtils::EqualsNoCase(pRootElement->ValueStr(), XML_SOURCES))
99 CLog::Log(LOGERROR, "CMediaSourceSettings: sources.xml file does not contain <sources>");
103 GetSources(pRootElement, "video", m_videoSources, dummy);
104 GetSources(pRootElement, "programs", m_programSources, m_defaultProgramSource);
105 GetSources(pRootElement, "pictures", m_pictureSources, m_defaultPictureSource);
106 GetSources(pRootElement, "files", m_fileSources, m_defaultFileSource);
107 GetSources(pRootElement, "music", m_musicSources, m_defaultMusicSource);
112 bool CMediaSourceSettings::Save()
114 return Save(GetSourcesFile());
117 bool CMediaSourceSettings::Save(const std::string &file) const
119 // TODO: Should we be specifying utf8 here??
121 TiXmlElement xmlRootElement(XML_SOURCES);
122 TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement);
126 // ok, now run through and save each sources section
127 SetSources(pRoot, "programs", m_programSources, m_defaultProgramSource);
128 SetSources(pRoot, "video", m_videoSources, "");
129 SetSources(pRoot, "music", m_musicSources, m_defaultMusicSource);
130 SetSources(pRoot, "pictures", m_pictureSources, m_defaultPictureSource);
131 SetSources(pRoot, "files", m_fileSources, m_defaultFileSource);
133 CWakeOnAccess::Get().QueueMACDiscoveryForAllRemotes();
135 return doc.SaveFile(file);
138 void CMediaSourceSettings::Clear()
140 m_programSources.clear();
141 m_pictureSources.clear();
142 m_fileSources.clear();
143 m_musicSources.clear();
144 m_videoSources.clear();
147 VECSOURCES* CMediaSourceSettings::GetSources(const std::string &type)
149 if (type == "programs" || type == "myprograms")
150 return &m_programSources;
151 else if (type == "files")
152 return &m_fileSources;
153 else if (type == "music")
154 return &m_musicSources;
155 else if (type == "video" || type == "videos")
156 return &m_videoSources;
157 else if (type == "pictures")
158 return &m_pictureSources;
163 const std::string& CMediaSourceSettings::GetDefaultSource(const std::string &type) const
165 if (type == "programs" || type == "myprograms")
166 return m_defaultProgramSource;
167 else if (type == "files")
168 return m_defaultFileSource;
169 else if (type == "music")
170 return m_defaultMusicSource;
171 else if (type == "pictures")
172 return m_defaultPictureSource;
174 return StringUtils::Empty;
177 void CMediaSourceSettings::SetDefaultSource(const std::string &type, const std::string &source)
179 if (type == "programs" || type == "myprograms")
180 m_defaultProgramSource = source;
181 else if (type == "files")
182 m_defaultFileSource = source;
183 else if (type == "music")
184 m_defaultMusicSource = source;
185 else if (type == "pictures")
186 m_defaultPictureSource = source;
189 // NOTE: This function does NOT save the sources.xml file - you need to call SaveSources() separately.
190 bool CMediaSourceSettings::UpdateSource(const std::string &strType, const std::string &strOldName, const std::string &strUpdateChild, const std::string &strUpdateValue)
192 VECSOURCES *pShares = GetSources(strType);
196 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
198 if (it->strName == strOldName)
200 if (strUpdateChild == "name")
201 it->strName = strUpdateValue;
202 else if (strUpdateChild == "lockmode")
203 it->m_iLockMode = (LockType)strtol(strUpdateValue.c_str(), NULL, 10);
204 else if (strUpdateChild == "lockcode")
205 it->m_strLockCode = strUpdateValue;
206 else if (strUpdateChild == "badpwdcount")
207 it->m_iBadPwdCount = (int)strtol(strUpdateValue.c_str(), NULL, 10);
208 else if (strUpdateChild == "thumbnail")
209 it->m_strThumbnailImage = strUpdateValue;
210 else if (strUpdateChild == "path")
212 it->vecPaths.clear();
213 it->strPath = strUpdateValue;
214 it->vecPaths.push_back(strUpdateValue);
226 bool CMediaSourceSettings::DeleteSource(const std::string &strType, const std::string &strName, const std::string &strPath, bool virtualSource /* = false */)
228 VECSOURCES *pShares = GetSources(strType);
234 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
236 if (it->strName == strName && it->strPath == strPath)
238 CLog::Log(LOGDEBUG, "CMediaSourceSettings: found share, removing!");
251 bool CMediaSourceSettings::AddShare(const std::string &type, const CMediaSource &share)
253 VECSOURCES *pShares = GetSources(type);
257 // translate dir and add to our current shares
258 string strPath1 = share.strPath;
259 if (strPath1.empty())
261 CLog::Log(LOGERROR, "CMediaSourceSettings: unable to add empty path");
264 StringUtils::ToUpper(strPath1);
266 CMediaSource shareToAdd = share;
267 if (strPath1.at(0) == '$')
269 shareToAdd.strPath = CUtil::TranslateSpecialSource(strPath1);
270 if (!share.strPath.empty())
271 CLog::Log(LOGDEBUG, "CMediaSourceSettings: translated (%s) to path (%s)", strPath1.c_str(), shareToAdd.strPath.c_str());
274 CLog::Log(LOGDEBUG, "CMediaSourceSettings: skipping invalid special directory token (%s)", strPath1.c_str());
278 pShares->push_back(shareToAdd);
286 bool CMediaSourceSettings::UpdateShare(const std::string &type, const std::string &oldName, const CMediaSource &share)
288 VECSOURCES *pShares = GetSources(type);
292 // update our current share list
293 CMediaSource* pShare = NULL;
294 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
296 if (it->strName == oldName)
298 it->strName = share.strName;
299 it->strPath = share.strPath;
300 it->vecPaths = share.vecPaths;
309 // Update our XML file as well
313 bool CMediaSourceSettings::GetSource(const std::string &category, const TiXmlNode *source, CMediaSource &share)
315 const TiXmlNode *pNodeName = source->FirstChild("name");
317 if (pNodeName && pNodeName->FirstChild())
318 strName = pNodeName->FirstChild()->ValueStr();
320 // get multiple paths
321 vector<string> vecPaths;
322 const TiXmlElement *pPathName = source->FirstChildElement("path");
323 while (pPathName != NULL)
325 if (pPathName->FirstChild())
327 CStdString strPath = pPathName->FirstChild()->ValueStr();
329 // make sure there are no virtualpaths or stack paths defined in sources.xml
330 if (!URIUtils::IsStack(strPath))
332 // translate special tags
333 if (!strPath.empty() && strPath.at(0) == '$')
334 strPath = CUtil::TranslateSpecialSource(strPath);
336 // need to check path validity again as CUtil::TranslateSpecialSource() may have failed
337 if (!strPath.empty())
339 URIUtils::AddSlashAtEnd(strPath);
340 vecPaths.push_back(strPath);
344 CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type (%s) in source", strPath.c_str());
347 pPathName = pPathName->NextSiblingElement("path");
350 const TiXmlNode *pLockMode = source->FirstChild("lockmode");
351 const TiXmlNode *pLockCode = source->FirstChild("lockcode");
352 const TiXmlNode *pBadPwdCount = source->FirstChild("badpwdcount");
353 const TiXmlNode *pThumbnailNode = source->FirstChild("thumbnail");
355 if (strName.empty() || vecPaths.empty())
358 vector<CStdString> verifiedPaths;
359 // disallowed for files, or theres only a single path in the vector
360 if (StringUtils::EqualsNoCase(category, "files") || vecPaths.size() == 1)
361 verifiedPaths.push_back(vecPaths[0]);
365 // validate the paths
366 for (vector<string>::const_iterator path = vecPaths.begin(); path != vecPaths.end(); ++path)
369 string protocol = url.GetProtocol();
370 bool bIsInvalid = false;
373 if (StringUtils::EqualsNoCase(category, "programs") || StringUtils::EqualsNoCase(category, "myprograms"))
375 // only allow HD and plugins
376 if (url.IsLocal() || StringUtils::EqualsNoCase(protocol, "plugin"))
377 verifiedPaths.push_back(*path);
381 // for others allow everything (if the user does something silly, we can't stop them)
383 verifiedPaths.push_back(*path);
387 CLog::Log(LOGERROR,"CMediaSourceSettings: invalid path type (%s) for multipath source", path->c_str());
390 // no valid paths? skip to next source
391 if (verifiedPaths.empty())
393 CLog::Log(LOGERROR,"CMediaSourceSettings: missing or invalid <name> and/or <path> in source");
398 share.FromNameAndPaths(category, strName, verifiedPaths);
400 share.m_iBadPwdCount = 0;
403 share.m_iLockMode = (LockType)strtol(pLockMode->FirstChild()->Value(), NULL, 10);
404 share.m_iHasLock = 2;
407 if (pLockCode && pLockCode->FirstChild())
408 share.m_strLockCode = pLockCode->FirstChild()->Value();
410 if (pBadPwdCount && pBadPwdCount->FirstChild())
411 share.m_iBadPwdCount = (int)strtol(pBadPwdCount->FirstChild()->Value(), NULL, 10);
413 if (pThumbnailNode && pThumbnailNode->FirstChild())
414 share.m_strThumbnailImage = pThumbnailNode->FirstChild()->Value();
416 XMLUtils::GetBoolean(source, "allowsharing", share.m_allowSharing);
421 void CMediaSourceSettings::GetSources(const TiXmlNode* pRootElement, const std::string& strTagName, VECSOURCES& items, std::string& strDefault)
426 const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str());
429 CLog::Log(LOGDEBUG, "CMediaSourceSettings: <%s> tag is missing or sources.xml is malformed", strTagName.c_str());
433 pChild = pChild->FirstChild();
434 while (pChild != NULL)
436 std::string strValue = pChild->ValueStr();
437 if (strValue == XML_SOURCE || strValue == "bookmark") // "bookmark" left in for backwards compatibility
440 if (GetSource(strTagName, pChild, share))
441 items.push_back(share);
443 CLog::Log(LOGERROR, "CMediaSourceSettings: Missing or invalid <name> and/or <path> in source");
445 else if (strValue == "default")
447 const TiXmlNode *pValueNode = pChild->FirstChild();
450 std::string pszText = pChild->FirstChild()->ValueStr();
451 if (!pszText.empty())
452 strDefault = pszText;
453 CLog::Log(LOGDEBUG, "CMediaSourceSettings: Setting <default> source to : %s", strDefault.c_str());
457 pChild = pChild->NextSibling();
461 bool CMediaSourceSettings::SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const std::string &defaultPath) const
463 TiXmlElement sectionElement(section);
464 TiXmlNode *sectionNode = root->InsertEndChild(sectionElement);
465 if (sectionNode == NULL)
468 XMLUtils::SetPath(sectionNode, "default", defaultPath);
469 for (CIVECSOURCES it = shares.begin(); it != shares.end(); it++)
471 const CMediaSource &share = *it;
475 TiXmlElement source(XML_SOURCE);
476 XMLUtils::SetString(&source, "name", share.strName);
478 for (unsigned int i = 0; i < share.vecPaths.size(); i++)
479 XMLUtils::SetPath(&source, "path", share.vecPaths[i]);
481 if (share.m_iHasLock)
483 XMLUtils::SetInt(&source, "lockmode", share.m_iLockMode);
484 XMLUtils::SetString(&source, "lockcode", share.m_strLockCode);
485 XMLUtils::SetInt(&source, "badpwdcount", share.m_iBadPwdCount);
488 if (!share.m_strThumbnailImage.empty())
489 XMLUtils::SetPath(&source, "thumbnail", share.m_strThumbnailImage);
491 XMLUtils::SetBoolean(&source, "allowsharing", share.m_allowSharing);
493 sectionNode->InsertEndChild(source);