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 <limits.h>
10 
11 #include "threads/SystemClock.h"
12 #include "SystemInfo.h"
13 #ifndef TARGET_POSIX
14 #include <conio.h>
15 #else
16 #include <sys/utsname.h>
17 #endif
18 #include "CompileInfo.h"
19 #include "ServiceBroker.h"
20 #include "filesystem/CurlFile.h"
21 #include "filesystem/File.h"
22 #include "guilib/LocalizeStrings.h"
23 #include "guilib/guiinfo/GUIInfoLabels.h"
24 #include "network/Network.h"
25 #include "platform/Filesystem.h"
26 #include "rendering/RenderSystem.h"
27 #include "settings/Settings.h"
28 #include "settings/SettingsComponent.h"
29 #include "utils/CPUInfo.h"
30 #include "utils/log.h"
31 
32 #ifdef TARGET_WINDOWS
33 #include <dwmapi.h>
34 #include "utils/CharsetConverter.h"
35 #include <VersionHelpers.h>
36 
37 #ifdef TARGET_WINDOWS_STORE
38 #include <winrt/Windows.Security.ExchangeActiveSyncProvisioning.h>
39 #include <winrt/Windows.System.Profile.h>
40 
41 using namespace winrt::Windows::ApplicationModel;
42 using namespace winrt::Windows::Security::ExchangeActiveSyncProvisioning;
43 using namespace winrt::Windows::System;
44 using namespace winrt::Windows::System::Profile;
45 #endif
46 #include <wincrypt.h>
47 #include "platform/win32/CharsetConverter.h"
48 #endif
49 #if defined(TARGET_DARWIN)
50 #include "platform/darwin/DarwinUtils.h"
51 #endif
52 #include "powermanagement/PowerManager.h"
53 #include "utils/StringUtils.h"
54 #include "utils/XMLUtils.h"
55 #if defined(TARGET_ANDROID)
56 #include <androidjni/Build.h>
57 #endif
58 
59 /* Platform identification */
60 #if defined(TARGET_DARWIN)
61 #include <Availability.h>
62 #include <mach-o/arch.h>
63 #include <sys/sysctl.h>
64 #include "utils/auto_buffer.h"
65 #elif defined(TARGET_ANDROID)
66 #include <android/api-level.h>
67 #include <sys/system_properties.h>
68 #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
69 #include <sys/param.h>
70 #elif defined(TARGET_LINUX)
71 #include "platform/linux/SysfsPath.h"
72 
73 #include <linux/version.h>
74 #endif
75 
76 #include <system_error>
77 
78 /* Expand macro before stringify */
79 #define STR_MACRO(x) #x
80 #define XSTR_MACRO(x) STR_MACRO(x)
81 
82 using namespace XFILE;
83 
84 #ifdef TARGET_WINDOWS_DESKTOP
sysGetVersionExWByRef(OSVERSIONINFOEXW & osVerInfo)85 static bool sysGetVersionExWByRef(OSVERSIONINFOEXW& osVerInfo)
86 {
87   ZeroMemory(&osVerInfo, sizeof(osVerInfo));
88   osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
89 
90   typedef NTSTATUS(__stdcall *RtlGetVersionPtr)(RTL_OSVERSIONINFOEXW* pOsInfo);
91   static HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
92   if (hNtDll != NULL)
93   {
94     static RtlGetVersionPtr RtlGetVer = (RtlGetVersionPtr) GetProcAddress(hNtDll, "RtlGetVersion");
95     if (RtlGetVer && RtlGetVer(&osVerInfo) == 0)
96       return true;
97   }
98   // failed to get OS information directly from ntdll.dll
99   // use GetVersionExW() as fallback
100   // note: starting from Windows 8.1 GetVersionExW() may return unfaithful information
101   if (GetVersionExW((OSVERSIONINFOW*) &osVerInfo) != 0)
102       return true;
103 
104   ZeroMemory(&osVerInfo, sizeof(osVerInfo));
105   return false;
106 }
107 
appendWindows10NameVersion(std::string & osNameVer)108 static bool appendWindows10NameVersion(std::string& osNameVer)
109 {
110   wchar_t versionW[32] = {};
111   DWORD len = sizeof(versionW);
112   bool obtained = false;
113   if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"DisplayVersion",
114                                     RRF_RT_REG_SZ, nullptr, &versionW, &len))
115   {
116     obtained = true;
117   }
118   else if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"ReleaseId",
119                                          RRF_RT_REG_SZ, nullptr, &versionW, &len))
120   {
121     obtained = true;
122   }
123   if (obtained)
124     osNameVer.append(StringUtils::Format(" {}", KODI::PLATFORM::WINDOWS::FromW(versionW)));
125 
126   return obtained;
127 }
128 #endif // TARGET_WINDOWS_DESKTOP
129 
130 #if defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
getValueFromOs_release(std::string key)131 static std::string getValueFromOs_release(std::string key)
132 {
133   FILE* os_rel = fopen("/etc/os-release", "r");
134   if (!os_rel)
135     return "";
136 
137   char* buf = new char[10 * 1024]; // more than enough
138   size_t len = fread(buf, 1, 10 * 1024, os_rel);
139   fclose(os_rel);
140   if (len == 0)
141   {
142     delete[] buf;
143     return "";
144   }
145 
146   std::string content(buf, len);
147   delete[] buf;
148 
149   // find begin of value string
150   size_t valStart = 0, seachPos;
151   key += '=';
152   if (content.compare(0, key.length(), key) == 0)
153     valStart = key.length();
154   else
155   {
156     key = "\n" + key;
157     seachPos = 0;
158     do
159     {
160       seachPos = content.find(key, seachPos);
161       if (seachPos == std::string::npos)
162         return "";
163       if (seachPos == 0 || content[seachPos - 1] != '\\')
164         valStart = seachPos + key.length();
165       else
166         seachPos++;
167     } while (valStart == 0);
168   }
169 
170   if (content[valStart] == '\n')
171     return "";
172 
173   // find end of value string
174   seachPos = valStart;
175   do
176   {
177     seachPos = content.find('\n', seachPos + 1);
178   } while (seachPos != std::string::npos && content[seachPos - 1] == '\\');
179   size_t const valEnd = seachPos;
180 
181   std::string value(content, valStart, valEnd - valStart);
182   if (value.empty())
183     return value;
184 
185   // remove quotes
186   if (value[0] == '\'' || value[0] == '"')
187   {
188     if (value.length() < 2)
189       return value;
190     size_t qEnd = value.rfind(value[0]);
191     if (qEnd != std::string::npos)
192     {
193       value.erase(qEnd);
194       value.erase(0, 1);
195     }
196   }
197 
198   // unescape characters
199   for (size_t slashPos = value.find('\\'); slashPos < value.length() - 1; slashPos = value.find('\\', slashPos))
200   {
201     if (value[slashPos + 1] == '\n')
202       value.erase(slashPos, 2);
203     else
204     {
205       value.erase(slashPos, 1);
206       slashPos++; // skip unescaped character
207     }
208   }
209 
210   return value;
211 }
212 
213 enum lsb_rel_info_type
214 {
215   lsb_rel_distributor,
216   lsb_rel_description,
217   lsb_rel_release,
218   lsb_rel_codename
219 };
220 
getValueFromLsb_release(enum lsb_rel_info_type infoType)221 static std::string getValueFromLsb_release(enum lsb_rel_info_type infoType)
222 {
223   std::string key, command("unset PYTHONHOME; unset PYTHONPATH; lsb_release ");
224   switch (infoType)
225   {
226   case lsb_rel_distributor:
227     command += "-i";
228     key = "Distributor ID:\t";
229     break;
230   case lsb_rel_description:
231     command += "-d";
232     key = "Description:\t";
233     break;
234   case lsb_rel_release:
235     command += "-r";
236     key = "Release:\t";
237     break;
238   case lsb_rel_codename:
239     command += "-c";
240     key = "Codename:\t";
241     break;
242   default:
243     return "";
244   }
245   command += " 2>/dev/null";
246   FILE* lsb_rel = popen(command.c_str(), "r");
247   if (lsb_rel == NULL)
248     return "";
249 
250   char buf[300]; // more than enough
251   if (fgets(buf, 300, lsb_rel) == NULL)
252   {
253     pclose(lsb_rel);
254     return "";
255   }
256   pclose(lsb_rel);
257 
258   std::string response(buf);
259   if (response.compare(0, key.length(), key) != 0)
260     return "";
261 
262   return response.substr(key.length(), response.find('\n') - key.length());
263 }
264 #endif // TARGET_LINUX && !TARGET_ANDROID
265 
266 CSysInfo g_sysinfo;
267 
268 CSysInfoJob::CSysInfoJob() = default;
269 
DoWork()270 bool CSysInfoJob::DoWork()
271 {
272   m_info.systemUptime      = GetSystemUpTime(false);
273   m_info.systemTotalUptime = GetSystemUpTime(true);
274   m_info.internetState     = GetInternetState();
275   m_info.videoEncoder      = GetVideoEncoder();
276   m_info.cpuFrequency =
277       StringUtils::Format("%4.0f MHz", CServiceBroker::GetCPUInfo()->GetCPUFrequency());
278   m_info.osVersionInfo     = CSysInfo::GetOsPrettyNameWithVersion() + " (kernel: " + CSysInfo::GetKernelName() + " " + CSysInfo::GetKernelVersionFull() + ")";
279   m_info.macAddress        = GetMACAddress();
280   m_info.batteryLevel      = GetBatteryLevel();
281   return true;
282 }
283 
GetData() const284 const CSysData &CSysInfoJob::GetData() const
285 {
286   return m_info;
287 }
288 
GetInternetState()289 CSysData::INTERNET_STATE CSysInfoJob::GetInternetState()
290 {
291   // Internet connection state!
292   XFILE::CCurlFile http;
293   if (http.IsInternet())
294     return CSysData::CONNECTED;
295   return CSysData::DISCONNECTED;
296 }
297 
GetMACAddress()298 std::string CSysInfoJob::GetMACAddress()
299 {
300   CNetworkInterface* iface = CServiceBroker::GetNetwork().GetFirstConnectedInterface();
301   if (iface)
302     return iface->GetMacAddress();
303 
304   return "";
305 }
306 
GetVideoEncoder()307 std::string CSysInfoJob::GetVideoEncoder()
308 {
309   return "GPU: " + CServiceBroker::GetRenderSystem()->GetRenderRenderer();
310 }
311 
GetBatteryLevel()312 std::string CSysInfoJob::GetBatteryLevel()
313 {
314   return StringUtils::Format("%d%%", CServiceBroker::GetPowerManager().BatteryLevel());
315 }
316 
SystemUpTime(int iInputMinutes,int & iMinutes,int & iHours,int & iDays)317 bool CSysInfoJob::SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays)
318 {
319   iHours = 0; iDays = 0;
320   iMinutes = iInputMinutes;
321   if (iMinutes >= 60) // Hour's
322   {
323     iHours = iMinutes / 60;
324     iMinutes = iMinutes - (iHours *60);
325   }
326   if (iHours >= 24) // Days
327   {
328     iDays = iHours / 24;
329     iHours = iHours - (iDays * 24);
330   }
331   return true;
332 }
333 
GetSystemUpTime(bool bTotalUptime)334 std::string CSysInfoJob::GetSystemUpTime(bool bTotalUptime)
335 {
336   std::string strSystemUptime;
337   int iInputMinutes, iMinutes,iHours,iDays;
338 
339   if(bTotalUptime)
340   {
341     //Total Uptime
342     iInputMinutes = g_sysinfo.GetTotalUptime() + ((int)(XbmcThreads::SystemClockMillis() / 60000));
343   }
344   else
345   {
346     //Current UpTime
347     iInputMinutes = (int)(XbmcThreads::SystemClockMillis() / 60000);
348   }
349 
350   SystemUpTime(iInputMinutes,iMinutes, iHours, iDays);
351   if (iDays > 0)
352   {
353     strSystemUptime = StringUtils::Format("%i %s, %i %s, %i %s",
354                                           iDays, g_localizeStrings.Get(12393).c_str(),
355                                           iHours, g_localizeStrings.Get(12392).c_str(),
356                                           iMinutes, g_localizeStrings.Get(12391).c_str());
357   }
358   else if (iDays == 0 && iHours >= 1 )
359   {
360     strSystemUptime = StringUtils::Format("%i %s, %i %s",
361                                           iHours, g_localizeStrings.Get(12392).c_str(),
362                                           iMinutes, g_localizeStrings.Get(12391).c_str());
363   }
364   else if (iDays == 0 && iHours == 0 &&  iMinutes >= 0)
365   {
366     strSystemUptime = StringUtils::Format("%i %s",
367                                           iMinutes, g_localizeStrings.Get(12391).c_str());
368   }
369   return strSystemUptime;
370 }
371 
TranslateInfo(int info) const372 std::string CSysInfo::TranslateInfo(int info) const
373 {
374   switch(info)
375   {
376   case SYSTEM_VIDEO_ENCODER_INFO:
377     return m_info.videoEncoder;
378   case NETWORK_MAC_ADDRESS:
379     return m_info.macAddress;
380   case SYSTEM_OS_VERSION_INFO:
381     return m_info.osVersionInfo;
382   case SYSTEM_CPUFREQUENCY:
383     return m_info.cpuFrequency;
384   case SYSTEM_UPTIME:
385     return m_info.systemUptime;
386   case SYSTEM_TOTALUPTIME:
387     return m_info.systemTotalUptime;
388   case SYSTEM_INTERNET_STATE:
389     if (m_info.internetState == CSysData::CONNECTED)
390       return g_localizeStrings.Get(13296);
391     else
392       return g_localizeStrings.Get(13297);
393   case SYSTEM_BATTERY_LEVEL:
394     return m_info.batteryLevel;
395   default:
396     return "";
397   }
398 }
399 
Reset()400 void CSysInfo::Reset()
401 {
402   m_info.Reset();
403 }
404 
CSysInfo(void)405 CSysInfo::CSysInfo(void) : CInfoLoader(15 * 1000)
406 {
407   memset(MD5_Sign, 0, sizeof(MD5_Sign));
408   m_iSystemTimeTotalUp = 0;
409 }
410 
411 CSysInfo::~CSysInfo() = default;
412 
Load(const TiXmlNode * settings)413 bool CSysInfo::Load(const TiXmlNode *settings)
414 {
415   if (settings == NULL)
416     return false;
417 
418   const TiXmlElement *pElement = settings->FirstChildElement("general");
419   if (pElement)
420     XMLUtils::GetInt(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, INT_MAX);
421 
422   return true;
423 }
424 
Save(TiXmlNode * settings) const425 bool CSysInfo::Save(TiXmlNode *settings) const
426 {
427   if (settings == NULL)
428     return false;
429 
430   TiXmlNode *generalNode = settings->FirstChild("general");
431   if (generalNode == NULL)
432   {
433     TiXmlElement generalNodeNew("general");
434     generalNode = settings->InsertEndChild(generalNodeNew);
435     if (generalNode == NULL)
436       return false;
437   }
438   XMLUtils::SetInt(generalNode, "systemtotaluptime", m_iSystemTimeTotalUp);
439 
440   return true;
441 }
442 
GetAppName(void)443 const std::string& CSysInfo::GetAppName(void)
444 {
445   assert(CCompileInfo::GetAppName() != NULL);
446   static const std::string appName(CCompileInfo::GetAppName());
447 
448   return appName;
449 }
450 
GetDiskSpace(std::string drive,int & iTotal,int & iTotalFree,int & iTotalUsed,int & iPercentFree,int & iPercentUsed)451 bool CSysInfo::GetDiskSpace(std::string drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed)
452 {
453   using namespace KODI::PLATFORM::FILESYSTEM;
454 
455   space_info total = { 0 };
456   std::error_code ec;
457 
458   // None of this makes sense but the idea of total space
459   // makes no sense on any system really.
460   // Return space for / or for C: as it's correct in a sense
461   // and not much worse than trying to count a total for different
462   // drives/mounts
463   if (drive.empty() || drive == "*")
464   {
465 #if defined(TARGET_WINDOWS)
466     drive = "C";
467 #elif defined(TARGET_POSIX)
468     drive = "/";
469 #endif
470   }
471 
472 #ifdef TARGET_WINDOWS_DESKTOP
473   using KODI::PLATFORM::WINDOWS::ToW;
474   UINT uidriveType = GetDriveType(ToW(drive + ":\\").c_str());
475   if (uidriveType != DRIVE_UNKNOWN && uidriveType != DRIVE_NO_ROOT_DIR)
476     total = space(drive + ":\\", ec);
477 #elif defined(TARGET_POSIX)
478   total = space(drive, ec);
479 #endif
480   if (ec.value() != 0)
481     return false;
482 
483   iTotal = static_cast<int>(total.capacity / MB);
484   iTotalFree = static_cast<int>(total.free / MB);
485   iTotalUsed = iTotal - iTotalFree;
486   if (total.capacity > 0)
487     iPercentUsed = static_cast<int>(100.0f * (total.capacity - total.free) / total.capacity + 0.5f);
488   else
489     iPercentUsed = 0;
490 
491   iPercentFree = 100 - iPercentUsed;
492 
493   return true;
494 }
495 
GetKernelName(bool emptyIfUnknown)496 std::string CSysInfo::GetKernelName(bool emptyIfUnknown /*= false*/)
497 {
498   static std::string kernelName;
499   if (kernelName.empty())
500   {
501 #if defined(TARGET_WINDOWS_DESKTOP)
502     OSVERSIONINFOEXW osvi;
503     if (sysGetVersionExWByRef(osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
504       kernelName = "Windows NT";
505 #elif defined(TARGET_WINDOWS_STORE)
506     auto e = EasClientDeviceInformation();
507     auto os = e.OperatingSystem();
508     g_charsetConverter.wToUTF8(std::wstring(os.c_str()), kernelName);
509 #elif defined(TARGET_POSIX)
510     struct utsname un;
511     if (uname(&un) == 0)
512       kernelName.assign(un.sysname);
513 #endif // defined(TARGET_POSIX)
514 
515     if (kernelName.empty())
516       kernelName = "Unknown kernel"; // can't detect
517   }
518 
519   if (emptyIfUnknown && kernelName == "Unknown kernel")
520     return "";
521 
522   return kernelName;
523 }
524 
GetKernelVersionFull(void)525 std::string CSysInfo::GetKernelVersionFull(void)
526 {
527   static std::string kernelVersionFull;
528   if (!kernelVersionFull.empty())
529     return kernelVersionFull;
530 
531 #if defined(TARGET_WINDOWS_DESKTOP)
532   OSVERSIONINFOEXW osvi = {};
533   DWORD dwBuildRevision = 0;
534   DWORD len = sizeof(DWORD);
535 
536   if (sysGetVersionExWByRef(osvi))
537     kernelVersionFull = StringUtils::Format("%d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion,
538                                             osvi.dwBuildNumber);
539   // get UBR (updates build revision)
540   if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"UBR",
541                                     RRF_RT_REG_DWORD, nullptr, &dwBuildRevision, &len))
542   {
543     kernelVersionFull += StringUtils::Format(".%d", dwBuildRevision);
544   }
545 
546 #elif  defined(TARGET_WINDOWS_STORE)
547   // get the system version number
548   auto sv = AnalyticsInfo::VersionInfo().DeviceFamilyVersion();
549   wchar_t* end;
550   unsigned long long  v = wcstoull(sv.c_str(), &end, 10);
551   unsigned long long v1 = (v & 0xFFFF000000000000L) >> 48;
552   unsigned long long v2 = (v & 0x0000FFFF00000000L) >> 32;
553   unsigned long long v3 = (v & 0x00000000FFFF0000L) >> 16;
554   unsigned long long v4 = (v & 0x000000000000FFFFL);
555   kernelVersionFull = StringUtils::Format("%lld.%lld.%lld", v1, v2, v3);
556   if (v4)
557     kernelVersionFull += StringUtils::Format(".%lld", v4);
558 
559 #elif defined(TARGET_POSIX)
560   struct utsname un;
561   if (uname(&un) == 0)
562     kernelVersionFull.assign(un.release);
563 #endif // defined(TARGET_POSIX)
564 
565   if (kernelVersionFull.empty())
566     kernelVersionFull = "0.0.0"; // can't detect
567 
568   return kernelVersionFull;
569 }
570 
GetKernelVersion(void)571 std::string CSysInfo::GetKernelVersion(void)
572 {
573   static std::string kernelVersionClear;
574   if (kernelVersionClear.empty())
575   {
576     kernelVersionClear = GetKernelVersionFull();
577     const size_t erasePos = kernelVersionClear.find_first_not_of("0123456789.");
578     if (erasePos != std::string::npos)
579       kernelVersionClear.erase(erasePos);
580   }
581 
582   return kernelVersionClear;
583 }
584 
GetOsName(bool emptyIfUnknown)585 std::string CSysInfo::GetOsName(bool emptyIfUnknown /* = false*/)
586 {
587   static std::string osName;
588   if (osName.empty())
589   {
590 #if defined (TARGET_WINDOWS)
591     osName = GetKernelName() + "-based OS";
592 #elif defined(TARGET_DRAGONFLY)
593     osName = "DragonFly";
594 #elif defined(TARGET_FREEBSD)
595     osName = GetKernelName(true); // FIXME: for FreeBSD OS name is a kernel name
596 #elif defined(TARGET_DARWIN_IOS)
597     osName = "iOS";
598 #elif defined(TARGET_DARWIN_TVOS)
599     osName = "tvOS";
600 #elif defined(TARGET_DARWIN_OSX)
601     osName = "OS X";
602 #elif defined (TARGET_ANDROID)
603     osName = "Android";
604 #elif defined(TARGET_LINUX)
605     osName = getValueFromOs_release("NAME");
606     if (osName.empty())
607       osName = getValueFromLsb_release(lsb_rel_distributor);
608     if (osName.empty())
609       osName = getValueFromOs_release("ID");
610 #endif // defined(TARGET_LINUX)
611 
612     if (osName.empty())
613       osName = "Unknown OS";
614   }
615 
616   if (emptyIfUnknown && osName == "Unknown OS")
617     return "";
618 
619   return osName;
620 }
621 
GetOsVersion(void)622 std::string CSysInfo::GetOsVersion(void)
623 {
624   static std::string osVersion;
625   if (!osVersion.empty())
626     return osVersion;
627 
628 #if defined(TARGET_WINDOWS) || defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
629   osVersion = GetKernelVersion(); // FIXME: for Win32 and FreeBSD OS version is a kernel version
630 #elif defined(TARGET_DARWIN)
631   osVersion = CDarwinUtils::GetVersionString();
632 #elif defined(TARGET_ANDROID)
633   char versionCStr[PROP_VALUE_MAX];
634   int propLen = __system_property_get("ro.build.version.release", versionCStr);
635   osVersion.assign(versionCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
636 
637   if (osVersion.empty() || std::string("0123456789").find(versionCStr[0]) == std::string::npos)
638     osVersion.clear(); // can't correctly detect Android version
639   else
640   {
641     size_t pointPos = osVersion.find('.');
642     if (pointPos == std::string::npos)
643       osVersion += ".0.0";
644     else if (osVersion.find('.', pointPos + 1) == std::string::npos)
645       osVersion += ".0";
646   }
647 #elif defined(TARGET_LINUX)
648   osVersion = getValueFromOs_release("VERSION_ID");
649   if (osVersion.empty())
650     osVersion = getValueFromLsb_release(lsb_rel_release);
651 #endif // defined(TARGET_LINUX)
652 
653   if (osVersion.empty())
654     osVersion = "0.0";
655 
656   return osVersion;
657 }
658 
GetOsPrettyNameWithVersion(void)659 std::string CSysInfo::GetOsPrettyNameWithVersion(void)
660 {
661   static std::string osNameVer;
662   if (!osNameVer.empty())
663     return osNameVer;
664 
665 #if defined (TARGET_WINDOWS_DESKTOP)
666   OSVERSIONINFOEXW osvi = {};
667 
668   osNameVer = "Windows ";
669   if (sysGetVersionExWByRef(osvi))
670   {
671     switch (GetWindowsVersion())
672     {
673     case WindowsVersionWin7:
674       if (osvi.wProductType == VER_NT_WORKSTATION)
675         osNameVer.append("7");
676       else
677         osNameVer.append("Server 2008 R2");
678       break;
679     case WindowsVersionWin8:
680       if (osvi.wProductType == VER_NT_WORKSTATION)
681         osNameVer.append("8");
682       else
683         osNameVer.append("Server 2012");
684       break;
685     case WindowsVersionWin8_1:
686       if (osvi.wProductType == VER_NT_WORKSTATION)
687         osNameVer.append("8.1");
688       else
689         osNameVer.append("Server 2012 R2");
690       break;
691     case WindowsVersionWin10:
692     case WindowsVersionWin10_1709:
693     case WindowsVersionWin10_1803:
694     case WindowsVersionWin10_1809:
695     case WindowsVersionWin10_1903:
696     case WindowsVersionWin10_1909:
697     case WindowsVersionWin10_2004:
698     case WindowsVersionWin10_Future:
699       osNameVer.append("10");
700       appendWindows10NameVersion(osNameVer);
701       break;
702     case WindowsVersionWin11:
703       osNameVer.append("11");
704       appendWindows10NameVersion(osNameVer);
705       break;
706     case WindowsVersionFuture:
707       osNameVer.append("Unknown future version");
708       break;
709     default:
710       osNameVer.append("Unknown version");
711       break;
712     }
713 
714     // Append Service Pack version if any
715     if (osvi.wServicePackMajor > 0 || osvi.wServicePackMinor > 0)
716     {
717       osNameVer.append(StringUtils::Format(" SP%d", osvi.wServicePackMajor));
718       if (osvi.wServicePackMinor > 0)
719       {
720         osNameVer.append(StringUtils::Format(".%d", osvi.wServicePackMinor));
721       }
722     }
723   }
724   else
725     osNameVer.append(" unknown");
726 #elif defined(TARGET_WINDOWS_STORE)
727   osNameVer = GetKernelName() + " " + GetOsVersion();
728 #elif defined(TARGET_FREEBSD) || defined(TARGET_DARWIN) || defined(TARGET_DRAGONFLY)
729   osNameVer = GetOsName() + " " + GetOsVersion();
730 #elif defined(TARGET_ANDROID)
731   osNameVer = GetOsName() + " " + GetOsVersion() + " API level " +   StringUtils::Format("%d", CJNIBuild::SDK_INT);
732 #elif defined(TARGET_LINUX)
733   osNameVer = getValueFromOs_release("PRETTY_NAME");
734   if (osNameVer.empty())
735   {
736     osNameVer = getValueFromLsb_release(lsb_rel_description);
737     std::string osName(GetOsName(true));
738     if (!osName.empty() && osNameVer.find(osName) == std::string::npos)
739       osNameVer = osName + osNameVer;
740     if (osNameVer.empty())
741       osNameVer = "Unknown Linux Distribution";
742   }
743 
744   if (osNameVer.find(GetOsVersion()) == std::string::npos)
745     osNameVer += " " + GetOsVersion();
746 #endif // defined(TARGET_LINUX)
747 
748   if (osNameVer.empty())
749     osNameVer = "Unknown OS Unknown version";
750 
751   return osNameVer;
752 
753 }
754 
GetManufacturerName(void)755 std::string CSysInfo::GetManufacturerName(void)
756 {
757   static std::string manufName;
758   static bool inited = false;
759   if (!inited)
760   {
761 #if defined(TARGET_ANDROID)
762     char deviceCStr[PROP_VALUE_MAX];
763     int propLen = __system_property_get("ro.product.manufacturer", deviceCStr);
764     manufName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
765 #elif defined(TARGET_DARWIN)
766     manufName = CDarwinUtils::GetManufacturer();
767 #elif defined(TARGET_WINDOWS_STORE)
768     auto eas = EasClientDeviceInformation();
769     auto manufacturer = eas.SystemManufacturer();
770     g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), manufName);
771 #elif defined(TARGET_LINUX)
772 
773     auto cpuInfo = CServiceBroker::GetCPUInfo();
774     manufName = cpuInfo->GetCPUSoC();
775 
776 #elif defined(TARGET_WINDOWS)
777     // We just don't care, might be useful on embedded
778 #endif
779     inited = true;
780   }
781 
782   return manufName;
783 }
784 
GetModelName(void)785 std::string CSysInfo::GetModelName(void)
786 {
787   static std::string modelName;
788   static bool inited = false;
789   if (!inited)
790   {
791 #if defined(TARGET_ANDROID)
792     char deviceCStr[PROP_VALUE_MAX];
793     int propLen = __system_property_get("ro.product.model", deviceCStr);
794     modelName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
795 #elif defined(TARGET_DARWIN_EMBEDDED)
796     modelName = CDarwinUtils::getIosPlatformString();
797 #elif defined(TARGET_DARWIN_OSX)
798     size_t nameLen = 0; // 'nameLen' should include terminating null
799     if (sysctlbyname("hw.model", NULL, &nameLen, NULL, 0) == 0 && nameLen > 1)
800     {
801       XUTILS::auto_buffer buf(nameLen);
802       if (sysctlbyname("hw.model", buf.get(), &nameLen, NULL, 0) == 0 && nameLen == buf.size())
803         modelName.assign(buf.get(), nameLen - 1); // assign exactly 'nameLen-1' characters to 'modelName'
804     }
805 #elif defined(TARGET_WINDOWS_STORE)
806     auto eas = EasClientDeviceInformation();
807     auto manufacturer = eas.SystemProductName();
808     g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), modelName);
809 #elif defined(TARGET_LINUX)
810     auto cpuInfo = CServiceBroker::GetCPUInfo();
811     modelName = cpuInfo->GetCPUHardware();
812 #elif defined(TARGET_WINDOWS)
813     // We just don't care, might be useful on embedded
814 #endif
815     inited = true;
816   }
817 
818   return modelName;
819 }
820 
IsAeroDisabled()821 bool CSysInfo::IsAeroDisabled()
822 {
823 #ifdef TARGET_WINDOWS_STORE
824   return true; // need to review https://msdn.microsoft.com/en-us/library/windows/desktop/aa969518(v=vs.85).aspx
825 #elif defined(TARGET_WINDOWS)
826   BOOL aeroEnabled = FALSE;
827   HRESULT res = DwmIsCompositionEnabled(&aeroEnabled);
828   if (SUCCEEDED(res))
829     return !aeroEnabled;
830 #endif
831   return false;
832 }
833 
834 CSysInfo::WindowsVersion CSysInfo::m_WinVer = WindowsVersionUnknown;
835 
IsWindowsVersion(WindowsVersion ver)836 bool CSysInfo::IsWindowsVersion(WindowsVersion ver)
837 {
838   if (ver == WindowsVersionUnknown)
839     return false;
840   return GetWindowsVersion() == ver;
841 }
842 
IsWindowsVersionAtLeast(WindowsVersion ver)843 bool CSysInfo::IsWindowsVersionAtLeast(WindowsVersion ver)
844 {
845   if (ver == WindowsVersionUnknown)
846     return false;
847   return GetWindowsVersion() >= ver;
848 }
849 
GetWindowsVersion()850 CSysInfo::WindowsVersion CSysInfo::GetWindowsVersion()
851 {
852 #ifdef TARGET_WINDOWS_DESKTOP
853   if (m_WinVer == WindowsVersionUnknown)
854   {
855     OSVERSIONINFOEXW osvi = {};
856     if (sysGetVersionExWByRef(osvi))
857     {
858       if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
859         m_WinVer = WindowsVersionWin7;
860       else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
861         m_WinVer = WindowsVersionWin8;
862       else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
863         m_WinVer = WindowsVersionWin8_1;
864       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < 16299)
865         m_WinVer = WindowsVersionWin10;
866       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 16299)
867         m_WinVer = WindowsVersionWin10_1709;
868       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 17134)
869         m_WinVer = WindowsVersionWin10_1803;
870       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 17763)
871         m_WinVer = WindowsVersionWin10_1809;
872       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 18362)
873         m_WinVer = WindowsVersionWin10_1903;
874       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 18363)
875         m_WinVer = WindowsVersionWin10_1909;
876       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 19041)
877         m_WinVer = WindowsVersionWin10_2004;
878       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 22000)
879         m_WinVer = WindowsVersionWin11;
880       else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber > 19041)
881         m_WinVer = WindowsVersionWin10_Future;
882       /* Insert checks for new Windows versions here */
883       else if ( (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 3) || osvi.dwMajorVersion > 10)
884         m_WinVer = WindowsVersionFuture;
885     }
886   }
887 #elif  defined(TARGET_WINDOWS_STORE)
888   m_WinVer = WindowsVersionWin10;
889 #endif // TARGET_WINDOWS
890   return m_WinVer;
891 }
892 
GetKernelBitness(void)893 int CSysInfo::GetKernelBitness(void)
894 {
895   static int kernelBitness = -1;
896   if (kernelBitness == -1)
897   {
898 #ifdef TARGET_WINDOWS_STORE
899     Package package = Package::Current();
900     auto arch = package.Id().Architecture();
901     switch (arch)
902     {
903     case ProcessorArchitecture::X86:
904       kernelBitness = 32;
905       break;
906     case ProcessorArchitecture::X64:
907       kernelBitness = 64;
908       break;
909     case ProcessorArchitecture::Arm:
910       kernelBitness = 32;
911       break;
912     case ProcessorArchitecture::Unknown: // not sure what to do here. guess 32 for now
913     case ProcessorArchitecture::Neutral:
914       kernelBitness = 32;
915       break;
916     }
917 #elif defined(TARGET_WINDOWS_DESKTOP)
918     SYSTEM_INFO si;
919     GetNativeSystemInfo(&si);
920     if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
921       kernelBitness = 32;
922     else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
923       kernelBitness = 64;
924     else
925     {
926       BOOL isWow64 = FALSE;
927       if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) // fallback
928         kernelBitness = 64;
929     }
930 #elif defined(TARGET_DARWIN_EMBEDDED)
931     // Note: OS X return x86 CPU type without CPU_ARCH_ABI64 flag
932     const NXArchInfo* archInfo = NXGetLocalArchInfo();
933     if (archInfo)
934       kernelBitness = ((archInfo->cputype & CPU_ARCH_ABI64) != 0) ? 64 : 32;
935 #elif defined(TARGET_POSIX)
936     struct utsname un;
937     if (uname(&un) == 0)
938     {
939       std::string machine(un.machine);
940       if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" ||
941           machine == "ppc64" || machine == "ppc64el" || machine == "ppc64le" || machine == "ia64" ||
942           machine == "mips64" || machine == "s390x" || machine == "riscv64")
943         kernelBitness = 64;
944       else
945         kernelBitness = 32;
946     }
947 #endif
948     if (kernelBitness == -1)
949       kernelBitness = 0; // can't detect
950   }
951 
952   return kernelBitness;
953 }
954 
GetKernelCpuFamily(void)955 const std::string& CSysInfo::GetKernelCpuFamily(void)
956 {
957   static std::string kernelCpuFamily;
958   if (kernelCpuFamily.empty())
959   {
960 #ifdef TARGET_WINDOWS
961     SYSTEM_INFO si;
962     GetNativeSystemInfo(&si);
963     if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ||
964         si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
965         kernelCpuFamily = "x86";
966     else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
967       kernelCpuFamily = "ARM";
968 #elif defined(TARGET_DARWIN)
969     const NXArchInfo* archInfo = NXGetLocalArchInfo();
970     if (archInfo)
971     {
972       const cpu_type_t cpuType = (archInfo->cputype & ~CPU_ARCH_ABI64); // get CPU family without 64-bit ABI flag
973       if (cpuType == CPU_TYPE_I386)
974         kernelCpuFamily = "x86";
975       else if (cpuType == CPU_TYPE_ARM)
976         kernelCpuFamily = "ARM";
977 #ifdef CPU_TYPE_MIPS
978       else if (cpuType == CPU_TYPE_MIPS)
979         kernelCpuFamily = "MIPS";
980 #endif // CPU_TYPE_MIPS
981     }
982 #elif defined(TARGET_POSIX)
983     struct utsname un;
984     if (uname(&un) == 0)
985     {
986       std::string machine(un.machine);
987       if (machine.compare(0, 3, "arm", 3) == 0 || machine.compare(0, 7, "aarch64", 7) == 0)
988         kernelCpuFamily = "ARM";
989       else if (machine.compare(0, 4, "mips", 4) == 0)
990         kernelCpuFamily = "MIPS";
991       else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" ||  machine.compare(0, 3, "x86", 3) == 0)
992         kernelCpuFamily = "x86";
993       else if (machine.compare(0, 4, "s390", 4) == 0)
994         kernelCpuFamily = "s390";
995       else if (machine.compare(0, 3, "ppc", 3) == 0 || machine.compare(0, 5, "power", 5) == 0)
996         kernelCpuFamily = "PowerPC";
997     }
998 #endif
999     if (kernelCpuFamily.empty())
1000       kernelCpuFamily = "unknown CPU family";
1001   }
1002   return kernelCpuFamily;
1003 }
1004 
GetXbmcBitness(void)1005 int CSysInfo::GetXbmcBitness(void)
1006 {
1007   return static_cast<int>(sizeof(void*) * 8);
1008 }
1009 
HasInternet()1010 bool CSysInfo::HasInternet()
1011 {
1012   if (m_info.internetState != CSysData::UNKNOWN)
1013     return m_info.internetState == CSysData::CONNECTED;
1014   return (m_info.internetState = CSysInfoJob::GetInternetState()) == CSysData::CONNECTED;
1015 }
1016 
GetHddSpaceInfo(int drive,bool shortText)1017 std::string CSysInfo::GetHddSpaceInfo(int drive, bool shortText)
1018 {
1019  int percent;
1020  return GetHddSpaceInfo( percent, drive, shortText);
1021 }
1022 
GetHddSpaceInfo(int & percent,int drive,bool shortText)1023 std::string CSysInfo::GetHddSpaceInfo(int& percent, int drive, bool shortText)
1024 {
1025   int total, totalFree, totalUsed, percentFree, percentused;
1026   std::string strRet;
1027   percent = 0;
1028   if (g_sysinfo.GetDiskSpace("", total, totalFree, totalUsed, percentFree, percentused))
1029   {
1030     if (shortText)
1031     {
1032       switch(drive)
1033       {
1034         case SYSTEM_FREE_SPACE:
1035           percent = percentFree;
1036           break;
1037         case SYSTEM_USED_SPACE:
1038           percent = percentused;
1039           break;
1040       }
1041     }
1042     else
1043     {
1044       switch(drive)
1045       {
1046       case SYSTEM_FREE_SPACE:
1047         strRet = StringUtils::Format("%i MB %s", totalFree, g_localizeStrings.Get(160).c_str());
1048         break;
1049       case SYSTEM_USED_SPACE:
1050         strRet = StringUtils::Format("%i MB %s", totalUsed, g_localizeStrings.Get(20162).c_str());
1051         break;
1052       case SYSTEM_TOTAL_SPACE:
1053         strRet = StringUtils::Format("%i MB %s", total, g_localizeStrings.Get(20161).c_str());
1054         break;
1055       case SYSTEM_FREE_SPACE_PERCENT:
1056         strRet = StringUtils::Format("%i %% %s", percentFree, g_localizeStrings.Get(160).c_str());
1057         break;
1058       case SYSTEM_USED_SPACE_PERCENT:
1059         strRet = StringUtils::Format("%i %% %s", percentused, g_localizeStrings.Get(20162).c_str());
1060         break;
1061       }
1062     }
1063   }
1064   else
1065   {
1066     if (shortText)
1067       strRet = g_localizeStrings.Get(10006); // N/A
1068     else
1069       strRet = g_localizeStrings.Get(10005); // Not available
1070   }
1071   return strRet;
1072 }
1073 
GetUserAgent()1074 std::string CSysInfo::GetUserAgent()
1075 {
1076   static std::string result;
1077   if (!result.empty())
1078     return result;
1079 
1080   result = GetAppName() + "/" + CSysInfo::GetVersionShort() + " (";
1081 #if defined(TARGET_WINDOWS)
1082   result += GetKernelName() + " " + GetKernelVersion();
1083 #ifndef TARGET_WINDOWS_STORE
1084   BOOL bIsWow = FALSE;
1085   if (IsWow64Process(GetCurrentProcess(), &bIsWow) && bIsWow)
1086       result.append("; WOW64");
1087   else
1088 #endif
1089   {
1090     SYSTEM_INFO si = {};
1091     GetSystemInfo(&si);
1092     if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
1093       result.append("; Win64; x64");
1094     else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
1095       result.append("; Win64; IA64");
1096     else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
1097       result.append("; ARM");
1098   }
1099 #elif defined(TARGET_DARWIN)
1100 #if defined(TARGET_DARWIN_EMBEDDED)
1101   std::string iDevStr(GetModelName()); // device model name with number of model version
1102   size_t iDevStrDigit = iDevStr.find_first_of("0123456789");
1103   std::string iDev(iDevStr, 0, iDevStrDigit);  // device model name without number
1104   if (iDevStrDigit == 0)
1105     iDev = "unknown";
1106   result += iDev + "; ";
1107   std::string iOSVersion(GetOsVersion());
1108   size_t lastDotPos = iOSVersion.rfind('.');
1109   if (lastDotPos != std::string::npos && iOSVersion.find('.') != lastDotPos
1110       && iOSVersion.find_first_not_of('0', lastDotPos + 1) == std::string::npos)
1111     iOSVersion.erase(lastDotPos);
1112   StringUtils::Replace(iOSVersion, '.', '_');
1113   if (iDev == "AppleTV")
1114   {
1115     // check if it's ATV4 (AppleTV5,3) or later
1116     auto modelMajorNumberEndPos = iDevStr.find_first_of(',', iDevStrDigit);
1117     std::string s{iDevStr, iDevStrDigit, modelMajorNumberEndPos - iDevStrDigit};
1118     if (stoi(s) >= 5)
1119       result += "CPU TVOS";
1120     else
1121       result += "CPU OS";
1122   }
1123   else if (iDev == "iPad")
1124     result += "CPU OS";
1125   else
1126     result += "CPU iPhone OS ";
1127   result += iOSVersion + " like Mac OS X";
1128 #else
1129   result += "Macintosh; ";
1130   std::string cpuFam(GetBuildTargetCpuFamily());
1131   if (cpuFam == "x86")
1132     result += "Intel ";
1133   result += "Mac OS X ";
1134   std::string OSXVersion(GetOsVersion());
1135   StringUtils::Replace(OSXVersion, '.', '_');
1136   result += OSXVersion;
1137 #endif
1138 #elif defined(TARGET_ANDROID)
1139   result += "Linux; Android ";
1140   std::string versionStr(GetOsVersion());
1141   const size_t verLen = versionStr.length();
1142   if (verLen >= 2 && versionStr.compare(verLen - 2, 2, ".0", 2) == 0)
1143     versionStr.erase(verLen - 2); // remove last ".0" if any
1144   result += versionStr;
1145   std::string deviceInfo(GetModelName());
1146 
1147   char buildId[PROP_VALUE_MAX];
1148   int propLen = __system_property_get("ro.build.id", buildId);
1149   if (propLen > 0 && propLen <= PROP_VALUE_MAX)
1150   {
1151     if (!deviceInfo.empty())
1152       deviceInfo += " ";
1153     deviceInfo += "Build/";
1154     deviceInfo.append(buildId, propLen);
1155   }
1156 
1157   if (!deviceInfo.empty())
1158     result += "; " + deviceInfo;
1159 #elif defined(TARGET_POSIX)
1160   result += "X11; ";
1161   struct utsname un;
1162   if (uname(&un) == 0)
1163   {
1164     std::string cpuStr(un.machine);
1165     if (cpuStr == "x86_64" && GetXbmcBitness() == 32)
1166       cpuStr = "i686 on x86_64";
1167     result += un.sysname;
1168     result += " ";
1169     result += cpuStr;
1170   }
1171   else
1172     result += "Unknown";
1173 #else
1174   result += "Unknown";
1175 #endif
1176   result += ")";
1177 
1178   if (GetAppName() != "Kodi")
1179     result += " Kodi_Fork_" + GetAppName() + "/1.0"; // default fork number is '1.0', replace it with actual number if necessary
1180 
1181 #ifdef TARGET_LINUX
1182   // Add distribution name
1183   std::string linuxOSName(GetOsName(true));
1184   if (!linuxOSName.empty())
1185     result += " " + linuxOSName + "/" + GetOsVersion();
1186 #endif
1187 
1188 #if defined(TARGET_DARWIN_IOS)
1189   std::string iDevVer;
1190   if (iDevStrDigit == std::string::npos)
1191     iDevVer = "0.0";
1192   else
1193     iDevVer.assign(iDevStr, iDevStrDigit, std::string::npos);
1194   StringUtils::Replace(iDevVer, ',', '.');
1195   result += " HW_" + iDev + "/" + iDevVer;
1196 #endif
1197   // add more device IDs here if needed.
1198   // keep only one device ID in result! Form:
1199   // result += " HW_" + "deviceID" + "/" + "1.0"; // '1.0' if device has no version
1200 
1201 #if defined(TARGET_ANDROID)
1202   // Android has no CPU string by default, so add it as additional parameter
1203   struct utsname un1;
1204   if (uname(&un1) == 0)
1205   {
1206     std::string cpuStr(un1.machine);
1207     StringUtils::Replace(cpuStr, ' ', '_');
1208     result += " Sys_CPU/" + cpuStr;
1209   }
1210 #endif
1211 
1212   result += " App_Bitness/" + StringUtils::Format("%d", GetXbmcBitness());
1213 
1214   std::string fullVer(CSysInfo::GetVersion());
1215   StringUtils::Replace(fullVer, ' ', '-');
1216   result += " Version/" + fullVer;
1217 
1218   return result;
1219 }
1220 
GetDeviceName()1221 std::string CSysInfo::GetDeviceName()
1222 {
1223   std::string friendlyName = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SERVICES_DEVICENAME);
1224   if (StringUtils::EqualsNoCase(friendlyName, CCompileInfo::GetAppName()))
1225   {
1226     std::string hostname("[unknown]");
1227     CServiceBroker::GetNetwork().GetHostName(hostname);
1228     return StringUtils::Format("%s (%s)", friendlyName.c_str(), hostname.c_str());
1229   }
1230 
1231   return friendlyName;
1232 }
1233 
1234 // Version string MUST NOT contain spaces.  It is used
1235 // in the HTTP request user agent.
GetVersionShort()1236 std::string CSysInfo::GetVersionShort()
1237 {
1238   if (strlen(CCompileInfo::GetSuffix()) == 0)
1239     return StringUtils::Format("%d.%d", CCompileInfo::GetMajor(), CCompileInfo::GetMinor());
1240   else
1241     return StringUtils::Format("%d.%d-%s", CCompileInfo::GetMajor(), CCompileInfo::GetMinor(), CCompileInfo::GetSuffix());
1242 }
1243 
GetVersion()1244 std::string CSysInfo::GetVersion()
1245 {
1246   return GetVersionShort() + " (" + CCompileInfo::GetVersionCode() + ")" +
1247          " Git:" + CCompileInfo::GetSCMID();
1248 }
1249 
GetVersionCode()1250 std::string CSysInfo::GetVersionCode()
1251 {
1252   return CCompileInfo::GetVersionCode();
1253 }
1254 
GetVersionGit()1255 std::string CSysInfo::GetVersionGit()
1256 {
1257   return CCompileInfo::GetSCMID();
1258 }
1259 
GetBuildDate()1260 std::string CSysInfo::GetBuildDate()
1261 {
1262   return CCompileInfo::GetBuildDate();
1263 }
1264 
GetBuildTargetPlatformName(void)1265 std::string CSysInfo::GetBuildTargetPlatformName(void)
1266 {
1267 #if defined(TARGET_DARWIN_OSX)
1268   return "OS X";
1269 #elif defined(TARGET_DARWIN_IOS)
1270   return "iOS";
1271 #elif defined(TARGET_DARWIN_TVOS)
1272   return "tvOS";
1273 #elif defined(TARGET_FREEBSD)
1274   return "FreeBSD";
1275 #elif defined(TARGET_DRAGONFLY)
1276   return "DragonFly";
1277 #elif defined(TARGET_ANDROID)
1278   return "Android";
1279 #elif defined(TARGET_LINUX)
1280   return "Linux";
1281 #elif defined(TARGET_WINDOWS)
1282 #ifdef NTDDI_VERSION
1283   return "Windows NT";
1284 #else // !NTDDI_VERSION
1285   return "unknown Win32 platform";
1286 #endif // !NTDDI_VERSION
1287 #else
1288   return "unknown platform";
1289 #endif
1290 }
1291 
GetBuildTargetPlatformVersion(void)1292 std::string CSysInfo::GetBuildTargetPlatformVersion(void)
1293 {
1294 #if defined(TARGET_DARWIN_OSX)
1295   return XSTR_MACRO(__MAC_OS_X_VERSION_MIN_REQUIRED);
1296 #elif defined(TARGET_DARWIN_IOS)
1297   return XSTR_MACRO(__IPHONE_OS_VERSION_MIN_REQUIRED);
1298 #elif defined(TARGET_DARWIN_TVOS)
1299   return XSTR_MACRO(__TV_OS_VERSION_MIN_REQUIRED);
1300 #elif defined(TARGET_DRAGONFLY)
1301   return XSTR_MACRO(__DragonFly_version);
1302 #elif defined(TARGET_FREEBSD)
1303   return XSTR_MACRO(__FreeBSD_version);
1304 #elif defined(TARGET_ANDROID)
1305   return "API level " XSTR_MACRO(__ANDROID_API__);
1306 #elif defined(TARGET_LINUX)
1307   return XSTR_MACRO(LINUX_VERSION_CODE);
1308 #elif defined(TARGET_WINDOWS)
1309 #ifdef NTDDI_VERSION
1310   return XSTR_MACRO(NTDDI_VERSION);
1311 #else // !NTDDI_VERSION
1312   return "(unknown Win32 platform)";
1313 #endif // !NTDDI_VERSION
1314 #else
1315   return "(unknown platform)";
1316 #endif
1317 }
1318 
GetBuildTargetPlatformVersionDecoded(void)1319 std::string CSysInfo::GetBuildTargetPlatformVersionDecoded(void)
1320 {
1321 #if defined(TARGET_DARWIN_OSX)
1322   if (__MAC_OS_X_VERSION_MIN_REQUIRED % 100)
1323     return StringUtils::Format("version %d.%d.%d", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
1324                                (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100,
1325                                __MAC_OS_X_VERSION_MIN_REQUIRED % 100);
1326   else
1327     return StringUtils::Format("version %d.%d", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
1328                                (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100);
1329 #elif defined(TARGET_DARWIN_EMBEDDED)
1330   std::string versionStr = GetBuildTargetPlatformVersion();
1331   static const int major = (std::stoi(versionStr) / 10000) % 100;
1332   static const int minor = (std::stoi(versionStr) / 100) % 100;
1333   static const int rev = std::stoi(versionStr) % 100;
1334   return StringUtils::Format("version %d.%d.%d", major, minor, rev);
1335 #elif defined(TARGET_DRAGONFLY)
1336   static const int major = (__DragonFly_version / 100000);
1337   static const int minor = (__DragonFly_version - 100000 * major) / 100;
1338   return StringUtils::Format("version %d.%d", major, minor);
1339 #elif defined(TARGET_FREEBSD)
1340   // FIXME: should works well starting from FreeBSD 8.1
1341   static const int major = (__FreeBSD_version / 100000) % 100;
1342   static const int minor = (__FreeBSD_version / 1000) % 100;
1343   static const int Rxx = __FreeBSD_version % 1000;
1344   if ((major < 9 && Rxx == 0))
1345     return StringUtils::Format("version %d.%d-RELEASE", major, minor);
1346   if (Rxx >= 500)
1347     return StringUtils::Format("version %d.%d-STABLE", major, minor);
1348 
1349   return StringUtils::Format("version %d.%d-CURRENT", major, minor);
1350 #elif defined(TARGET_ANDROID)
1351   return "API level " XSTR_MACRO(__ANDROID_API__);
1352 #elif defined(TARGET_LINUX)
1353   return StringUtils::Format("version %d.%d.%d", (LINUX_VERSION_CODE >> 16) & 0xFF , (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF);
1354 #elif defined(TARGET_WINDOWS)
1355 #ifdef NTDDI_VERSION
1356   std::string version(StringUtils::Format("version %d.%d", int(NTDDI_VERSION >> 24) & 0xFF, int(NTDDI_VERSION >> 16) & 0xFF));
1357   if (SPVER(NTDDI_VERSION))
1358     version += StringUtils::Format(" SP%d", int(SPVER(NTDDI_VERSION)));
1359   return version;
1360 #else // !NTDDI_VERSION
1361   return "(unknown Win32 platform)";
1362 #endif // !NTDDI_VERSION
1363 #else
1364   return "(unknown platform)";
1365 #endif
1366 }
1367 
GetBuildTargetCpuFamily(void)1368 std::string CSysInfo::GetBuildTargetCpuFamily(void)
1369 {
1370 #if defined(__thumb__) || defined(_M_ARMT)
1371   return "ARM (Thumb)";
1372 #elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
1373   return "ARM";
1374 #elif defined(__mips__) || defined(mips) || defined(__mips)
1375   return "MIPS";
1376 #elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
1377    defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_)
1378   return "x86";
1379 #elif defined(__s390x__)
1380   return "s390";
1381 #elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC)
1382   return "PowerPC";
1383 #else
1384   return "unknown CPU family";
1385 #endif
1386 }
1387 
GetUsedCompilerNameAndVer(void)1388 std::string CSysInfo::GetUsedCompilerNameAndVer(void)
1389 {
1390 #if defined(__clang__)
1391 #ifdef __clang_version__
1392   return "Clang " __clang_version__;
1393 #else // ! __clang_version__
1394   return "Clang " XSTR_MACRO(__clang_major__) "." XSTR_MACRO(__clang_minor__) "." XSTR_MACRO(__clang_patchlevel__);
1395 #endif //! __clang_version__
1396 #elif defined (__INTEL_COMPILER)
1397   return "Intel Compiler " XSTR_MACRO(__INTEL_COMPILER);
1398 #elif defined (__GNUC__)
1399   std::string compilerStr;
1400 #ifdef __llvm__
1401   /* Note: this will not detect GCC + DragonEgg */
1402   compilerStr = "llvm-gcc ";
1403 #else // __llvm__
1404   compilerStr = "GCC ";
1405 #endif // !__llvm__
1406   compilerStr += XSTR_MACRO(__GNUC__) "." XSTR_MACRO(__GNUC_MINOR__) "." XSTR_MACRO(__GNUC_PATCHLEVEL__);
1407   return compilerStr;
1408 #elif defined (_MSC_VER)
1409   return "MSVC " XSTR_MACRO(_MSC_FULL_VER);
1410 #else
1411   return "unknown compiler";
1412 #endif
1413 }
1414 
GetPrivacyPolicy()1415 std::string CSysInfo::GetPrivacyPolicy()
1416 {
1417   if (m_privacyPolicy.empty())
1418   {
1419     CFile file;
1420     XFILE::auto_buffer buf;
1421     if (file.LoadFile("special://xbmc/privacy-policy.txt", buf) > 0)
1422     {
1423       std::string strBuf(buf.get(), buf.length());
1424       m_privacyPolicy = strBuf;
1425     }
1426     else
1427       m_privacyPolicy = g_localizeStrings.Get(19055);
1428   }
1429   return m_privacyPolicy;
1430 }
1431 
GetWindowsDeviceFamily()1432 CSysInfo::WindowsDeviceFamily CSysInfo::GetWindowsDeviceFamily()
1433 {
1434 #ifdef TARGET_WINDOWS_STORE
1435   auto familyName = AnalyticsInfo::VersionInfo().DeviceFamily();
1436   if (familyName == L"Windows.Desktop")
1437     return WindowsDeviceFamily::Desktop;
1438   else if (familyName == L"Windows.Mobile")
1439     return WindowsDeviceFamily::Mobile;
1440   else if (familyName == L"Windows.Universal")
1441     return WindowsDeviceFamily::IoT;
1442   else if (familyName == L"Windows.Team")
1443     return WindowsDeviceFamily::Surface;
1444   else if (familyName == L"Windows.Xbox")
1445     return WindowsDeviceFamily::Xbox;
1446   else
1447     return WindowsDeviceFamily::Other;
1448 #endif // TARGET_WINDOWS_STORE
1449   return WindowsDeviceFamily::Desktop;
1450 }
1451 
GetJob() const1452 CJob *CSysInfo::GetJob() const
1453 {
1454   return new CSysInfoJob();
1455 }
1456 
OnJobComplete(unsigned int jobID,bool success,CJob * job)1457 void CSysInfo::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1458 {
1459   m_info = static_cast<CSysInfoJob*>(job)->GetData();
1460   CInfoLoader::OnJobComplete(jobID, success, job);
1461 }
1462