Remove LiveTV menu.
[vuplus_xbmc] / xbmc / settings / MediaSourceSettings.cpp
1 /*
2  *      Copyright (C) 2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "MediaSourceSettings.h"
22 #include "URL.h"
23 #include "Util.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"
32
33 #define SOURCES_FILE  "sources.xml"
34 #define XML_SOURCES   "sources"
35 #define XML_SOURCE    "source"
36
37 using namespace std;
38 using namespace XFILE;
39
40 CMediaSourceSettings::CMediaSourceSettings()
41 {
42   Clear();
43 }
44
45 CMediaSourceSettings::~CMediaSourceSettings()
46 { }
47
48 CMediaSourceSettings& CMediaSourceSettings::Get()
49 {
50   static CMediaSourceSettings sMediaSourceSettings;
51   return sMediaSourceSettings;
52 }
53
54 std::string CMediaSourceSettings::GetSourcesFile()
55 {
56   std::string file;
57   if (CProfilesManager::Get().GetCurrentProfile().hasSources())
58     file = CProfilesManager::Get().GetProfileUserDataFolder();
59   else
60     file = CProfilesManager::Get().GetUserDataFolder();
61
62   return URIUtils::AddFileToFolder(file, SOURCES_FILE);
63 }
64
65 void CMediaSourceSettings::OnSettingsLoaded()
66 {
67   Load();
68 }
69
70 void CMediaSourceSettings::OnSettingsUnloaded()
71 {
72   Clear();
73 }
74
75 bool CMediaSourceSettings::Load()
76 {
77   return Load(GetSourcesFile());
78 }
79
80 bool CMediaSourceSettings::Load(const std::string &file)
81 {
82   Clear();
83
84   if (!CFile::Exists(file))
85     return false;
86
87   CLog::Log(LOGNOTICE, "CMediaSourceSettings: loading media sources from %s", file.c_str());
88
89   // load xml file
90   CXBMCTinyXML xmlDoc;
91   if (!xmlDoc.LoadFile(file))
92   {
93     CLog::Log(LOGERROR, "CMediaSourceSettings: error loading %s: Line %d, %s", file.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
94     return false;
95   }
96
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>");
100
101   // parse sources
102   std::string dummy;
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);
108
109   return true;
110 }
111
112 bool CMediaSourceSettings::Save()
113 {
114   return Save(GetSourcesFile());
115 }
116
117 bool CMediaSourceSettings::Save(const std::string &file) const
118 {
119   // TODO: Should we be specifying utf8 here??
120   CXBMCTinyXML doc;
121   TiXmlElement xmlRootElement(XML_SOURCES);
122   TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement);
123   if (pRoot == NULL)
124     return false;
125
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);
132
133   CWakeOnAccess::Get().QueueMACDiscoveryForAllRemotes();
134
135   return doc.SaveFile(file);
136 }
137
138 void CMediaSourceSettings::Clear()
139 {
140   m_programSources.clear();
141   m_pictureSources.clear();
142   m_fileSources.clear();
143   m_musicSources.clear();
144   m_videoSources.clear();
145 }
146
147 VECSOURCES* CMediaSourceSettings::GetSources(const std::string &type)
148 {
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;
159
160   return NULL;
161 }
162
163 const std::string& CMediaSourceSettings::GetDefaultSource(const std::string &type) const
164 {
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;
173
174   return StringUtils::Empty;
175 }
176
177 void CMediaSourceSettings::SetDefaultSource(const std::string &type, const std::string &source)
178 {
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;
187 }
188
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)
191 {
192   VECSOURCES *pShares = GetSources(strType);
193   if (pShares == NULL)
194     return false;
195
196   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
197   {
198     if (it->strName == strOldName)
199     {
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")
211       {
212         it->vecPaths.clear();
213         it->strPath = strUpdateValue;
214         it->vecPaths.push_back(strUpdateValue);
215       }
216       else
217         return false;
218
219       return true;
220     }
221   }
222
223   return false;
224 }
225
226 bool CMediaSourceSettings::DeleteSource(const std::string &strType, const std::string &strName, const std::string &strPath, bool virtualSource /* = false */)
227 {
228   VECSOURCES *pShares = GetSources(strType);
229   if (pShares == NULL)
230     return false;
231
232   bool found = false;
233
234   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
235   {
236     if (it->strName == strName && it->strPath == strPath)
237     {
238       CLog::Log(LOGDEBUG, "CMediaSourceSettings: found share, removing!");
239       pShares->erase(it);
240       found = true;
241       break;
242     }
243   }
244
245   if (virtualSource)
246     return found;
247
248   return Save();
249 }
250
251 bool CMediaSourceSettings::AddShare(const std::string &type, const CMediaSource &share)
252 {
253   VECSOURCES *pShares = GetSources(type);
254   if (pShares == NULL)
255     return false;
256
257   // translate dir and add to our current shares
258   string strPath1 = share.strPath;
259   if (strPath1.empty())
260   {
261     CLog::Log(LOGERROR, "CMediaSourceSettings: unable to add empty path");
262     return false;
263   }
264   StringUtils::ToUpper(strPath1);
265
266   CMediaSource shareToAdd = share;
267   if (strPath1.at(0) == '$')
268   {
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());
272     else
273     {
274       CLog::Log(LOGDEBUG, "CMediaSourceSettings: skipping invalid special directory token (%s)", strPath1.c_str());
275       return false;
276     }
277   }
278   pShares->push_back(shareToAdd);
279
280   if (!share.m_ignore)
281     return Save();
282
283   return true;
284 }
285
286 bool CMediaSourceSettings::UpdateShare(const std::string &type, const std::string &oldName, const CMediaSource &share)
287 {
288   VECSOURCES *pShares = GetSources(type);
289   if (pShares == NULL)
290     return false;
291
292   // update our current share list
293   CMediaSource* pShare = NULL;
294   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
295   {
296     if (it->strName == oldName)
297     {
298       it->strName = share.strName;
299       it->strPath = share.strPath;
300       it->vecPaths = share.vecPaths;
301       pShare = &(*it);
302       break;
303     }
304   }
305
306   if (pShare == NULL)
307     return false;
308
309   // Update our XML file as well
310   return Save();
311 }
312
313 bool CMediaSourceSettings::GetSource(const std::string &category, const TiXmlNode *source, CMediaSource &share)
314 {
315   const TiXmlNode *pNodeName = source->FirstChild("name");
316   string strName;
317   if (pNodeName && pNodeName->FirstChild())
318     strName = pNodeName->FirstChild()->ValueStr();
319
320   // get multiple paths
321   vector<string> vecPaths;
322   const TiXmlElement *pPathName = source->FirstChildElement("path");
323   while (pPathName != NULL)
324   {
325     if (pPathName->FirstChild())
326     {
327       CStdString strPath = pPathName->FirstChild()->ValueStr();
328
329       // make sure there are no virtualpaths or stack paths defined in sources.xml
330       if (!URIUtils::IsStack(strPath))
331       {
332         // translate special tags
333         if (!strPath.empty() && strPath.at(0) == '$')
334           strPath = CUtil::TranslateSpecialSource(strPath);
335
336         // need to check path validity again as CUtil::TranslateSpecialSource() may have failed
337         if (!strPath.empty())
338         {
339           URIUtils::AddSlashAtEnd(strPath);
340           vecPaths.push_back(strPath);
341         }
342       }
343       else
344         CLog::Log(LOGERROR, "CMediaSourceSettings:    invalid path type (%s) in source", strPath.c_str());
345     }
346
347     pPathName = pPathName->NextSiblingElement("path");
348   }
349
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");
354
355   if (strName.empty() || vecPaths.empty())
356     return false;
357
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]);
362   // multiple paths?
363   else
364   {
365     // validate the paths
366     for (vector<string>::const_iterator path = vecPaths.begin(); path != vecPaths.end(); ++path)
367     {
368       CURL url(*path);
369       string protocol = url.GetProtocol();
370       bool bIsInvalid = false;
371
372       // for my programs
373       if (StringUtils::EqualsNoCase(category, "programs") || StringUtils::EqualsNoCase(category, "myprograms"))
374       {
375         // only allow HD and plugins
376         if (url.IsLocal() || StringUtils::EqualsNoCase(protocol, "plugin"))
377           verifiedPaths.push_back(*path);
378         else
379           bIsInvalid = true;
380       }
381       // for others allow everything (if the user does something silly, we can't stop them)
382       else
383         verifiedPaths.push_back(*path);
384
385       // error message
386       if (bIsInvalid)
387         CLog::Log(LOGERROR,"CMediaSourceSettings:    invalid path type (%s) for multipath source", path->c_str());
388     }
389
390     // no valid paths? skip to next source
391     if (verifiedPaths.empty())
392     {
393       CLog::Log(LOGERROR,"CMediaSourceSettings:    missing or invalid <name> and/or <path> in source");
394       return false;
395     }
396   }
397
398   share.FromNameAndPaths(category, strName, verifiedPaths);
399
400   share.m_iBadPwdCount = 0;
401   if (pLockMode)
402   {
403     share.m_iLockMode = (LockType)strtol(pLockMode->FirstChild()->Value(), NULL, 10);
404     share.m_iHasLock = 2;
405   }
406
407   if (pLockCode && pLockCode->FirstChild())
408     share.m_strLockCode = pLockCode->FirstChild()->Value();
409
410   if (pBadPwdCount && pBadPwdCount->FirstChild())
411     share.m_iBadPwdCount = (int)strtol(pBadPwdCount->FirstChild()->Value(), NULL, 10);
412
413   if (pThumbnailNode && pThumbnailNode->FirstChild())
414     share.m_strThumbnailImage = pThumbnailNode->FirstChild()->Value();
415
416   XMLUtils::GetBoolean(source, "allowsharing", share.m_allowSharing);
417
418   return true;
419 }
420
421 void CMediaSourceSettings::GetSources(const TiXmlNode* pRootElement, const std::string& strTagName, VECSOURCES& items, std::string& strDefault)
422 {
423   strDefault = "";
424   items.clear();
425
426   const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str());
427   if (pChild == NULL)
428   {
429     CLog::Log(LOGDEBUG, "CMediaSourceSettings: <%s> tag is missing or sources.xml is malformed", strTagName.c_str());
430     return;
431   }
432
433   pChild = pChild->FirstChild();
434   while (pChild != NULL)
435   {
436     std::string strValue = pChild->ValueStr();
437     if (strValue == XML_SOURCE || strValue == "bookmark") // "bookmark" left in for backwards compatibility
438     {
439       CMediaSource share;
440       if (GetSource(strTagName, pChild, share))
441         items.push_back(share);
442       else
443         CLog::Log(LOGERROR, "CMediaSourceSettings:    Missing or invalid <name> and/or <path> in source");
444     }
445     else if (strValue == "default")
446     {
447       const TiXmlNode *pValueNode = pChild->FirstChild();
448       if (pValueNode)
449       {
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());
454       }
455     }
456
457     pChild = pChild->NextSibling();
458   }
459 }
460
461 bool CMediaSourceSettings::SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const std::string &defaultPath) const
462 {
463   TiXmlElement sectionElement(section);
464   TiXmlNode *sectionNode = root->InsertEndChild(sectionElement);
465   if (sectionNode == NULL)
466     return false;
467
468   XMLUtils::SetPath(sectionNode, "default", defaultPath);
469   for (CIVECSOURCES it = shares.begin(); it != shares.end(); it++)
470   {
471     const CMediaSource &share = *it;
472     if (share.m_ignore)
473       continue;
474
475     TiXmlElement source(XML_SOURCE);
476     XMLUtils::SetString(&source, "name", share.strName);
477
478     for (unsigned int i = 0; i < share.vecPaths.size(); i++)
479       XMLUtils::SetPath(&source, "path", share.vecPaths[i]);
480
481     if (share.m_iHasLock)
482     {
483       XMLUtils::SetInt(&source, "lockmode", share.m_iLockMode);
484       XMLUtils::SetString(&source, "lockcode", share.m_strLockCode);
485       XMLUtils::SetInt(&source, "badpwdcount", share.m_iBadPwdCount);
486     }
487
488     if (!share.m_strThumbnailImage.empty())
489       XMLUtils::SetPath(&source, "thumbnail", share.m_strThumbnailImage);
490
491     XMLUtils::SetBoolean(&source, "allowsharing", share.m_allowSharing);
492
493     sectionNode->InsertEndChild(source);
494   }
495
496   return true;
497 }