1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "SettingsComponent.h"
10 
11 #include "AdvancedSettings.h"
12 #include "AppParamParser.h"
13 #include "CompileInfo.h"
14 #include "ServiceBroker.h"
15 #include "Settings.h"
16 #include "Util.h"
17 #include "filesystem/Directory.h"
18 #include "filesystem/SpecialProtocol.h"
19 #ifdef TARGET_DARWIN_EMBEDDED
20 #include "platform/darwin/ios-common/DarwinEmbedUtils.h"
21 #endif
22 #ifdef TARGET_WINDOWS
23 #include "platform/Environment.h"
24 #endif
25 #include "profiles/ProfileManager.h"
26 #include "utils/log.h"
27 #include "utils/StringUtils.h"
28 #include "utils/URIUtils.h"
29 #ifdef TARGET_WINDOWS
30 #include "win32util.h"
31 #endif
32 
CSettingsComponent()33 CSettingsComponent::CSettingsComponent()
34 {
35   m_advancedSettings.reset(new CAdvancedSettings());
36   m_settings.reset(new CSettings());
37   m_profileManager.reset(new CProfileManager());
38 }
39 
~CSettingsComponent()40 CSettingsComponent::~CSettingsComponent()
41 {
42   Deinit();
43 }
44 
Init(const CAppParamParser & params)45 void CSettingsComponent::Init(const CAppParamParser &params)
46 {
47   if (m_state == State::DEINITED)
48   {
49     // only the InitDirectories* for the current platform should return true
50     bool inited = InitDirectoriesLinux(params.m_platformDirectories);
51     if (!inited)
52       inited = InitDirectoriesOSX(params.m_platformDirectories);
53     if (!inited)
54       inited = InitDirectoriesWin32(params.m_platformDirectories);
55 
56     m_settings->Initialize();
57 
58     m_advancedSettings->Initialize(params, *m_settings->GetSettingsManager());
59     URIUtils::RegisterAdvancedSettings(*m_advancedSettings);
60 
61     m_profileManager->Initialize(m_settings);
62 
63     CServiceBroker::RegisterSettingsComponent(this);
64 
65     m_state = State::INITED;
66   }
67 }
68 
Load()69 bool CSettingsComponent::Load()
70 {
71   if (m_state == State::INITED)
72   {
73     if (!m_profileManager->Load())
74     {
75       CLog::Log(LOGFATAL, "unable to load profile");
76       return false;
77     }
78 
79     CSpecialProtocol::RegisterProfileManager(*m_profileManager);
80     XFILE::IDirectory::RegisterProfileManager(*m_profileManager);
81 
82     if (!m_settings->Load())
83     {
84       CLog::Log(LOGFATAL, "unable to load settings");
85       return false;
86     }
87 
88     m_settings->SetLoaded();
89 
90     m_state = State::LOADED;
91     return true;
92   }
93   else if (m_state == State::LOADED)
94   {
95     return true;
96   }
97   else
98   {
99     return false;
100   }
101 }
102 
Deinit()103 void CSettingsComponent::Deinit()
104 {
105   if (m_state >= State::INITED)
106   {
107     CServiceBroker::UnregisterSettingsComponent();
108 
109     if (m_state == State::LOADED)
110     {
111       m_settings->Unload();
112 
113       XFILE::IDirectory::UnregisterProfileManager();
114       CSpecialProtocol::UnregisterProfileManager();
115     }
116     m_profileManager->Uninitialize();
117 
118     URIUtils::UnregisterAdvancedSettings();
119     m_advancedSettings->Uninitialize(*m_settings->GetSettingsManager());
120 
121     m_settings->Uninitialize();
122   }
123   m_state = State::DEINITED;
124 }
125 
GetSettings()126 std::shared_ptr<CSettings> CSettingsComponent::GetSettings()
127 {
128   return m_settings;
129 }
130 
GetAdvancedSettings()131 std::shared_ptr<CAdvancedSettings> CSettingsComponent::GetAdvancedSettings()
132 {
133   return m_advancedSettings;
134 }
135 
GetProfileManager()136 std::shared_ptr<CProfileManager> CSettingsComponent::GetProfileManager()
137 {
138   return m_profileManager;
139 }
140 
InitDirectoriesLinux(bool bPlatformDirectories)141 bool CSettingsComponent::InitDirectoriesLinux(bool bPlatformDirectories)
142 {
143   /*
144    The following is the directory mapping for Platform Specific Mode:
145 
146    special://xbmc/          => [read-only] system directory (/usr/share/kodi)
147    special://home/          => [read-write] user's directory that will override special://kodi/ system-wide
148    installations like skins, screensavers, etc.
149    ($HOME/.kodi)
150    NOTE: XBMC will look in both special://xbmc/addons and special://home/addons for addons.
151    special://masterprofile/ => [read-write] userdata of master profile. It will by default be
152    mapped to special://home/userdata ($HOME/.kodi/userdata)
153    special://profile/       => [read-write] current profile's userdata directory.
154    Generally special://masterprofile for the master profile or
155    special://masterprofile/profiles/<profile_name> for other profiles.
156 
157    NOTE: All these root directories are lowercase. Some of the sub-directories
158    might be mixed case.
159    */
160 
161 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
162   std::string appPath;
163   std::string appName = CCompileInfo::GetAppName();
164   std::string dotLowerAppName = "." + appName;
165   StringUtils::ToLower(dotLowerAppName);
166   const char* envAppHome = "KODI_HOME";
167   const char* envAppBinHome = "KODI_BIN_HOME";
168   const char* envAppTemp = "KODI_TEMP";
169 
170   std::string userName;
171   if (getenv("USER"))
172     userName = getenv("USER");
173   else
174     userName = "root";
175 
176   std::string userHome;
177   if (getenv("KODI_DATA"))
178     userHome = getenv("KODI_DATA");
179   else if (getenv("HOME"))
180   {
181     userHome = getenv("HOME");
182     userHome.append("/" + dotLowerAppName);
183   }
184   else
185   {
186     userHome = "/root";
187     userHome.append("/" + dotLowerAppName);
188   }
189 
190   std::string strTempPath;
191   if (getenv(envAppTemp))
192     strTempPath = getenv(envAppTemp);
193   else
194     strTempPath = userHome + "/temp";
195 
196 
197   std::string binaddonAltDir;
198   if (getenv("KODI_BINADDON_PATH"))
199     binaddonAltDir = getenv("KODI_BINADDON_PATH");
200 
201   auto appBinPath = CUtil::GetHomePath(envAppBinHome);
202   // overridden by user
203   if (getenv(envAppHome))
204     appPath = getenv(envAppHome);
205   else
206   {
207     // use build time default
208     appPath = INSTALL_PATH;
209     /* Check if binaries and arch independent data files are being kept in
210      * separate locations. */
211     if (!XFILE::CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "userdata")))
212     {
213       /* Attempt to locate arch independent data files. */
214       appPath = CUtil::GetHomePath(appBinPath);
215       if (!XFILE::CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "userdata")))
216       {
217         fprintf(stderr, "Unable to find path to %s data files!\n", appName.c_str());
218         exit(1);
219       }
220     }
221   }
222 
223   /* Set some environment variables */
224   setenv(envAppBinHome, appBinPath.c_str(), 0);
225   setenv(envAppHome, appPath.c_str(), 0);
226 
227   if (bPlatformDirectories)
228   {
229     // map our special drives
230     CSpecialProtocol::SetXBMCBinPath(appBinPath);
231     CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
232     CSpecialProtocol::SetXBMCPath(appPath);
233     CSpecialProtocol::SetHomePath(userHome);
234     CSpecialProtocol::SetMasterProfilePath(userHome + "/userdata");
235     CSpecialProtocol::SetTempPath(strTempPath);
236     CSpecialProtocol::SetLogPath(strTempPath);
237 
238     CreateUserDirs();
239 
240   }
241   else
242   {
243     URIUtils::AddSlashAtEnd(appPath);
244 
245     CSpecialProtocol::SetXBMCBinPath(appBinPath);
246     CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
247     CSpecialProtocol::SetXBMCPath(appPath);
248     CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data"));
249     CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata"));
250 
251     std::string strTempPath = appPath;
252     strTempPath = URIUtils::AddFileToFolder(strTempPath, "portable_data/temp");
253     if (getenv(envAppTemp))
254       strTempPath = getenv(envAppTemp);
255     CSpecialProtocol::SetTempPath(strTempPath);
256     CSpecialProtocol::SetLogPath(strTempPath);
257     CreateUserDirs();
258   }
259   CSpecialProtocol::SetXBMCBinAddonPath(appBinPath + "/addons");
260 
261   return true;
262 #else
263   return false;
264 #endif
265 }
266 
InitDirectoriesOSX(bool bPlatformDirectories)267 bool CSettingsComponent::InitDirectoriesOSX(bool bPlatformDirectories)
268 {
269 #if defined(TARGET_DARWIN)
270   std::string userName;
271   if (getenv("USER"))
272     userName = getenv("USER");
273   else
274     userName = "root";
275 
276   std::string userHome;
277   if (getenv("HOME"))
278     userHome = getenv("HOME");
279   else
280     userHome = "/root";
281 
282   std::string binaddonAltDir;
283   if (getenv("KODI_BINADDON_PATH"))
284     binaddonAltDir = getenv("KODI_BINADDON_PATH");
285 
286   std::string appPath = CUtil::GetHomePath();
287   setenv("KODI_HOME", appPath.c_str(), 0);
288 
289 #if defined(TARGET_DARWIN_EMBEDDED)
290   std::string fontconfigPath;
291   fontconfigPath = appPath + "/system/players/VideoPlayer/etc/fonts/fonts.conf";
292   setenv("FONTCONFIG_FILE", fontconfigPath.c_str(), 0);
293 #endif
294 
295   // setup path to our internal dylibs so loader can find them
296   std::string frameworksPath = CUtil::GetFrameworksPath();
297   CSpecialProtocol::SetXBMCFrameworksPath(frameworksPath);
298 
299   if (bPlatformDirectories)
300   {
301     // map our special drives
302     CSpecialProtocol::SetXBMCBinPath(appPath);
303     CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
304     CSpecialProtocol::SetXBMCPath(appPath);
305 #if defined(TARGET_DARWIN_EMBEDDED)
306     std::string appName = CCompileInfo::GetAppName();
307     CSpecialProtocol::SetHomePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() + "/" +
308                                   appName);
309     CSpecialProtocol::SetMasterProfilePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() +
310                                            "/" + appName + "/userdata");
311 #else
312     std::string appName = CCompileInfo::GetAppName();
313     CSpecialProtocol::SetHomePath(userHome + "/Library/Application Support/" + appName);
314     CSpecialProtocol::SetMasterProfilePath(userHome + "/Library/Application Support/" + appName + "/userdata");
315 #endif
316 
317     std::string dotLowerAppName = "." + appName;
318     StringUtils::ToLower(dotLowerAppName);
319     // location for temp files
320 #if defined(TARGET_DARWIN_EMBEDDED)
321     std::string strTempPath = URIUtils::AddFileToFolder(
322         userHome, std::string(CDarwinEmbedUtils::GetAppRootFolder()) + "/" + appName + "/temp");
323 #else
324     std::string strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/");
325     XFILE::CDirectory::Create(strTempPath);
326     strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/temp");
327 #endif
328     CSpecialProtocol::SetTempPath(strTempPath);
329 
330     // xbmc.log file location
331 #if defined(TARGET_DARWIN_EMBEDDED)
332     strTempPath = userHome + "/" + std::string(CDarwinEmbedUtils::GetAppRootFolder());
333 #else
334     strTempPath = userHome + "/Library/Logs";
335 #endif
336     CSpecialProtocol::SetLogPath(strTempPath);
337     CreateUserDirs();
338   }
339   else
340   {
341     URIUtils::AddSlashAtEnd(appPath);
342 
343     CSpecialProtocol::SetXBMCBinPath(appPath);
344     CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
345     CSpecialProtocol::SetXBMCPath(appPath);
346     CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data"));
347     CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata"));
348 
349     std::string strTempPath = URIUtils::AddFileToFolder(appPath, "portable_data/temp");
350     CSpecialProtocol::SetTempPath(strTempPath);
351     CSpecialProtocol::SetLogPath(strTempPath);
352     CreateUserDirs();
353   }
354   CSpecialProtocol::SetXBMCBinAddonPath(appPath + "/addons");
355   return true;
356 #else
357   return false;
358 #endif
359 }
360 
InitDirectoriesWin32(bool bPlatformDirectories)361 bool CSettingsComponent::InitDirectoriesWin32(bool bPlatformDirectories)
362 {
363 #ifdef TARGET_WINDOWS
364   std::string xbmcPath = CUtil::GetHomePath();
365   CEnvironment::setenv("KODI_HOME", xbmcPath);
366   CSpecialProtocol::SetXBMCBinPath(xbmcPath);
367   CSpecialProtocol::SetXBMCPath(xbmcPath);
368   CSpecialProtocol::SetXBMCBinAddonPath(xbmcPath + "/addons");
369 
370   std::string strWin32UserFolder = CWIN32Util::GetProfilePath();
371   CSpecialProtocol::SetLogPath(strWin32UserFolder);
372   CSpecialProtocol::SetHomePath(strWin32UserFolder);
373   CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(strWin32UserFolder, "userdata"));
374   CSpecialProtocol::SetTempPath(URIUtils::AddFileToFolder(strWin32UserFolder,"cache"));
375 
376   CEnvironment::setenv("KODI_PROFILE_USERDATA", CSpecialProtocol::TranslatePath("special://masterprofile/"));
377 
378   CreateUserDirs();
379 
380   return true;
381 #else
382   return false;
383 #endif
384 }
385 
CreateUserDirs() const386 void CSettingsComponent::CreateUserDirs() const
387 {
388   XFILE::CDirectory::Create("special://home/");
389   XFILE::CDirectory::Create("special://home/addons");
390   XFILE::CDirectory::Create("special://home/addons/packages");
391   XFILE::CDirectory::Create("special://home/addons/temp");
392   XFILE::CDirectory::Create("special://home/media");
393   XFILE::CDirectory::Create("special://home/system");
394   XFILE::CDirectory::Create("special://masterprofile/");
395   XFILE::CDirectory::Create("special://temp/");
396   XFILE::CDirectory::Create("special://logpath");
397   XFILE::CDirectory::Create("special://temp/temp"); // temp directory for python and dllGetTempPathA
398 
399   //Let's clear our archive cache before starting up anything more
400   auto archiveCachePath = CSpecialProtocol::TranslatePath("special://temp/archive_cache/");
401   if (XFILE::CDirectory::Exists(archiveCachePath))
402     if (!XFILE::CDirectory::RemoveRecursive(archiveCachePath))
403       CLog::Log(LOGWARNING, "Failed to remove the archive cache at %s", archiveCachePath.c_str());
404   XFILE::CDirectory::Create(archiveCachePath);
405 
406 }
407