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 ¶ms)
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