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 "network/Network.h"
10 #include "threads/SystemClock.h"
11 #if defined(TARGET_DARWIN)
12 #include <sys/param.h>
13 #include <mach-o/dyld.h>
14 #endif
15 
16 #if (defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
17 #include <sys/param.h>
18 #include <sys/sysctl.h>
19 #endif
20 
21 #ifdef TARGET_POSIX
22 #include <sys/types.h>
23 #include <dirent.h>
24 #include <unistd.h>
25 #include <sys/wait.h>
26 #endif
27 #if defined(TARGET_ANDROID)
28 #include <androidjni/ApplicationInfo.h>
29 #include "platform/android/bionic_supplement/bionic_supplement.h"
30 #include "platform/android/activity/XBMCApp.h"
31 #include "CompileInfo.h"
32 #endif
33 #include <stdlib.h>
34 #include <algorithm>
35 #include <array>
36 
37 #include "addons/VFSEntry.h"
38 #include "ServiceBroker.h"
39 #include "Util.h"
40 #include "filesystem/PVRDirectory.h"
41 #include "filesystem/Directory.h"
42 #include "filesystem/StackDirectory.h"
43 #include "filesystem/MultiPathDirectory.h"
44 #include "filesystem/SpecialProtocol.h"
45 #include "filesystem/RSSDirectory.h"
46 #ifdef HAS_UPNP
47 #include "filesystem/UPnPDirectory.h"
48 #endif
49 #include "profiles/ProfileManager.h"
50 #include "utils/RegExp.h"
51 #include "windowing/GraphicContext.h"
52 #include "guilib/TextureManager.h"
53 #include "storage/MediaManager.h"
54 #ifdef TARGET_WINDOWS
55 #include "utils/CharsetConverter.h"
56 #include "WIN32Util.h"
57 #endif
58 #if defined(TARGET_DARWIN)
59 #include "CompileInfo.h"
60 #include "platform/darwin/DarwinUtils.h"
61 #endif
62 #include "URL.h"
63 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleStream.h"
64 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleTagSami.h"
65 #include "filesystem/File.h"
66 #include "guilib/LocalizeStrings.h"
67 #include "platform/Environment.h"
68 #include "settings/AdvancedSettings.h"
69 #include "settings/MediaSettings.h"
70 #include "settings/Settings.h"
71 #include "settings/SettingsComponent.h"
72 #include "utils/Digest.h"
73 #include "utils/FileExtensionProvider.h"
74 #include "utils/LangCodeExpander.h"
75 #include "utils/StringUtils.h"
76 #include "utils/TimeUtils.h"
77 #include "utils/URIUtils.h"
78 #include "utils/log.h"
79 #include "video/VideoDatabase.h"
80 #include "video/VideoInfoTag.h"
81 #ifdef HAVE_LIBCAP
82   #include <sys/capability.h>
83 #endif
84 
85 #include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h"
86 
87 #include <fstrcmp.h>
88 
89 #ifdef HAS_DVD_DRIVE
90 using namespace MEDIA_DETECT;
91 #endif
92 
93 using namespace XFILE;
94 using namespace PLAYLIST;
95 using KODI::UTILITY::CDigest;
96 
97 #if !defined(TARGET_WINDOWS)
98 unsigned int CUtil::s_randomSeed = time(NULL);
99 #endif
100 
101 namespace
102 {
103 #ifdef TARGET_WINDOWS
IsDirectoryValidRoot(std::wstring path)104 bool IsDirectoryValidRoot(std::wstring path)
105 {
106   path += L"\\system\\settings\\settings.xml";
107 #if defined(TARGET_WINDOWS_STORE)
108   auto h = CreateFile2(path.c_str(), GENERIC_READ, 0, OPEN_EXISTING, NULL);
109 #else
110   auto h = CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr,
111     OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
112 #endif
113   if (h != INVALID_HANDLE_VALUE)
114   {
115     CloseHandle(h);
116     return true;
117   }
118 
119   return false;
120 }
121 
GetHomePath(const std::string & strTarget,std::string strPath)122 std::string GetHomePath(const std::string& strTarget, std::string strPath)
123 {
124   std::wstring strPathW;
125 
126   // Environment variable was set and we have a path
127   // Let's make sure it's not relative and test it
128   // so it's actually pointing to a directory containing
129   // our stuff
130   if (strPath.find("..") != std::string::npos)
131   {
132     //expand potential relative path to full path
133     g_charsetConverter.utf8ToW(strPath, strPathW, false);
134     CWIN32Util::AddExtraLongPathPrefix(strPathW);
135     auto bufSize = GetFullPathNameW(strPathW.c_str(), 0, nullptr, nullptr);
136     if (bufSize != 0)
137     {
138       auto buf = std::make_unique<wchar_t[]>(bufSize);
139       if (GetFullPathNameW(strPathW.c_str(), bufSize, buf.get(), nullptr) <= bufSize - 1)
140       {
141         strPathW = buf.get();
142         CWIN32Util::RemoveExtraLongPathPrefix(strPathW);
143 
144         if (IsDirectoryValidRoot(strPathW))
145         {
146           g_charsetConverter.wToUTF8(strPathW, strPath);
147           return strPath;
148         }
149       }
150     }
151   }
152 
153   // Okay se no environment variable is set, let's
154   // grab the executable path and check if it's being
155   // run from a directory containing our stuff
156   strPath = CUtil::ResolveExecutablePath();
157   auto last_sep = strPath.find_last_of(PATH_SEPARATOR_CHAR);
158   if (last_sep != std::string::npos)
159       strPath = strPath.substr(0, last_sep);
160 
161   g_charsetConverter.utf8ToW(strPath, strPathW);
162   if (IsDirectoryValidRoot(strPathW))
163     return strPath;
164 
165   // Still nothing, let's check the current working
166   // directory and see if it points to a directory
167   // with our stuff in it. This bit should never be
168   // needed when running on a users system, it's intended
169   // to make our dev environment easier.
170   auto bufSize = GetCurrentDirectoryW(0, nullptr);
171   if (bufSize > 0)
172   {
173     auto buf = std::make_unique<wchar_t[]>(bufSize);
174     if (0 != GetCurrentDirectoryW(bufSize, buf.get()))
175     {
176       std::string currentDirectory;
177       std::wstring currentDirectoryW(buf.get());
178       CWIN32Util::RemoveExtraLongPathPrefix(currentDirectoryW);
179 
180       if (IsDirectoryValidRoot(currentDirectoryW))
181       {
182         g_charsetConverter.wToUTF8(currentDirectoryW, currentDirectory);
183         return currentDirectory;
184       }
185     }
186   }
187 
188   // If we ended up here we're most likely screwed
189   // we will crash in a few seconds
190   return strPath;
191 }
192 #endif
193 #if defined(TARGET_DARWIN)
194 #if !defined(TARGET_DARWIN_EMBEDDED)
IsDirectoryValidRoot(std::string path)195 bool IsDirectoryValidRoot(std::string path)
196 {
197   path += "/system/settings/settings.xml";
198   return CFile::Exists(path);
199 }
200 #endif
201 
GetHomePath(const std::string & strTarget,std::string strPath)202 std::string GetHomePath(const std::string& strTarget, std::string strPath)
203 {
204   if (strPath.empty())
205   {
206     auto strHomePath = CUtil::ResolveExecutablePath();
207     int      result = -1;
208     char     given_path[2 * MAXPATHLEN];
209     size_t path_size = 2 * MAXPATHLEN;
210 
211     result = CDarwinUtils::GetExecutablePath(given_path, &path_size);
212     if (result == 0)
213     {
214       // Move backwards to last /.
215       for (int n = strlen(given_path) - 1; given_path[n] != '/'; n--)
216         given_path[n] = '\0';
217 
218 #if defined(TARGET_DARWIN_EMBEDDED)
219       strcat(given_path, "/AppData/AppHome/");
220 #else
221       // Assume local path inside application bundle.
222       strcat(given_path, "../Resources/");
223       strcat(given_path, CCompileInfo::GetAppName());
224       strcat(given_path, "/");
225 
226       // if this path doesn't exist we
227       // might not be started from the app bundle
228       // but from the debugger/xcode. Lets
229       // see if this assumption is valid
230       if (!CDirectory::Exists(given_path))
231       {
232         std::string given_path_stdstr = CUtil::ResolveExecutablePath();
233         // try to find the correct folder by going back
234         // in the executable path until settings.xml was found
235         bool validRoot = false;
236         do
237         {
238           given_path_stdstr = URIUtils::GetParentPath(given_path_stdstr);
239           validRoot = IsDirectoryValidRoot(given_path_stdstr);
240         }
241         while(given_path_stdstr.length() > 0 && !validRoot);
242         strncpy(given_path, given_path_stdstr.c_str(), sizeof(given_path)-1);
243       }
244 
245 #endif
246 
247       // Convert to real path.
248       char real_path[2 * MAXPATHLEN];
249       if (realpath(given_path, real_path) != NULL)
250       {
251         strPath = real_path;
252         return strPath;
253       }
254     }
255     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
256     if (last_sep != std::string::npos)
257       strPath = strHomePath.substr(0, last_sep);
258     else
259       strPath = strHomePath;
260 
261   }
262   return strPath;
263 }
264 #endif
265 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
GetHomePath(const std::string & strTarget,std::string strPath)266 std::string GetHomePath(const std::string& strTarget, std::string strPath)
267 {
268   if (strPath.empty())
269   {
270     auto strHomePath = CUtil::ResolveExecutablePath();
271     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
272     if (last_sep != std::string::npos)
273       strPath = strHomePath.substr(0, last_sep);
274     else
275       strPath = strHomePath;
276   }
277   /* Change strPath accordingly when target is KODI_HOME and when INSTALL_PATH
278    * and BIN_INSTALL_PATH differ
279    */
280   std::string installPath = INSTALL_PATH;
281   std::string binInstallPath = BIN_INSTALL_PATH;
282 
283   if (strTarget.empty() && installPath.compare(binInstallPath))
284   {
285     int pos = strPath.length() - binInstallPath.length();
286     std::string tmp = strPath;
287     tmp.erase(0, pos);
288     if (!tmp.compare(binInstallPath))
289     {
290       strPath.erase(pos, strPath.length());
291       strPath.append(installPath);
292     }
293   }
294 
295   return strPath;
296 }
297 #endif
298 }
299 
GetTitleFromPath(const std::string & strFileNameAndPath,bool bIsFolder)300 std::string CUtil::GetTitleFromPath(const std::string& strFileNameAndPath, bool bIsFolder /* = false */)
301 {
302   CURL pathToUrl(strFileNameAndPath);
303   return GetTitleFromPath(pathToUrl, bIsFolder);
304 }
305 
GetTitleFromPath(const CURL & url,bool bIsFolder)306 std::string CUtil::GetTitleFromPath(const CURL& url, bool bIsFolder /* = false */)
307 {
308   // use above to get the filename
309   std::string path(url.Get());
310   URIUtils::RemoveSlashAtEnd(path);
311   std::string strFilename = URIUtils::GetFileName(path);
312 
313 #ifdef HAS_UPNP
314   // UPNP
315   if (url.IsProtocol("upnp"))
316     strFilename = CUPnPDirectory::GetFriendlyName(url);
317 #endif
318 
319   if (url.IsProtocol("rss") || url.IsProtocol("rsss"))
320   {
321     CRSSDirectory dir;
322     CFileItemList items;
323     if(dir.GetDirectory(url, items) && !items.m_strTitle.empty())
324       return items.m_strTitle;
325   }
326 
327   // Shoutcast
328   else if (url.IsProtocol("shout"))
329   {
330     const std::string strFileNameAndPath = url.Get();
331     const size_t genre = strFileNameAndPath.find_first_of('=');
332     if(genre == std::string::npos)
333       strFilename = g_localizeStrings.Get(260);
334     else
335       strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
336   }
337 
338   // Windows SMB Network (SMB)
339   else if (url.IsProtocol("smb") && strFilename.empty())
340   {
341     if (url.GetHostName().empty())
342     {
343       strFilename = g_localizeStrings.Get(20171);
344     }
345     else
346     {
347       strFilename = url.GetHostName();
348     }
349   }
350 
351   // Root file views
352   else if (url.IsProtocol("sources"))
353     strFilename = g_localizeStrings.Get(744);
354 
355   // Music Playlists
356   else if (StringUtils::StartsWith(path, "special://musicplaylists"))
357     strFilename = g_localizeStrings.Get(136);
358 
359   // Video Playlists
360   else if (StringUtils::StartsWith(path, "special://videoplaylists"))
361     strFilename = g_localizeStrings.Get(136);
362 
363   else if (URIUtils::HasParentInHostname(url) && strFilename.empty())
364     strFilename = URIUtils::GetFileName(url.GetHostName());
365 
366   // now remove the extension if needed
367   if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS) && !bIsFolder)
368   {
369     URIUtils::RemoveExtension(strFilename);
370     return strFilename;
371   }
372 
373   // URLDecode since the original path may be an URL
374   strFilename = CURL::Decode(strFilename);
375   return strFilename;
376 }
377 
CleanString(const std::string & strFileName,std::string & strTitle,std::string & strTitleAndYear,std::string & strYear,bool bRemoveExtension,bool bCleanChars)378 void CUtil::CleanString(const std::string& strFileName,
379                         std::string& strTitle,
380                         std::string& strTitleAndYear,
381                         std::string& strYear,
382                         bool bRemoveExtension /* = false */,
383                         bool bCleanChars /* = true */)
384 {
385   strTitleAndYear = strFileName;
386 
387   if (strFileName == "..")
388    return;
389 
390   const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
391   const std::vector<std::string> &regexps = advancedSettings->m_videoCleanStringRegExps;
392 
393   CRegExp reTags(true, CRegExp::autoUtf8);
394   CRegExp reYear(false, CRegExp::autoUtf8);
395 
396   if (!reYear.RegComp(advancedSettings->m_videoCleanDateTimeRegExp))
397   {
398     CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, advancedSettings->m_videoCleanDateTimeRegExp.c_str());
399   }
400   else
401   {
402     if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
403     {
404       strTitleAndYear = reYear.GetMatch(1);
405       strYear = reYear.GetMatch(2);
406     }
407   }
408 
409   URIUtils::RemoveExtension(strTitleAndYear);
410 
411   for (const auto &regexp : regexps)
412   {
413     if (!reTags.RegComp(regexp.c_str()))
414     { // invalid regexp - complain in logs
415       CLog::Log(LOGERROR, "%s: Invalid string clean RegExp:'%s'", __FUNCTION__, regexp.c_str());
416       continue;
417     }
418     int j=0;
419     if ((j=reTags.RegFind(strTitleAndYear.c_str())) > 0)
420       strTitleAndYear = strTitleAndYear.substr(0, j);
421   }
422 
423   // final cleanup - special characters used instead of spaces:
424   // all '_' tokens should be replaced by spaces
425   // if the file contains no spaces, all '.' tokens should be replaced by
426   // spaces - one possibility of a mistake here could be something like:
427   // "Dr..StrangeLove" - hopefully no one would have anything like this.
428   if (bCleanChars)
429   {
430     bool initialDots = true;
431     bool alreadyContainsSpace = (strTitleAndYear.find(' ') != std::string::npos);
432 
433     for (char &c : strTitleAndYear)
434     {
435       if (c != '.')
436         initialDots = false;
437 
438       if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
439       {
440         c = ' ';
441       }
442     }
443   }
444 
445   StringUtils::Trim(strTitleAndYear);
446   strTitle = strTitleAndYear;
447 
448   // append year
449   if (!strYear.empty())
450     strTitleAndYear = strTitle + " (" + strYear + ")";
451 
452   // restore extension if needed
453   if (!bRemoveExtension)
454     strTitleAndYear += URIUtils::GetExtension(strFileName);
455 }
456 
GetQualifiedFilename(const std::string & strBasePath,std::string & strFilename)457 void CUtil::GetQualifiedFilename(const std::string &strBasePath, std::string &strFilename)
458 {
459   // Check if the filename is a fully qualified URL such as protocol://path/to/file
460   CURL plItemUrl(strFilename);
461   if (!plItemUrl.GetProtocol().empty())
462     return;
463 
464   // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
465   if (strFilename.size() > 1)
466 #ifdef TARGET_POSIX
467     if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
468 #else
469     if ( strFilename[1] == ':' || (strFilename[0] == '\\' && strFilename[1] == '\\'))
470 #endif
471       return;
472 
473   // add to base path and then clean
474   strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
475 
476   // get rid of any /./ or \.\ that happen to be there
477   StringUtils::Replace(strFilename, "\\.\\", "\\");
478   StringUtils::Replace(strFilename, "/./", "/");
479 
480   // now find any "\\..\\" and remove them via GetParentPath
481   size_t pos;
482   while ((pos = strFilename.find("/../")) != std::string::npos)
483   {
484     std::string basePath = strFilename.substr(0, pos + 1);
485     strFilename.erase(0, pos + 4);
486     basePath = URIUtils::GetParentPath(basePath);
487     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
488   }
489   while ((pos = strFilename.find("\\..\\")) != std::string::npos)
490   {
491     std::string basePath = strFilename.substr(0, pos + 1);
492     strFilename.erase(0, pos + 4);
493     basePath = URIUtils::GetParentPath(basePath);
494     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
495   }
496 }
497 
RunShortcut(const char * szShortcutPath)498 void CUtil::RunShortcut(const char* szShortcutPath)
499 {
500 }
501 
GetHomePath(const std::string & strTarget)502 std::string CUtil::GetHomePath(const std::string& strTarget)
503 {
504   auto strPath = CEnvironment::getenv(strTarget);
505 
506   return ::GetHomePath(strTarget, strPath);
507 }
508 
IsPicture(const std::string & strFile)509 bool CUtil::IsPicture(const std::string& strFile)
510 {
511   return URIUtils::HasExtension(strFile,
512                   CServiceBroker::GetFileExtensionProvider().GetPictureExtensions()+ "|.tbn|.dds");
513 }
514 
GetSplashPath()515 std::string CUtil::GetSplashPath()
516 {
517   std::array<std::string, 4> candidates {{ "special://home/media/splash.jpg", "special://home/media/splash.png", "special://xbmc/media/splash.jpg", "special://xbmc/media/splash.png" }};
518   auto it = std::find_if(candidates.begin(), candidates.end(), [](std::string const& file) { return XFILE::CFile::Exists(file); });
519   if (it == candidates.end())
520     throw std::runtime_error("No splash image found");
521   return CSpecialProtocol::TranslatePathConvertCase(*it);
522 }
523 
ExcludeFileOrFolder(const std::string & strFileOrFolder,const std::vector<std::string> & regexps)524 bool CUtil::ExcludeFileOrFolder(const std::string& strFileOrFolder, const std::vector<std::string>& regexps)
525 {
526   if (strFileOrFolder.empty())
527     return false;
528 
529   CRegExp regExExcludes(true, CRegExp::autoUtf8);  // case insensitive regex
530 
531   for (const auto &regexp : regexps)
532   {
533     if (!regExExcludes.RegComp(regexp.c_str()))
534     { // invalid regexp - complain in logs
535       CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexp.c_str());
536       continue;
537     }
538     if (regExExcludes.RegFind(strFileOrFolder) > -1)
539     {
540       CLog::LogF(LOGDEBUG, "File '{}' excluded. (Matches exclude rule RegExp: '{}')", CURL::GetRedacted(strFileOrFolder), regexp);
541       return true;
542     }
543   }
544   return false;
545 }
546 
GetFileAndProtocol(const std::string & strURL,std::string & strDir)547 void CUtil::GetFileAndProtocol(const std::string& strURL, std::string& strDir)
548 {
549   strDir = strURL;
550   if (!URIUtils::IsRemote(strURL)) return ;
551   if (URIUtils::IsDVD(strURL)) return ;
552 
553   CURL url(strURL);
554   strDir = StringUtils::Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
555 }
556 
GetDVDIfoTitle(const std::string & strFile)557 int CUtil::GetDVDIfoTitle(const std::string& strFile)
558 {
559   std::string strFilename = URIUtils::GetFileName(strFile);
560   if (StringUtils::EqualsNoCase(strFilename, "video_ts.ifo")) return 0;
561   //VTS_[TITLE]_0.IFO
562   return atoi(strFilename.substr(4, 2).c_str());
563 }
564 
GetFileDigest(const std::string & strPath,KODI::UTILITY::CDigest::Type type)565 std::string CUtil::GetFileDigest(const std::string& strPath, KODI::UTILITY::CDigest::Type type)
566 {
567   CFile file;
568   std::string result;
569   if (file.Open(strPath))
570   {
571     CDigest digest{type};
572     char temp[1024];
573     while (true)
574     {
575       ssize_t read = file.Read(temp,1024);
576       if (read <= 0)
577         break;
578       digest.Update(temp,read);
579     }
580     result = digest.Finalize();
581     file.Close();
582   }
583 
584   return result;
585 }
586 
GetDirectoryName(const std::string & strFileName,std::string & strDescription)587 bool CUtil::GetDirectoryName(const std::string& strFileName, std::string& strDescription)
588 {
589   std::string strFName = URIUtils::GetFileName(strFileName);
590   strDescription = URIUtils::GetDirectory(strFileName);
591   URIUtils::RemoveSlashAtEnd(strDescription);
592 
593   size_t iPos = strDescription.find_last_of("/\\");
594   if (iPos != std::string::npos)
595     strDescription = strDescription.substr(iPos + 1);
596   else if (strDescription.size() <= 0)
597     strDescription = strFName;
598   return true;
599 }
600 
GetDVDDriveIcon(const std::string & strPath,std::string & strIcon)601 void CUtil::GetDVDDriveIcon(const std::string& strPath, std::string& strIcon)
602 {
603   if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath))
604   {
605     strIcon = "DefaultDVDEmpty.png";
606     return ;
607   }
608 
609   CFileItem item = CFileItem(strPath, false);
610 
611   if (item.IsBluray())
612   {
613     strIcon = "DefaultBluray.png";
614     return;
615   }
616 
617   if ( URIUtils::IsDVD(strPath) )
618   {
619     strIcon = "DefaultDVDFull.png";
620     return ;
621   }
622 
623   if ( URIUtils::IsISO9660(strPath) )
624   {
625 #ifdef HAS_DVD_DRIVE
626     CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo();
627     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
628     {
629       strIcon = "DefaultVCD.png";
630       return ;
631     }
632 #endif
633     strIcon = "DefaultDVDRom.png";
634     return ;
635   }
636 
637   if ( URIUtils::IsCDDA(strPath) )
638   {
639     strIcon = "DefaultCDDA.png";
640     return ;
641   }
642 }
643 
RemoveTempFiles()644 void CUtil::RemoveTempFiles()
645 {
646   const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
647 
648   std::string searchPath = profileManager->GetDatabaseFolder();
649   CFileItemList items;
650   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
651     return;
652 
653   for (const auto &item : items)
654   {
655     if (item->m_bIsFolder)
656       continue;
657     XFILE::CFile::Delete(item->GetPath());
658   }
659 }
660 
ClearSubtitles()661 void CUtil::ClearSubtitles()
662 {
663   //delete cached subs
664   CFileItemList items;
665   CDirectory::GetDirectory("special://temp/",items, "", DIR_FLAG_DEFAULTS);
666   for (const auto &item : items)
667   {
668     if (!item->m_bIsFolder)
669     {
670       if (item->GetPath().find("subtitle") != std::string::npos ||
671           item->GetPath().find("vobsub_queue") != std::string::npos)
672       {
673         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, item->GetPath().c_str());
674         CFile::Delete(item->GetPath());
675       }
676     }
677   }
678 }
679 
ClearTempFonts()680 void CUtil::ClearTempFonts()
681 {
682   const std::string searchPath = "special://home/media/Fonts/";
683 
684   if (!CDirectory::Exists(searchPath))
685     return;
686 
687   CFileItemList items;
688   CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE | DIR_FLAG_GET_HIDDEN);
689 
690   for (const auto &item : items)
691   {
692     if (item->m_bIsFolder)
693       continue;
694 
695     if (StringUtils::StartsWithNoCase(URIUtils::GetFileName(item->GetPath()), "tmp.font."))
696       CFile::Delete(item->GetPath());
697   }
698 }
699 
ToInt64(uint32_t high,uint32_t low)700 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
701 {
702   int64_t n;
703   n = high;
704   n <<= 32;
705   n += low;
706   return n;
707 }
708 
709 /*!
710   \brief Finds next unused filename that matches padded int format identifier provided
711   \param[in]  fn_template    filename template consisting of a padded int format identifier (eg screenshot%03d)
712   \param[in]  max            maximum number to search for avaialble name
713   \return "" on failure, string next available name matching format identifier on success
714 */
715 
GetNextFilename(const std::string & fn_template,int max)716 std::string CUtil::GetNextFilename(const std::string &fn_template, int max)
717 {
718   std::string searchPath = URIUtils::GetDirectory(fn_template);
719   std::string mask = URIUtils::GetExtension(fn_template);
720   std::string name = StringUtils::Format(fn_template.c_str(), 0);
721 
722   CFileItemList items;
723   if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
724     return name;
725 
726   items.SetFastLookup(true);
727   for (int i = 0; i <= max; i++)
728   {
729     std::string name = StringUtils::Format(fn_template.c_str(), i);
730     if (!items.Get(name))
731       return name;
732   }
733   return "";
734 }
735 
GetNextPathname(const std::string & path_template,int max)736 std::string CUtil::GetNextPathname(const std::string &path_template, int max)
737 {
738   if (path_template.find("%04d") == std::string::npos)
739     return "";
740 
741   for (int i = 0; i <= max; i++)
742   {
743     std::string name = StringUtils::Format(path_template.c_str(), i);
744     if (!CFile::Exists(name) && !CDirectory::Exists(name))
745       return name;
746   }
747   return "";
748 }
749 
StatToStatI64(struct _stati64 * result,struct stat * stat)750 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
751 {
752   result->st_dev = stat->st_dev;
753   result->st_ino = stat->st_ino;
754   result->st_mode = stat->st_mode;
755   result->st_nlink = stat->st_nlink;
756   result->st_uid = stat->st_uid;
757   result->st_gid = stat->st_gid;
758   result->st_rdev = stat->st_rdev;
759   result->st_size = (int64_t)stat->st_size;
760 
761 #ifndef TARGET_POSIX
762   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
763   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
764   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
765 #else
766   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
767   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
768   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
769 #endif
770 }
771 
Stat64ToStatI64(struct _stati64 * result,struct __stat64 * stat)772 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
773 {
774   result->st_dev = stat->st_dev;
775   result->st_ino = stat->st_ino;
776   result->st_mode = stat->st_mode;
777   result->st_nlink = stat->st_nlink;
778   result->st_uid = stat->st_uid;
779   result->st_gid = stat->st_gid;
780   result->st_rdev = stat->st_rdev;
781   result->st_size = stat->st_size;
782 #ifndef TARGET_POSIX
783   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
784   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
785   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
786 #else
787   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
788   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
789   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
790 #endif
791 }
792 
StatI64ToStat64(struct __stat64 * result,struct _stati64 * stat)793 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
794 {
795   result->st_dev = stat->st_dev;
796   result->st_ino = stat->st_ino;
797   result->st_mode = stat->st_mode;
798   result->st_nlink = stat->st_nlink;
799   result->st_uid = stat->st_uid;
800   result->st_gid = stat->st_gid;
801   result->st_rdev = stat->st_rdev;
802   result->st_size = stat->st_size;
803 #ifndef TARGET_POSIX
804   result->st_atime = stat->st_atime;
805   result->st_mtime = stat->st_mtime;
806   result->st_ctime = stat->st_ctime;
807 #else
808   result->st_atime = stat->_st_atime;
809   result->st_mtime = stat->_st_mtime;
810   result->st_ctime = stat->_st_ctime;
811 #endif
812 }
813 
StatToStat64(struct __stat64 * result,const struct stat * stat)814 void CUtil::StatToStat64(struct __stat64 *result, const struct stat *stat)
815 {
816   memset(result, 0, sizeof(*result));
817   result->st_dev   = stat->st_dev;
818   result->st_ino   = stat->st_ino;
819   result->st_mode  = stat->st_mode;
820   result->st_nlink = stat->st_nlink;
821   result->st_uid   = stat->st_uid;
822   result->st_gid   = stat->st_gid;
823   result->st_rdev  = stat->st_rdev;
824   result->st_size  = stat->st_size;
825   result->st_atime = stat->st_atime;
826   result->st_mtime = stat->st_mtime;
827   result->st_ctime = stat->st_ctime;
828 }
829 
Stat64ToStat(struct stat * result,struct __stat64 * stat)830 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
831 {
832   result->st_dev = stat->st_dev;
833   result->st_ino = stat->st_ino;
834   result->st_mode = stat->st_mode;
835   result->st_nlink = stat->st_nlink;
836   result->st_uid = stat->st_uid;
837   result->st_gid = stat->st_gid;
838   result->st_rdev = stat->st_rdev;
839 #ifndef TARGET_POSIX
840   if (stat->st_size <= LONG_MAX)
841     result->st_size = (_off_t)stat->st_size;
842 #else
843   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
844     result->st_size = stat->st_size;
845 #endif
846   else
847   {
848     result->st_size = 0;
849     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
850   }
851   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
852   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
853   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
854 }
855 
856 #ifdef TARGET_WINDOWS
Stat64ToStat64i32(struct _stat64i32 * result,struct __stat64 * stat)857 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
858 {
859   result->st_dev = stat->st_dev;
860   result->st_ino = stat->st_ino;
861   result->st_mode = stat->st_mode;
862   result->st_nlink = stat->st_nlink;
863   result->st_uid = stat->st_uid;
864   result->st_gid = stat->st_gid;
865   result->st_rdev = stat->st_rdev;
866 #ifndef TARGET_POSIX
867   if (stat->st_size <= LONG_MAX)
868     result->st_size = (_off_t)stat->st_size;
869 #else
870   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
871     result->st_size = stat->st_size;
872 #endif
873   else
874   {
875     result->st_size = 0;
876     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
877   }
878 #ifndef TARGET_POSIX
879   result->st_atime = stat->st_atime;
880   result->st_mtime = stat->st_mtime;
881   result->st_ctime = stat->st_ctime;
882 #else
883   result->st_atime = stat->_st_atime;
884   result->st_mtime = stat->_st_mtime;
885   result->st_ctime = stat->_st_ctime;
886 #endif
887 }
888 #endif
889 
CreateDirectoryEx(const std::string & strPath)890 bool CUtil::CreateDirectoryEx(const std::string& strPath)
891 {
892   // Function to create all directories at once instead
893   // of calling CreateDirectory for every subdir.
894   // Creates the directory and subdirectories if needed.
895 
896   // return true if directory already exist
897   if (CDirectory::Exists(strPath)) return true;
898 
899   // we currently only allow HD and smb and nfs paths
900   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath))
901   {
902     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
903     return false;
904   }
905 
906   std::vector<std::string> dirs = URIUtils::SplitPath(strPath);
907   if (dirs.empty())
908     return false;
909   std::string dir(dirs.front());
910   URIUtils::AddSlashAtEnd(dir);
911   for (std::vector<std::string>::const_iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
912   {
913     dir = URIUtils::AddFileToFolder(dir, *it);
914     CDirectory::Create(dir);
915   }
916 
917   // was the final destination directory successfully created ?
918   return CDirectory::Exists(strPath);
919 }
920 
MakeLegalFileName(const std::string & strFile,int LegalType)921 std::string CUtil::MakeLegalFileName(const std::string &strFile, int LegalType)
922 {
923   std::string result = strFile;
924 
925   StringUtils::Replace(result, '/', '_');
926   StringUtils::Replace(result, '\\', '_');
927   StringUtils::Replace(result, '?', '_');
928 
929   if (LegalType == LEGAL_WIN32_COMPAT)
930   {
931     // just filter out some illegal characters on windows
932     StringUtils::Replace(result, ':', '_');
933     StringUtils::Replace(result, '*', '_');
934     StringUtils::Replace(result, '?', '_');
935     StringUtils::Replace(result, '\"', '_');
936     StringUtils::Replace(result, '<', '_');
937     StringUtils::Replace(result, '>', '_');
938     StringUtils::Replace(result, '|', '_');
939     StringUtils::TrimRight(result, ". ");
940   }
941   return result;
942 }
943 
944 // legalize entire path
MakeLegalPath(const std::string & strPathAndFile,int LegalType)945 std::string CUtil::MakeLegalPath(const std::string &strPathAndFile, int LegalType)
946 {
947   if (URIUtils::IsStack(strPathAndFile))
948     return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
949   if (URIUtils::IsMultiPath(strPathAndFile))
950     return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
951   if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile))
952     return strPathAndFile; // we don't support writing anywhere except HD, SMB and NFS - no need to legalize path
953 
954   bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
955   std::vector<std::string> dirs = URIUtils::SplitPath(strPathAndFile);
956   if (dirs.empty())
957     return strPathAndFile;
958   // we just add first token to path and don't legalize it - possible values:
959   // "X:" (local win32), "" (local unix - empty string before '/') or
960   // "protocol://domain"
961   std::string dir(dirs.front());
962   URIUtils::AddSlashAtEnd(dir);
963   for (std::vector<std::string>::const_iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
964     dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
965   if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
966   return dir;
967 }
968 
ValidatePath(const std::string & path,bool bFixDoubleSlashes)969 std::string CUtil::ValidatePath(const std::string &path, bool bFixDoubleSlashes /* = false */)
970 {
971   std::string result = path;
972 
973   // Don't do any stuff on URLs containing %-characters or protocols that embed
974   // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
975   // recurse and crash XBMC
976   if (URIUtils::IsURL(path) &&
977       (path.find('%') != std::string::npos ||
978       StringUtils::StartsWithNoCase(path, "apk:") ||
979       StringUtils::StartsWithNoCase(path, "zip:") ||
980       StringUtils::StartsWithNoCase(path, "rar:") ||
981       StringUtils::StartsWithNoCase(path, "stack:") ||
982       StringUtils::StartsWithNoCase(path, "bluray:") ||
983       StringUtils::StartsWithNoCase(path, "multipath:") ))
984     return result;
985 
986   // check the path for incorrect slashes
987 #ifdef TARGET_WINDOWS
988   if (URIUtils::IsDOSPath(path))
989   {
990     StringUtils::Replace(result, '/', '\\');
991     /* The double slash correction should only be used when *absolutely*
992        necessary! This applies to certain DLLs or use from Python DLLs/scripts
993        that incorrectly generate double (back) slashes.
994     */
995     if (bFixDoubleSlashes && !result.empty())
996     {
997       // Fixup for double back slashes (but ignore the \\ of unc-paths)
998       for (size_t x = 1; x < result.size() - 1; x++)
999       {
1000         if (result[x] == '\\' && result[x+1] == '\\')
1001           result.erase(x, 1);
1002       }
1003     }
1004   }
1005   else if (path.find("://") != std::string::npos || path.find(":\\\\") != std::string::npos)
1006 #endif
1007   {
1008     StringUtils::Replace(result, '\\', '/');
1009     /* The double slash correction should only be used when *absolutely*
1010        necessary! This applies to certain DLLs or use from Python DLLs/scripts
1011        that incorrectly generate double (back) slashes.
1012     */
1013     if (bFixDoubleSlashes && !result.empty())
1014     {
1015       // Fixup for double forward slashes(/) but don't touch the :// of URLs
1016       for (size_t x = 2; x < result.size() - 1; x++)
1017       {
1018         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
1019           result.erase(x, 1);
1020       }
1021     }
1022   }
1023   return result;
1024 }
1025 
IsUsingTTFSubtitles()1026 bool CUtil::IsUsingTTFSubtitles()
1027 {
1028   return URIUtils::HasExtension(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_FONT), ".ttf");
1029 }
1030 
SplitExecFunction(const std::string & execString,std::string & function,std::vector<std::string> & parameters)1031 void CUtil::SplitExecFunction(const std::string &execString, std::string &function, std::vector<std::string> &parameters)
1032 {
1033   std::string paramString;
1034 
1035   size_t iPos = execString.find('(');
1036   size_t iPos2 = execString.rfind(')');
1037   if (iPos != std::string::npos && iPos2 != std::string::npos)
1038   {
1039     paramString = execString.substr(iPos + 1, iPos2 - iPos - 1);
1040     function = execString.substr(0, iPos);
1041   }
1042   else
1043     function = execString;
1044 
1045   // remove any whitespace, and the standard prefix (if it exists)
1046   StringUtils::Trim(function);
1047 
1048   SplitParams(paramString, parameters);
1049 }
1050 
SplitParams(const std::string & paramString,std::vector<std::string> & parameters)1051 void CUtil::SplitParams(const std::string &paramString, std::vector<std::string> &parameters)
1052 {
1053   bool inQuotes = false;
1054   bool lastEscaped = false; // only every second character can be escaped
1055   int inFunction = 0;
1056   size_t whiteSpacePos = 0;
1057   std::string parameter;
1058   parameters.clear();
1059   for (size_t pos = 0; pos < paramString.size(); pos++)
1060   {
1061     char ch = paramString[pos];
1062     bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1063     lastEscaped = escaped;
1064     if (inQuotes)
1065     { // if we're in a quote, we accept everything until the closing quote
1066       if (ch == '"' && !escaped)
1067       { // finished a quote - no need to add the end quote to our string
1068         inQuotes = false;
1069       }
1070     }
1071     else
1072     { // not in a quote, so check if we should be starting one
1073       if (ch == '"' && !escaped)
1074       { // start of quote - no need to add the quote to our string
1075         inQuotes = true;
1076       }
1077       if (inFunction && ch == ')')
1078       { // end of a function
1079         inFunction--;
1080       }
1081       if (ch == '(')
1082       { // start of function
1083         inFunction++;
1084       }
1085       if (!inFunction && ch == ',')
1086       { // not in a function, so a comma signifies the end of this parameter
1087         if (whiteSpacePos)
1088           parameter = parameter.substr(0, whiteSpacePos);
1089         // trim off start and end quotes
1090         if (parameter.length() > 1 && parameter[0] == '"' && parameter[parameter.length() - 1] == '"')
1091           parameter = parameter.substr(1, parameter.length() - 2);
1092         else if (parameter.length() > 3 && parameter[parameter.length() - 1] == '"')
1093         {
1094           // check name="value" style param.
1095           size_t quotaPos = parameter.find('"');
1096           if (quotaPos > 1 && quotaPos < parameter.length() - 1 && parameter[quotaPos - 1] == '=')
1097           {
1098             parameter.erase(parameter.length() - 1);
1099             parameter.erase(quotaPos);
1100           }
1101         }
1102         parameters.push_back(parameter);
1103         parameter.clear();
1104         whiteSpacePos = 0;
1105         continue;
1106       }
1107     }
1108     if ((ch == '"' || ch == '\\') && escaped)
1109     { // escaped quote or backslash
1110       parameter[parameter.size()-1] = ch;
1111       continue;
1112     }
1113     // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1114     if (ch == ' ' && !inQuotes)
1115     {
1116       if (parameter.empty()) // skip whitespace on left
1117         continue;
1118       if (!whiteSpacePos) // make a note of where whitespace starts on the right
1119         whiteSpacePos = parameter.size();
1120     }
1121     else
1122       whiteSpacePos = 0;
1123     parameter += ch;
1124   }
1125   if (inFunction || inQuotes)
1126     CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
1127   if (whiteSpacePos)
1128     parameter.erase(whiteSpacePos);
1129   // trim off start and end quotes
1130   if (parameter.size() > 1 && parameter[0] == '"' && parameter[parameter.size() - 1] == '"')
1131     parameter = parameter.substr(1,parameter.size() - 2);
1132   else if (parameter.size() > 3 && parameter[parameter.size() - 1] == '"')
1133   {
1134     // check name="value" style param.
1135     size_t quotaPos = parameter.find('"');
1136     if (quotaPos > 1 && quotaPos < parameter.length() - 1 && parameter[quotaPos - 1] == '=')
1137     {
1138       parameter.erase(parameter.length() - 1);
1139       parameter.erase(quotaPos);
1140     }
1141   }
1142   if (!parameter.empty() || parameters.size())
1143     parameters.push_back(parameter);
1144 }
1145 
GetMatchingSource(const std::string & strPath1,VECSOURCES & VECSOURCES,bool & bIsSourceName)1146 int CUtil::GetMatchingSource(const std::string& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1147 {
1148   if (strPath1.empty())
1149     return -1;
1150 
1151   // copy as we may change strPath
1152   std::string strPath = strPath1;
1153 
1154   // Check for special protocols
1155   CURL checkURL(strPath);
1156 
1157   if (StringUtils::StartsWith(strPath, "special://skin/"))
1158     return 1;
1159 
1160   // stack://
1161   if (checkURL.IsProtocol("stack"))
1162     strPath.erase(0, 8); // remove the stack protocol
1163 
1164   if (checkURL.IsProtocol("shout"))
1165     strPath = checkURL.GetHostName();
1166 
1167   // a plugin path should not be configured in any mediasource
1168   if (checkURL.IsProtocol("plugin"))
1169     return -1;
1170 
1171   if (checkURL.IsProtocol("multipath"))
1172     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1173 
1174   bIsSourceName = false;
1175   int iIndex = -1;
1176 
1177   // we first test the NAME of a source
1178   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1179   {
1180     const CMediaSource &share = VECSOURCES[i];
1181     std::string strName = share.strName;
1182 
1183     // special cases for dvds
1184     if (URIUtils::IsOnDVD(share.strPath))
1185     {
1186       if (URIUtils::IsOnDVD(strPath))
1187         return i;
1188 
1189       // not a path, so we need to modify the source name
1190       // since we add the drive status and disc name to the source
1191       // "Name (Drive Status/Disc Name)"
1192       size_t iPos = strName.rfind('(');
1193       if (iPos != std::string::npos && iPos > 1)
1194         strName = strName.substr(0, iPos - 1);
1195     }
1196     if (StringUtils::EqualsNoCase(strPath, strName))
1197     {
1198       bIsSourceName = true;
1199       return i;
1200     }
1201   }
1202 
1203   // now test the paths
1204 
1205   // remove user details, and ensure path only uses forward slashes
1206   // and ends with a trailing slash so as not to match a substring
1207   CURL urlDest(strPath);
1208   urlDest.SetOptions("");
1209   urlDest.SetProtocolOptions("");
1210   std::string strDest = urlDest.GetWithoutUserDetails();
1211   ForceForwardSlashes(strDest);
1212   if (!URIUtils::HasSlashAtEnd(strDest))
1213     strDest += "/";
1214 
1215   size_t iLength = 0;
1216   size_t iLenPath = strDest.size();
1217   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1218   {
1219     const CMediaSource &share = VECSOURCES[i];
1220 
1221     // does it match a source name?
1222     if (share.strPath.substr(0,8) == "shout://")
1223     {
1224       CURL url(share.strPath);
1225       if (strPath == url.GetHostName())
1226         return i;
1227     }
1228 
1229     // doesn't match a name, so try the source path
1230     std::vector<std::string> vecPaths;
1231 
1232     // add any concatenated paths if they exist
1233     if (!share.vecPaths.empty())
1234       vecPaths = share.vecPaths;
1235 
1236     // add the actual share path at the front of the vector
1237     vecPaths.insert(vecPaths.begin(), share.strPath);
1238 
1239     // test each path
1240     for (const auto &path : vecPaths)
1241     {
1242       // remove user details, and ensure path only uses forward slashes
1243       // and ends with a trailing slash so as not to match a substring
1244       CURL urlShare(path);
1245       urlShare.SetOptions("");
1246       urlShare.SetProtocolOptions("");
1247       std::string strShare = urlShare.GetWithoutUserDetails();
1248       ForceForwardSlashes(strShare);
1249       if (!URIUtils::HasSlashAtEnd(strShare))
1250         strShare += "/";
1251       size_t iLenShare = strShare.size();
1252 
1253       if ((iLenPath >= iLenShare) && StringUtils::StartsWithNoCase(strDest, strShare) && (iLenShare > iLength))
1254       {
1255         // if exact match, return it immediately
1256         if (iLenPath == iLenShare)
1257         {
1258           // if the path EXACTLY matches an item in a concatenated path
1259           // set source name to true to load the full virtualpath
1260           bIsSourceName = false;
1261           if (vecPaths.size() > 1)
1262             bIsSourceName = true;
1263           return i;
1264         }
1265         iIndex = i;
1266         iLength = iLenShare;
1267       }
1268     }
1269   }
1270 
1271   // return the index of the share with the longest match
1272   if (iIndex == -1)
1273   {
1274 
1275     // rar:// and zip://
1276     // if archive wasn't mounted, look for a matching share for the archive instead
1277     if( StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://") )
1278     {
1279       // get the hostname portion of the url since it contains the archive file
1280       strPath = checkURL.GetHostName();
1281 
1282       bIsSourceName = false;
1283       bool bDummy;
1284       return GetMatchingSource(strPath, VECSOURCES, bDummy);
1285     }
1286 
1287     CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource: no matching source found for [%s]", strPath1.c_str());
1288   }
1289   return iIndex;
1290 }
1291 
TranslateSpecialSource(const std::string & strSpecial)1292 std::string CUtil::TranslateSpecialSource(const std::string &strSpecial)
1293 {
1294   if (!strSpecial.empty() && strSpecial[0] == '$')
1295   {
1296     if (StringUtils::StartsWithNoCase(strSpecial, "$home"))
1297       return URIUtils::AddFileToFolder("special://home/", strSpecial.substr(5));
1298     else if (StringUtils::StartsWithNoCase(strSpecial, "$subtitles"))
1299       return URIUtils::AddFileToFolder("special://subtitles/", strSpecial.substr(10));
1300     else if (StringUtils::StartsWithNoCase(strSpecial, "$userdata"))
1301       return URIUtils::AddFileToFolder("special://userdata/", strSpecial.substr(9));
1302     else if (StringUtils::StartsWithNoCase(strSpecial, "$database"))
1303       return URIUtils::AddFileToFolder("special://database/", strSpecial.substr(9));
1304     else if (StringUtils::StartsWithNoCase(strSpecial, "$thumbnails"))
1305       return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.substr(11));
1306     else if (StringUtils::StartsWithNoCase(strSpecial, "$recordings"))
1307       return URIUtils::AddFileToFolder("special://recordings/", strSpecial.substr(11));
1308     else if (StringUtils::StartsWithNoCase(strSpecial, "$screenshots"))
1309       return URIUtils::AddFileToFolder("special://screenshots/", strSpecial.substr(12));
1310     else if (StringUtils::StartsWithNoCase(strSpecial, "$musicplaylists"))
1311       return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.substr(15));
1312     else if (StringUtils::StartsWithNoCase(strSpecial, "$videoplaylists"))
1313       return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.substr(15));
1314     else if (StringUtils::StartsWithNoCase(strSpecial, "$cdrips"))
1315       return URIUtils::AddFileToFolder("special://cdrips/", strSpecial.substr(7));
1316     // this one will be removed post 2.0
1317     else if (StringUtils::StartsWithNoCase(strSpecial, "$playlists"))
1318       return URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH), strSpecial.substr(10));
1319   }
1320   return strSpecial;
1321 }
1322 
MusicPlaylistsLocation()1323 std::string CUtil::MusicPlaylistsLocation()
1324 {
1325   const std::string path = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
1326   std::vector<std::string> vec;
1327   vec.push_back(URIUtils::AddFileToFolder(path, "music"));
1328   vec.push_back(URIUtils::AddFileToFolder(path, "mixed"));
1329   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1330 }
1331 
VideoPlaylistsLocation()1332 std::string CUtil::VideoPlaylistsLocation()
1333 {
1334   const std::string path = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
1335   std::vector<std::string> vec;
1336   vec.push_back(URIUtils::AddFileToFolder(path, "video"));
1337   vec.push_back(URIUtils::AddFileToFolder(path, "mixed"));
1338   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1339 }
1340 
DeleteMusicDatabaseDirectoryCache()1341 void CUtil::DeleteMusicDatabaseDirectoryCache()
1342 {
1343   CUtil::DeleteDirectoryCache("mdb-");
1344   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1345 }
1346 
DeleteVideoDatabaseDirectoryCache()1347 void CUtil::DeleteVideoDatabaseDirectoryCache()
1348 {
1349   CUtil::DeleteDirectoryCache("vdb-");
1350   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1351 }
1352 
DeleteDirectoryCache(const std::string & prefix)1353 void CUtil::DeleteDirectoryCache(const std::string &prefix)
1354 {
1355   std::string searchPath = "special://temp/";
1356   CFileItemList items;
1357   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", DIR_FLAG_NO_FILE_DIRS))
1358     return;
1359 
1360   for (const auto &item : items)
1361   {
1362     if (item->m_bIsFolder)
1363       continue;
1364     std::string fileName = URIUtils::GetFileName(item->GetPath());
1365     if (StringUtils::StartsWith(fileName, prefix))
1366       XFILE::CFile::Delete(item->GetPath());
1367   }
1368 }
1369 
1370 
GetRecursiveListing(const std::string & strPath,CFileItemList & items,const std::string & strMask,unsigned int flags)1371 void CUtil::GetRecursiveListing(const std::string& strPath, CFileItemList& items, const std::string& strMask, unsigned int flags /* = DIR_FLAG_DEFAULTS */)
1372 {
1373   CFileItemList myItems;
1374   CDirectory::GetDirectory(strPath,myItems,strMask,flags);
1375   for (const auto &item : myItems)
1376   {
1377     if (item->m_bIsFolder)
1378       CUtil::GetRecursiveListing(item->GetPath(),items,strMask,flags);
1379     else
1380       items.Add(item);
1381   }
1382 }
1383 
GetRecursiveDirsListing(const std::string & strPath,CFileItemList & item,unsigned int flags)1384 void CUtil::GetRecursiveDirsListing(const std::string& strPath, CFileItemList& item, unsigned int flags /* = DIR_FLAG_DEFAULTS */)
1385 {
1386   CFileItemList myItems;
1387   CDirectory::GetDirectory(strPath,myItems,"",flags);
1388   for (const auto &i : myItems)
1389   {
1390     if (i->m_bIsFolder && !i->IsPath(".."))
1391     {
1392       item.Add(i);
1393       CUtil::GetRecursiveDirsListing(i->GetPath(),item,flags);
1394     }
1395   }
1396 }
1397 
ForceForwardSlashes(std::string & strPath)1398 void CUtil::ForceForwardSlashes(std::string& strPath)
1399 {
1400   size_t iPos = strPath.rfind('\\');
1401   while (iPos != std::string::npos)
1402   {
1403     strPath.at(iPos) = '/';
1404     iPos = strPath.rfind('\\');
1405   }
1406 }
1407 
AlbumRelevance(const std::string & strAlbumTemp1,const std::string & strAlbum1,const std::string & strArtistTemp1,const std::string & strArtist1)1408 double CUtil::AlbumRelevance(const std::string& strAlbumTemp1, const std::string& strAlbum1, const std::string& strArtistTemp1, const std::string& strArtist1)
1409 {
1410   // case-insensitive fuzzy string comparison on the album and artist for relevance
1411   // weighting is identical, both album and artist are 50% of the total relevance
1412   // a missing artist means the maximum relevance can only be 0.50
1413   std::string strAlbumTemp = strAlbumTemp1;
1414   StringUtils::ToLower(strAlbumTemp);
1415   std::string strAlbum = strAlbum1;
1416   StringUtils::ToLower(strAlbum);
1417   double fAlbumPercentage = fstrcmp(strAlbumTemp.c_str(), strAlbum.c_str());
1418   double fArtistPercentage = 0.0f;
1419   if (!strArtist1.empty())
1420   {
1421     std::string strArtistTemp = strArtistTemp1;
1422     StringUtils::ToLower(strArtistTemp);
1423     std::string strArtist = strArtist1;
1424     StringUtils::ToLower(strArtist);
1425     fArtistPercentage = fstrcmp(strArtistTemp.c_str(), strArtist.c_str());
1426   }
1427   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1428   return fRelevance;
1429 }
1430 
MakeShortenPath(std::string StrInput,std::string & StrOutput,size_t iTextMaxLength)1431 bool CUtil::MakeShortenPath(std::string StrInput, std::string& StrOutput, size_t iTextMaxLength)
1432 {
1433   size_t iStrInputSize = StrInput.size();
1434   if(iStrInputSize <= 0 || iTextMaxLength >= iStrInputSize)
1435   {
1436     StrOutput = StrInput;
1437     return true;
1438   }
1439 
1440   char cDelim = '\0';
1441   size_t nGreaterDelim, nPos;
1442 
1443   nPos = StrInput.find_last_of( '\\' );
1444   if (nPos != std::string::npos)
1445     cDelim = '\\';
1446   else
1447   {
1448     nPos = StrInput.find_last_of( '/' );
1449     if (nPos != std::string::npos)
1450       cDelim = '/';
1451   }
1452   if ( cDelim == '\0' )
1453     return false;
1454 
1455   if (nPos == StrInput.size() - 1)
1456   {
1457     StrInput.erase(StrInput.size() - 1);
1458     nPos = StrInput.find_last_of(cDelim);
1459   }
1460   while( iTextMaxLength < iStrInputSize )
1461   {
1462     nPos = StrInput.find_last_of( cDelim, nPos );
1463     nGreaterDelim = nPos;
1464 
1465     if (nPos == std::string::npos || nPos == 0)
1466       break;
1467 
1468     nPos = StrInput.find_last_of( cDelim, nPos - 1 );
1469 
1470     if ( nPos == std::string::npos )
1471       break;
1472     if ( nGreaterDelim > nPos )
1473       StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
1474     iStrInputSize = StrInput.size();
1475   }
1476   // replace any additional /../../ with just /../ if necessary
1477   std::string replaceDots = StringUtils::Format("..%c..", cDelim);
1478   while (StrInput.size() > (unsigned int)iTextMaxLength)
1479     if (!StringUtils::Replace(StrInput, replaceDots, ".."))
1480       break;
1481   // finally, truncate our string to force inside our max text length,
1482   // replacing the last 2 characters with ".."
1483 
1484   // eg end up with:
1485   // "smb://../Playboy Swimsuit Cal.."
1486   if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1487   {
1488     StrInput.erase(iTextMaxLength - 2);
1489     StrInput += "..";
1490   }
1491   StrOutput = StrInput;
1492   return true;
1493 }
1494 
SupportsWriteFileOperations(const std::string & strPath)1495 bool CUtil::SupportsWriteFileOperations(const std::string& strPath)
1496 {
1497   // currently only hd, smb, nfs and dav support delete and rename
1498   if (URIUtils::IsHD(strPath))
1499     return true;
1500   if (URIUtils::IsSmb(strPath))
1501     return true;
1502   if (URIUtils::IsPVRRecording(strPath))
1503     return CPVRDirectory::SupportsWriteFileOperations(strPath);
1504   if (URIUtils::IsNfs(strPath))
1505     return true;
1506   if (URIUtils::IsDAV(strPath))
1507     return true;
1508   if (URIUtils::IsStack(strPath))
1509     return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
1510   if (URIUtils::IsMultiPath(strPath))
1511     return CMultiPathDirectory::SupportsWriteFileOperations(strPath);
1512 
1513 
1514   if (CServiceBroker::IsBinaryAddonCacheUp())
1515   {
1516     CURL url(strPath);
1517     for (const auto& addon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
1518     {
1519       const auto& info = addon->GetProtocolInfo();
1520       auto prots = StringUtils::Split(info.type, "|");
1521       if (info.supportWrite &&
1522           std::find(prots.begin(), prots.end(), url.GetProtocol()) != prots.end())
1523         return true;
1524     }
1525   }
1526 
1527   return false;
1528 }
1529 
SupportsReadFileOperations(const std::string & strPath)1530 bool CUtil::SupportsReadFileOperations(const std::string& strPath)
1531 {
1532   return !URIUtils::IsVideoDb(strPath);
1533 }
1534 
GetDefaultFolderThumb(const std::string & folderThumb)1535 std::string CUtil::GetDefaultFolderThumb(const std::string &folderThumb)
1536 {
1537   if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(folderThumb))
1538     return folderThumb;
1539   return "";
1540 }
1541 
GetSkinThemes(std::vector<std::string> & vecTheme)1542 void CUtil::GetSkinThemes(std::vector<std::string>& vecTheme)
1543 {
1544   static const std::string TexturesXbt = "Textures.xbt";
1545 
1546   std::string strPath = URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media");
1547   CFileItemList items;
1548   CDirectory::GetDirectory(strPath, items, "", DIR_FLAG_DEFAULTS);
1549   // Search for Themes in the Current skin!
1550   for (const auto &pItem : items)
1551   {
1552     if (!pItem->m_bIsFolder)
1553     {
1554       std::string strExtension = URIUtils::GetExtension(pItem->GetPath());
1555       std::string strLabel = pItem->GetLabel();
1556       if ((strExtension == ".xbt" && !StringUtils::EqualsNoCase(strLabel, TexturesXbt)))
1557         vecTheme.push_back(StringUtils::Left(strLabel, strLabel.size() - strExtension.size()));
1558     }
1559     else
1560     {
1561       // check if this is an xbt:// VFS path
1562       CURL itemUrl(pItem->GetPath());
1563       if (!itemUrl.IsProtocol("xbt") || !itemUrl.GetFileName().empty())
1564         continue;
1565 
1566       std::string strLabel = URIUtils::GetFileName(itemUrl.GetHostName());
1567       if (!StringUtils::EqualsNoCase(strLabel, TexturesXbt))
1568         vecTheme.push_back(StringUtils::Left(strLabel, strLabel.size() - URIUtils::GetExtension(strLabel).size()));
1569     }
1570   }
1571   std::sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1572 }
1573 
InitRandomSeed()1574 void CUtil::InitRandomSeed()
1575 {
1576   // Init random seed
1577   int64_t now;
1578   now = CurrentHostCounter();
1579   unsigned int seed = (unsigned int)now;
1580 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1581   srand(seed);
1582 }
1583 
1584 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN_TVOS)
RunCommandLine(const std::string & cmdLine,bool waitExit)1585 bool CUtil::RunCommandLine(const std::string& cmdLine, bool waitExit)
1586 {
1587   std::vector<std::string> args = StringUtils::Split(cmdLine, ",");
1588 
1589   // Strip quotes and whitespace around the arguments, or exec will fail.
1590   // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1591   // But it's still limited, for example quotes inside the strings are not expanded, etc.
1592   //! @todo Maybe some python library routine can parse this more properly ?
1593   for (std::vector<std::string>::iterator it = args.begin(); it != args.end(); ++it)
1594   {
1595     size_t pos;
1596     pos = it->find_first_not_of(" \t\n\"'");
1597     if (pos != std::string::npos)
1598     {
1599       it->erase(0, pos);
1600     }
1601 
1602     pos = it->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1603     {
1604       it->erase(++pos, it->size());
1605     }
1606   }
1607 
1608   return Command(args, waitExit);
1609 }
1610 
Command(const std::vector<std::string> & arrArgs,bool waitExit)1611 bool CUtil::Command(const std::vector<std::string>& arrArgs, bool waitExit)
1612 {
1613 #ifdef _DEBUG
1614   printf("Executing: ");
1615   for (const auto &arg : arrArgs)
1616     printf("%s ", arg.c_str());
1617   printf("\n");
1618 #endif
1619 
1620   pid_t child = fork();
1621   int n = 0;
1622   if (child == 0)
1623   {
1624     if (!waitExit)
1625     {
1626       // fork again in order not to leave a zombie process
1627       child = fork();
1628       if (child == -1)
1629         _exit(2);
1630       else if (child != 0)
1631         _exit(0);
1632     }
1633     close(0);
1634     close(1);
1635     close(2);
1636     if (!arrArgs.empty())
1637     {
1638       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1639       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1640       for (size_t i=0; i<arrArgs.size(); i++)
1641         args[i] = const_cast<char *>(arrArgs[i].c_str());
1642       execvp(args[0], args);
1643     }
1644   }
1645   else
1646   {
1647     waitpid(child, &n, 0);
1648   }
1649 
1650   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
1651 }
1652 #endif
1653 
LookupRomanDigit(char roman_digit)1654 int CUtil::LookupRomanDigit(char roman_digit)
1655 {
1656   switch (roman_digit)
1657   {
1658     case 'i':
1659     case 'I':
1660       return 1;
1661     case 'v':
1662     case 'V':
1663       return 5;
1664     case 'x':
1665     case 'X':
1666       return 10;
1667     case 'l':
1668     case 'L':
1669       return 50;
1670     case 'c':
1671     case 'C':
1672       return 100;
1673     case 'd':
1674     case 'D':
1675       return 500;
1676     case 'm':
1677     case 'M':
1678       return 1000;
1679     default:
1680       return 0;
1681   }
1682 }
1683 
TranslateRomanNumeral(const char * roman_numeral)1684 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
1685 {
1686 
1687   int decimal = -1;
1688 
1689   if (roman_numeral && roman_numeral[0])
1690   {
1691     int temp_sum  = 0,
1692         last      = 0,
1693         repeat    = 0,
1694         trend     = 1;
1695     decimal = 0;
1696     while (*roman_numeral)
1697     {
1698       int digit = CUtil::LookupRomanDigit(*roman_numeral);
1699       int test  = last;
1700 
1701       // General sanity checks
1702 
1703       // numeral not in LUT
1704       if (!digit)
1705         return -1;
1706 
1707       while (test > 5)
1708         test /= 10;
1709 
1710       // N = 10^n may not precede (N+1) > 10^(N+1)
1711       if (test == 1 && digit > last * 10)
1712         return -1;
1713 
1714       // N = 5*10^n may not precede (N+1) >= N
1715       if (test == 5 && digit >= last)
1716         return -1;
1717 
1718       // End general sanity checks
1719 
1720       if (last < digit)
1721       {
1722         // smaller numerals may not repeat before a larger one
1723         if (repeat)
1724           return -1;
1725 
1726         temp_sum += digit;
1727 
1728         repeat  = 0;
1729         trend   = 0;
1730       }
1731       else if (last == digit)
1732       {
1733         temp_sum += digit;
1734         repeat++;
1735         trend = 1;
1736       }
1737       else
1738       {
1739         if (!repeat)
1740           decimal += 2 * last - temp_sum;
1741         else
1742           decimal += temp_sum;
1743 
1744         temp_sum = digit;
1745 
1746         trend   = 1;
1747         repeat  = 0;
1748       }
1749       // Post general sanity checks
1750 
1751       // numerals may not repeat more than thrice
1752       if (repeat == 3)
1753         return -1;
1754 
1755       last = digit;
1756       roman_numeral++;
1757     }
1758 
1759     if (trend)
1760       decimal += temp_sum;
1761     else
1762       decimal += 2 * last - temp_sum;
1763   }
1764   return decimal;
1765 }
1766 
ResolveExecutablePath()1767 std::string CUtil::ResolveExecutablePath()
1768 {
1769   std::string strExecutablePath;
1770 #ifdef TARGET_WINDOWS
1771   static const size_t bufSize = MAX_PATH * 2;
1772   wchar_t* buf = new wchar_t[bufSize];
1773   buf[0] = 0;
1774   ::GetModuleFileNameW(0, buf, bufSize);
1775   buf[bufSize-1] = 0;
1776   g_charsetConverter.wToUTF8(buf,strExecutablePath);
1777   delete[] buf;
1778 #elif defined(TARGET_DARWIN)
1779   char     given_path[2*MAXPATHLEN];
1780   size_t path_size =2*MAXPATHLEN;
1781 
1782   CDarwinUtils::GetExecutablePath(given_path, &path_size);
1783   strExecutablePath = given_path;
1784 #elif (defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
1785   char buf[PATH_MAX];
1786   size_t buflen;
1787   int mib[4];
1788 
1789   mib[0] = CTL_KERN;
1790   mib[1] = KERN_PROC;
1791   mib[2] = KERN_PROC_PATHNAME;
1792   mib[3] = getpid();
1793 
1794   buflen = sizeof(buf) - 1;
1795   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
1796     strExecutablePath = "";
1797   else
1798     strExecutablePath = buf;
1799 #elif defined(TARGET_ANDROID)
1800   strExecutablePath = CXBMCApp::getApplicationInfo().nativeLibraryDir;
1801 
1802   std::string appName = CCompileInfo::GetAppName();
1803   std::string libName = "lib" + appName + ".so";
1804   StringUtils::ToLower(libName);
1805   strExecutablePath += "/" + libName;
1806 #else
1807   /* Get our PID and build the name of the link in /proc */
1808   pid_t pid = getpid();
1809   char linkname[64]; /* /proc/<pid>/exe */
1810   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
1811 
1812   /* Now read the symbolic link */
1813   char buf[PATH_MAX + 1];
1814   buf[0] = 0;
1815 
1816   int ret = readlink(linkname, buf, sizeof(buf) - 1);
1817   if (ret != -1)
1818     buf[ret] = 0;
1819 
1820   strExecutablePath = buf;
1821 #endif
1822   return strExecutablePath;
1823 }
1824 
GetFrameworksPath(bool forPython)1825 std::string CUtil::GetFrameworksPath(bool forPython)
1826 {
1827   std::string strFrameworksPath;
1828 #if defined(TARGET_DARWIN)
1829   strFrameworksPath = CDarwinUtils::GetFrameworkPath(forPython);
1830 #endif
1831   return strFrameworksPath;
1832 }
1833 
GetVideoBasePathAndFileName(const std::string & videoPath,std::string & basePath,std::string & videoFileName)1834 void CUtil::GetVideoBasePathAndFileName(const std::string& videoPath, std::string& basePath, std::string& videoFileName)
1835 {
1836   CFileItem item(videoPath, false);
1837   videoFileName = URIUtils::ReplaceExtension(URIUtils::GetFileName(videoPath), "");
1838 
1839   if (item.HasVideoInfoTag())
1840     basePath = item.GetVideoInfoTag()->m_basePath;
1841 
1842   if (basePath.empty() && item.IsOpticalMediaFile())
1843     basePath = item.GetLocalMetadataPath();
1844 
1845   CURL url(videoPath);
1846   if (basePath.empty() && url.IsProtocol("bluray"))
1847   {
1848     basePath = url.GetHostName();
1849     videoFileName = URIUtils::ReplaceExtension(GetTitleFromPath(url.GetHostName()), "");
1850 
1851     url = CURL(url.GetHostName());
1852     if (url.IsProtocol("udf"))
1853       basePath = URIUtils::GetParentPath(url.GetHostName());
1854   }
1855 
1856   if (basePath.empty())
1857     basePath = URIUtils::GetBasePath(videoPath);
1858 }
1859 
GetItemsToScan(const std::string & videoPath,const std::string & item_exts,const std::vector<std::string> & sub_dirs,CFileItemList & items)1860 void CUtil::GetItemsToScan(const std::string& videoPath,
1861                            const std::string& item_exts,
1862                            const std::vector<std::string>& sub_dirs,
1863                            CFileItemList& items)
1864 {
1865   int flags = DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO;
1866 
1867   if (!videoPath.empty())
1868     CDirectory::GetDirectory(videoPath, items, item_exts, flags);
1869 
1870   std::vector<std::string> additionalPaths;
1871   for (const auto &item : items)
1872   {
1873     for (const auto& subdir : sub_dirs)
1874     {
1875       if (StringUtils::EqualsNoCase(item->GetLabel(), subdir))
1876         additionalPaths.push_back(item->GetPath());
1877     }
1878   }
1879 
1880   for (std::vector<std::string>::const_iterator it = additionalPaths.begin(); it != additionalPaths.end(); ++it)
1881   {
1882     CFileItemList moreItems;
1883     CDirectory::GetDirectory(*it, moreItems, item_exts, flags);
1884     items.Append(moreItems);
1885   }
1886 }
1887 
1888 
ScanPathsForAssociatedItems(const std::string & videoName,const CFileItemList & items,const std::vector<std::string> & item_exts,std::vector<std::string> & associatedFiles)1889 void CUtil::ScanPathsForAssociatedItems(const std::string& videoName,
1890                                         const CFileItemList& items,
1891                                         const std::vector<std::string>& item_exts,
1892                                         std::vector<std::string>& associatedFiles)
1893 {
1894   for (const auto &pItem : items)
1895   {
1896     if (pItem->m_bIsFolder)
1897       continue;
1898 
1899     std::string strCandidate = URIUtils::GetFileName(pItem->GetPath());
1900 
1901     // skip duplicates
1902     if (std::find(associatedFiles.begin(), associatedFiles.end(), pItem->GetPath()) != associatedFiles.end())
1903       continue;
1904 
1905     URIUtils::RemoveExtension(strCandidate);
1906     // NOTE: We don't know if one of videoName or strCandidate is URL-encoded and the other is not, so try both
1907     if (StringUtils::StartsWithNoCase(strCandidate, videoName) || (StringUtils::StartsWithNoCase(strCandidate, CURL::Decode(videoName))))
1908     {
1909       if (URIUtils::IsRAR(pItem->GetPath()) || URIUtils::IsZIP(pItem->GetPath()))
1910         CUtil::ScanArchiveForAssociatedItems(pItem->GetPath(), "", item_exts, associatedFiles);
1911       else
1912       {
1913         associatedFiles.push_back(pItem->GetPath());
1914         CLog::Log(LOGINFO, "%s: found associated file %s", __FUNCTION__,
1915                   CURL::GetRedacted(pItem->GetPath()).c_str());
1916       }
1917     }
1918     else
1919     {
1920       if (URIUtils::IsRAR(pItem->GetPath()) || URIUtils::IsZIP(pItem->GetPath()))
1921         CUtil::ScanArchiveForAssociatedItems(pItem->GetPath(), videoName, item_exts, associatedFiles);
1922     }
1923   }
1924 }
1925 
ScanArchiveForAssociatedItems(const std::string & strArchivePath,const std::string & videoNameNoExt,const std::vector<std::string> & item_exts,std::vector<std::string> & associatedFiles)1926 int CUtil::ScanArchiveForAssociatedItems(const std::string& strArchivePath,
1927                                          const std::string& videoNameNoExt,
1928                                          const std::vector<std::string>& item_exts,
1929                                          std::vector<std::string>& associatedFiles)
1930 {
1931   CLog::LogF(LOGDEBUG, "Scanning archive %s", CURL::GetRedacted(strArchivePath).c_str());
1932   int nItemsAdded = 0;
1933   CFileItemList ItemList;
1934 
1935   // zip only gets the root dir
1936   if (URIUtils::HasExtension(strArchivePath, ".zip"))
1937   {
1938     CURL pathToUrl(strArchivePath);
1939     CURL zipURL = URIUtils::CreateArchivePath("zip", pathToUrl, "");
1940     if (!CDirectory::GetDirectory(zipURL, ItemList, "", DIR_FLAG_NO_FILE_DIRS))
1941       return false;
1942   }
1943   else if (URIUtils::HasExtension(strArchivePath, ".rar"))
1944   {
1945     CURL pathToUrl(strArchivePath);
1946     CURL rarURL = URIUtils::CreateArchivePath("rar", pathToUrl, "");
1947     if (!CDirectory::GetDirectory(rarURL, ItemList, "", DIR_FLAG_NO_FILE_DIRS))
1948       return false;
1949   }
1950   for (const auto &item : ItemList)
1951   {
1952     std::string strPathInRar = item->GetPath();
1953     std::string strExt = URIUtils::GetExtension(strPathInRar);
1954 
1955     // Check another archive in archive
1956     if (strExt == ".zip" || strExt == ".rar")
1957     {
1958       nItemsAdded +=
1959           ScanArchiveForAssociatedItems(strPathInRar, videoNameNoExt, item_exts, associatedFiles);
1960       continue;
1961     }
1962 
1963     // check that the found filename matches the movie filename
1964     size_t fnl = videoNameNoExt.size();
1965     // NOTE: We don't know if videoNameNoExt is URL-encoded, so try both
1966     if (fnl &&
1967       !(StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), videoNameNoExt) ||
1968         StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), CURL::Decode(videoNameNoExt))))
1969       continue;
1970 
1971     for (const auto& ext : item_exts)
1972     {
1973       if (StringUtils::EqualsNoCase(strExt, ext))
1974       {
1975         CLog::Log(LOGINFO, "%s: found associated file %s", __FUNCTION__,
1976                   CURL::GetRedacted(strPathInRar).c_str());
1977         associatedFiles.push_back(strPathInRar);
1978         nItemsAdded++;
1979         break;
1980       }
1981     }
1982   }
1983 
1984   return nItemsAdded;
1985 }
1986 
ScanForExternalSubtitles(const std::string & strMovie,std::vector<std::string> & vecSubtitles)1987 void CUtil::ScanForExternalSubtitles(const std::string& strMovie, std::vector<std::string>& vecSubtitles)
1988 {
1989   unsigned int startTimer = XbmcThreads::SystemClockMillis();
1990 
1991   CFileItem item(strMovie, false);
1992   if ((item.IsInternetStream() && !URIUtils::IsOnLAN(item.GetDynPath()))
1993     || item.IsPlayList()
1994     || item.IsLiveTV()
1995     || !item.IsVideo())
1996     return;
1997 
1998   CLog::Log(LOGDEBUG, "%s: Searching for subtitles...", __FUNCTION__);
1999 
2000   std::string strBasePath;
2001   std::string strSubtitle;
2002 
2003   GetVideoBasePathAndFileName(strMovie, strBasePath, strSubtitle);
2004 
2005   CFileItemList items;
2006   const std::vector<std::string> common_sub_dirs = { "subs", "subtitles", "vobsubs", "sub", "vobsub", "subtitle" };
2007   const std::string subtitleExtensions = CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions();
2008   GetItemsToScan(strBasePath, subtitleExtensions, common_sub_dirs, items);
2009 
2010   const std::string customPath = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH);
2011 
2012   if (!CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() && !customPath.empty()) // to avoid checking non-existent directories (network) every time..
2013   {
2014     if (!CServiceBroker::GetNetwork().IsAvailable() && !URIUtils::IsHD(customPath))
2015     {
2016       CLog::Log(LOGINFO, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's inaccessible");
2017       CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2018     }
2019     else if (!CDirectory::Exists(customPath))
2020     {
2021       CLog::Log(LOGINFO, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistent");
2022       CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2023     }
2024 
2025     CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(1);
2026   }
2027 
2028   std::vector<std::string> strLookInPaths;
2029   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
2030   if (CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() == 1)
2031   {
2032     std::string strPath2 = customPath;
2033     URIUtils::AddSlashAtEnd(strPath2);
2034     strLookInPaths.push_back(strPath2);
2035   }
2036 
2037   int flags = DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO;
2038   for (const std::string& path : strLookInPaths)
2039   {
2040     CFileItemList moreItems;
2041     CDirectory::GetDirectory(path, moreItems, subtitleExtensions, flags);
2042     items.Append(moreItems);
2043   }
2044 
2045   std::vector<std::string> exts = StringUtils::Split(subtitleExtensions, '|');
2046   exts.erase(std::remove(exts.begin(), exts.end(), ".zip"), exts.end());
2047   exts.erase(std::remove(exts.begin(), exts.end(), ".rar"), exts.end());
2048 
2049   ScanPathsForAssociatedItems(strSubtitle, items, exts, vecSubtitles);
2050 
2051   size_t iSize = vecSubtitles.size();
2052   for (size_t i = 0; i < iSize; i++)
2053   {
2054     if (URIUtils::HasExtension(vecSubtitles[i], ".smi"))
2055     {
2056       //Cache multi-language sami subtitle
2057       CDVDSubtitleStream stream;
2058       if (stream.Open(vecSubtitles[i]))
2059       {
2060         CDVDSubtitleTagSami TagConv;
2061         TagConv.LoadHead(&stream);
2062         if (TagConv.m_Langclass.size() >= 2)
2063         {
2064           for (const auto &lang : TagConv.m_Langclass)
2065           {
2066             std::string strDest = StringUtils::Format("special://temp/subtitle.%s.%zu.smi", lang.Name.c_str(), i);
2067             if (CFile::Copy(vecSubtitles[i], strDest))
2068             {
2069               CLog::Log(LOGINFO, " cached subtitle %s->%s",
2070                         CURL::GetRedacted(vecSubtitles[i]).c_str(), strDest.c_str());
2071               vecSubtitles.push_back(strDest);
2072             }
2073           }
2074         }
2075       }
2076     }
2077   }
2078   CLog::Log(LOGDEBUG, "%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2079 }
2080 
GetExternalStreamDetailsFromFilename(const std::string & videoPath,const std::string & associatedFile)2081 ExternalStreamInfo CUtil::GetExternalStreamDetailsFromFilename(const std::string& videoPath, const std::string& associatedFile)
2082 {
2083   ExternalStreamInfo info;
2084 
2085   std::string videoBaseName = URIUtils::GetFileName(videoPath);
2086   URIUtils::RemoveExtension(videoBaseName);
2087 
2088   std::string toParse = URIUtils::GetFileName(associatedFile);
2089   URIUtils::RemoveExtension(toParse);
2090 
2091   // we check left part - if it's same as video base name - strip it
2092   if (StringUtils::StartsWithNoCase(toParse, videoBaseName))
2093     toParse = toParse.substr(videoBaseName.length());
2094   else if (URIUtils::GetExtension(associatedFile) == ".sub" && URIUtils::IsInArchive(associatedFile))
2095   {
2096     // exclude parsing of vobsub file names that are embedded in an archive
2097     CLog::Log(LOGDEBUG, "%s - skipping archived vobsub filename parsing: %s", __FUNCTION__, CURL::GetRedacted(associatedFile).c_str());
2098     toParse.clear();
2099   }
2100 
2101   // trim any non-alphanumeric char in the beginning
2102   std::string::iterator result = std::find_if(toParse.begin(), toParse.end(), StringUtils::isasciialphanum);
2103 
2104   std::string name;
2105   if (result != toParse.end()) // if we have anything to parse
2106   {
2107     std::string inputString(result, toParse.end());
2108     std::string delimiters(" .-");
2109     std::vector<std::string> tokens;
2110     StringUtils::Tokenize(inputString, tokens, delimiters);
2111 
2112     for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
2113     {
2114       if (info.language.empty())
2115       {
2116         std::string langTmp(*it);
2117         std::string langCode;
2118         // try to recognize language
2119         if (g_LangCodeExpander.ConvertToISO6392B(langTmp, langCode))
2120         {
2121           info.language = langCode;
2122           continue;
2123         }
2124       }
2125 
2126       // try to recognize a flag
2127       std::string flag_tmp(*it);
2128       StringUtils::ToLower(flag_tmp);
2129       if (!flag_tmp.compare("none"))
2130       {
2131         info.flag |= StreamFlags::FLAG_NONE;
2132         continue;
2133       }
2134       else if (!flag_tmp.compare("default"))
2135       {
2136         info.flag |= StreamFlags::FLAG_DEFAULT;
2137         continue;
2138       }
2139       else if (!flag_tmp.compare("forced"))
2140       {
2141         info.flag |= StreamFlags::FLAG_FORCED;
2142         continue;
2143       }
2144 
2145       name += " " + (*it);
2146     }
2147   }
2148   name += " ";
2149   name += g_localizeStrings.Get(21602); // External
2150   StringUtils::Trim(name);
2151   info.name = StringUtils::RemoveDuplicatedSpacesAndTabs(name);
2152   if (info.flag == 0)
2153     info.flag = StreamFlags::FLAG_NONE;
2154 
2155   CLog::Log(LOGDEBUG, "%s - Language = '%s' / Name = '%s' / Flag = '%u' from %s",
2156              __FUNCTION__, info.language.c_str(), info.name.c_str(), info.flag, CURL::GetRedacted(associatedFile).c_str());
2157 
2158   return info;
2159 }
2160 
2161 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2162  */
FindVobSubPair(const std::vector<std::string> & vecSubtitles,const std::string & strIdxPath,std::string & strSubPath)2163 bool CUtil::FindVobSubPair(const std::vector<std::string>& vecSubtitles, const std::string& strIdxPath, std::string& strSubPath)
2164 {
2165   if (URIUtils::HasExtension(strIdxPath, ".idx"))
2166   {
2167     std::string strIdxFile;
2168     std::string strIdxDirectory;
2169     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2170     for (const auto &subtitlePath : vecSubtitles)
2171     {
2172       std::string strSubFile;
2173       std::string strSubDirectory;
2174       URIUtils::Split(subtitlePath, strSubDirectory, strSubFile);
2175       if (URIUtils::IsInArchive(subtitlePath))
2176         strSubDirectory = CURL::Decode(strSubDirectory);
2177       if (URIUtils::HasExtension(strSubFile, ".sub") &&
2178           (URIUtils::PathEquals(URIUtils::ReplaceExtension(strIdxPath,""),
2179                                 URIUtils::ReplaceExtension(subtitlePath,"")) ||
2180            (strSubDirectory.size() >= 11 &&
2181             StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(strIdxPath,"")))))
2182       {
2183         strSubPath = subtitlePath;
2184         return true;
2185       }
2186     }
2187   }
2188   return false;
2189 }
2190 
2191 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2192  */
IsVobSub(const std::vector<std::string> & vecSubtitles,const std::string & strSubPath)2193 bool CUtil::IsVobSub(const std::vector<std::string>& vecSubtitles, const std::string& strSubPath)
2194 {
2195   if (URIUtils::HasExtension(strSubPath, ".sub"))
2196   {
2197     std::string strSubFile;
2198     std::string strSubDirectory;
2199     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2200     if (URIUtils::IsInArchive(strSubPath))
2201       strSubDirectory = CURL::Decode(strSubDirectory);
2202     for (const auto &subtitlePath : vecSubtitles)
2203     {
2204       std::string strIdxFile;
2205       std::string strIdxDirectory;
2206       URIUtils::Split(subtitlePath, strIdxDirectory, strIdxFile);
2207       if (URIUtils::HasExtension(strIdxFile, ".idx") &&
2208           (URIUtils::PathEquals(URIUtils::ReplaceExtension(subtitlePath,""),
2209                                 URIUtils::ReplaceExtension(strSubPath,"")) ||
2210            (strSubDirectory.size() >= 11 &&
2211             StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(subtitlePath,"")))))
2212         return true;
2213     }
2214   }
2215   return false;
2216 }
2217 
2218 /*! \brief find a plain or archived vobsub .sub file corresponding to an .idx file
2219  */
GetVobSubSubFromIdx(const std::string & vobSubIdx)2220 std::string CUtil::GetVobSubSubFromIdx(const std::string& vobSubIdx)
2221 {
2222   std::string vobSub = URIUtils::ReplaceExtension(vobSubIdx, ".sub");
2223 
2224   // check if a .sub file exists in the same directory
2225   if (CFile::Exists(vobSub))
2226   {
2227     return vobSub;
2228   }
2229 
2230   // look inside a .rar or .zip in the same directory
2231   const std::string archTypes[] = { "rar", "zip" };
2232   std::string vobSubFilename = URIUtils::GetFileName(vobSub);
2233   for (const std::string& archType : archTypes)
2234   {
2235     vobSub = URIUtils::CreateArchivePath(archType,
2236                                          CURL(URIUtils::ReplaceExtension(vobSubIdx, std::string(".") + archType)),
2237                                          vobSubFilename).Get();
2238     if (CFile::Exists(vobSub))
2239       return vobSub;
2240   }
2241 
2242   return std::string();
2243 }
2244 
2245 /*! \brief find a .idx file from a path of a plain or archived vobsub .sub file
2246  */
GetVobSubIdxFromSub(const std::string & vobSub)2247 std::string CUtil::GetVobSubIdxFromSub(const std::string& vobSub)
2248 {
2249   std::string vobSubIdx = URIUtils::ReplaceExtension(vobSub, ".idx");
2250 
2251   // check if a .idx file exists in the same directory
2252   if (CFile::Exists(vobSubIdx))
2253   {
2254     return vobSubIdx;
2255   }
2256 
2257   // look outside archive (usually .rar) if the .sub is inside one
2258   if (URIUtils::IsInArchive(vobSub))
2259   {
2260 
2261     std::string archiveFile = URIUtils::GetDirectory(vobSub);
2262     std::string vobSubIdxDir = URIUtils::GetParentPath(archiveFile);
2263 
2264     if (!vobSubIdxDir.empty())
2265     {
2266       std::string vobSubIdxFilename = URIUtils::GetFileName(vobSubIdx);
2267       std::string vobSubIdx = URIUtils::AddFileToFolder(vobSubIdxDir, vobSubIdxFilename);
2268 
2269       if (CFile::Exists(vobSubIdx))
2270         return vobSubIdx;
2271     }
2272   }
2273 
2274   return std::string();
2275 }
2276 
ScanForExternalDemuxSub(const std::string & videoPath,std::vector<std::string> & vecSubtitles)2277 void CUtil::ScanForExternalDemuxSub(const std::string& videoPath, std::vector<std::string>& vecSubtitles)
2278 {
2279   CFileItem item(videoPath, false);
2280   if (item.IsInternetStream()
2281     || item.IsPlayList()
2282     || item.IsLiveTV()
2283     || item.IsPVR()
2284     || !item.IsVideo())
2285     return;
2286 
2287   std::string strBasePath;
2288   std::string strSubtitle;
2289 
2290   GetVideoBasePathAndFileName(videoPath, strBasePath, strSubtitle);
2291 
2292   CFileItemList items;
2293   const std::vector<std::string> common_sub_dirs = { "subs", "subtitles", "vobsubs", "sub", "vobsub", "subtitle" };
2294   const std::string DemuxSubExtensions = ".sup";
2295   GetItemsToScan(strBasePath, DemuxSubExtensions, common_sub_dirs, items);
2296 
2297   std::vector<std::string> exts = StringUtils::Split(DemuxSubExtensions, "|");
2298   ScanPathsForAssociatedItems(strSubtitle, items, exts, vecSubtitles);
2299 }
2300 
ScanForExternalAudio(const std::string & videoPath,std::vector<std::string> & vecAudio)2301 void CUtil::ScanForExternalAudio(const std::string& videoPath, std::vector<std::string>& vecAudio)
2302 {
2303   CFileItem item(videoPath, false);
2304   if ( item.IsInternetStream()
2305    ||  item.IsPlayList()
2306    ||  item.IsLiveTV()
2307    ||  item.IsPVR()
2308    || !item.IsVideo())
2309     return;
2310 
2311   std::string strBasePath;
2312   std::string strAudio;
2313 
2314   GetVideoBasePathAndFileName(videoPath, strBasePath, strAudio);
2315 
2316   CFileItemList items;
2317   const std::vector<std::string> common_sub_dirs = { "audio", "tracks"};
2318   GetItemsToScan(strBasePath, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), common_sub_dirs, items);
2319 
2320   std::vector<std::string> exts = StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2321 
2322   CVideoDatabase database;
2323   database.Open();
2324   bool useAllExternalAudio = database.GetUseAllExternalAudioForVideo(videoPath);
2325 
2326   if (useAllExternalAudio)
2327   {
2328     for (const auto& audioItem : items.GetList())
2329     {
2330       vecAudio.push_back(audioItem.get()->GetPath());
2331     }
2332   }
2333   else
2334     ScanPathsForAssociatedItems(strAudio, items, exts, vecAudio);
2335 }
2336 
CanBindPrivileged()2337 bool CUtil::CanBindPrivileged()
2338 {
2339 #ifdef TARGET_POSIX
2340 
2341   if (geteuid() == 0)
2342     return true; //root user can always bind to privileged ports
2343 
2344 #ifdef HAVE_LIBCAP
2345 
2346   //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2347   bool canbind = false;
2348   cap_t capabilities = cap_get_proc();
2349   if (capabilities)
2350   {
2351     cap_flag_value_t value;
2352     if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
2353       canbind = value;
2354 
2355     cap_free(capabilities);
2356   }
2357 
2358   return canbind;
2359 
2360 #else //HAVE_LIBCAP
2361 
2362   return false;
2363 
2364 #endif //HAVE_LIBCAP
2365 
2366 #else //TARGET_POSIX
2367 
2368   return true;
2369 
2370 #endif //TARGET_POSIX
2371 }
2372 
ValidatePort(int port)2373 bool CUtil::ValidatePort(int port)
2374 {
2375   // check that it's a valid port
2376 #ifdef TARGET_POSIX
2377   if (!CUtil::CanBindPrivileged() && (port < 1024 || port > 65535))
2378     return false;
2379   else
2380 #endif
2381   if (port <= 0 || port > 65535)
2382     return false;
2383 
2384   return true;
2385 }
2386 
GetRandomNumber()2387 int CUtil::GetRandomNumber()
2388 {
2389 #if !defined(TARGET_WINDOWS)
2390   return rand_r(&s_randomSeed);
2391 #else
2392   unsigned int number;
2393   if (rand_s(&number) == 0)
2394     return (int)number;
2395 
2396   return rand();
2397 #endif
2398 }
2399 
CopyUserDataIfNeeded(const std::string & strPath,const std::string & file,const std::string & destname)2400 void CUtil::CopyUserDataIfNeeded(const std::string& strPath,
2401                                  const std::string& file,
2402                                  const std::string& destname)
2403 {
2404   std::string destPath;
2405   if (destname.empty())
2406     destPath = URIUtils::AddFileToFolder(strPath, file);
2407   else
2408     destPath = URIUtils::AddFileToFolder(strPath, destname);
2409 
2410   if (!CFile::Exists(destPath))
2411   {
2412     // need to copy it across
2413     std::string srcPath = URIUtils::AddFileToFolder("special://xbmc/userdata/", file);
2414     CFile::Copy(srcPath, destPath);
2415   }
2416 }
2417