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> ®exps = 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 ®exp : 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 ®exp : 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> ¶meters)
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 ¶mString, std::vector<std::string> ¶meters)
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