Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / Util.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "network/Network.h"
21 #include "threads/SystemClock.h"
22 #include "system.h"
23 #if defined(TARGET_DARWIN)
24 #include <sys/param.h>
25 #include <mach-o/dyld.h>
26 #endif
27
28 #if defined(TARGET_FREEBSD)
29 #include <sys/param.h>
30 #include <sys/sysctl.h>
31 #endif
32
33 #ifdef TARGET_POSIX
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <unistd.h>
37 #include <sys/wait.h>
38 #endif
39 #if defined(TARGET_ANDROID)
40 #include "android/bionic_supplement/bionic_supplement.h"
41 #endif
42 #include <stdlib.h>
43
44 #include "Application.h"
45 #include "Util.h"
46 #include "addons/Addon.h"
47 #include "filesystem/PVRDirectory.h"
48 #include "filesystem/Directory.h"
49 #include "filesystem/StackDirectory.h"
50 #include "filesystem/MultiPathDirectory.h"
51 #include "filesystem/SpecialProtocol.h"
52 #include "filesystem/RSSDirectory.h"
53 #ifdef HAS_FILESYSTEM_RAR
54 #include "filesystem/RarManager.h"
55 #endif
56 #include "filesystem/MythDirectory.h"
57 #ifdef HAS_UPNP
58 #include "filesystem/UPnPDirectory.h"
59 #endif
60 #include "profiles/ProfilesManager.h"
61 #include "utils/RegExp.h"
62 #include "guilib/GraphicContext.h"
63 #include "guilib/TextureManager.h"
64 #include "utils/fstrcmp.h"
65 #include "storage/MediaManager.h"
66 #ifdef TARGET_WINDOWS
67 #include "utils/CharsetConverter.h"
68 #include <shlobj.h>
69 #include "WIN32Util.h"
70 #endif
71 #if defined(TARGET_DARWIN)
72 #include "osx/DarwinUtils.h"
73 #endif
74 #include "GUIUserMessages.h"
75 #include "filesystem/File.h"
76 #include "settings/MediaSettings.h"
77 #include "settings/Settings.h"
78 #include "utils/StringUtils.h"
79 #include "settings/AdvancedSettings.h"
80 #ifdef HAS_IRSERVERSUITE
81   #include "input/windows/IRServerSuite.h"
82 #endif
83 #include "guilib/LocalizeStrings.h"
84 #include "utils/md5.h"
85 #include "utils/TimeUtils.h"
86 #include "utils/URIUtils.h"
87 #include "utils/log.h"
88 #include "utils/Environment.h"
89
90 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleTagSami.h"
91 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleStream.h"
92 #include "URL.h"
93 #include "utils/LangCodeExpander.h"
94 #ifdef HAVE_LIBCAP
95   #include <sys/capability.h>
96 #endif
97
98 #include "cores/dvdplayer/DVDDemuxers/DVDDemux.h"
99
100 using namespace std;
101
102
103 #define clamp(x) (x) > 255.f ? 255 : ((x) < 0 ? 0 : (BYTE)(x+0.5f)) // Valid ranges: brightness[-1 -> 1 (0 is default)] contrast[0 -> 2 (1 is default)]  gamma[0.5 -> 3.5 (1 is default)] default[ramp is linear]
104 static const int64_t SECS_BETWEEN_EPOCHS = 11644473600LL;
105 static const int64_t SECS_TO_100NS = 10000000;
106
107 using namespace XFILE;
108 using namespace PLAYLIST;
109
110 #ifdef HAS_DX
111 static D3DGAMMARAMP oldramp, flashramp;
112 #elif defined(HAS_SDL_2D)
113 static uint16_t oldrampRed[256];
114 static uint16_t oldrampGreen[256];
115 static uint16_t oldrampBlue[256];
116 static uint16_t flashrampRed[256];
117 static uint16_t flashrampGreen[256];
118 static uint16_t flashrampBlue[256];
119 #endif
120
121 #if !defined(TARGET_WINDOWS)
122 unsigned int CUtil::s_randomSeed = time(NULL);
123 #endif
124
125 CUtil::CUtil(void)
126 {
127 }
128
129 CUtil::~CUtil(void)
130 {}
131
132 CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
133 {
134   // use above to get the filename
135   CStdString path(strFileNameAndPath);
136   URIUtils::RemoveSlashAtEnd(path);
137   CStdString strFilename = URIUtils::GetFileName(path);
138
139   CURL url(strFileNameAndPath);
140   CStdString strHostname = url.GetHostName();
141
142 #ifdef HAS_UPNP
143   // UPNP
144   if (url.GetProtocol() == "upnp")
145     strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
146 #endif
147
148   if (url.GetProtocol() == "rss")
149   {
150     CRSSDirectory dir;
151     CFileItemList items;
152     if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.empty())
153       return items.m_strTitle;
154   }
155
156   // Shoutcast
157   else if (url.GetProtocol() == "shout")
158   {
159     const int genre = strFileNameAndPath.find_first_of('=');
160     if(genre <0)
161       strFilename = g_localizeStrings.Get(260);
162     else
163       strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
164   }
165
166   // Windows SMB Network (SMB)
167   else if (url.GetProtocol() == "smb" && strFilename.empty())
168   {
169     if (url.GetHostName().empty())
170     {
171       strFilename = g_localizeStrings.Get(20171);
172     }
173     else
174     {
175       strFilename = url.GetHostName();
176     }
177   }
178   // iTunes music share (DAAP)
179   else if (url.GetProtocol() == "daap" && strFilename.empty())
180     strFilename = g_localizeStrings.Get(20174);
181
182   // HDHomerun Devices
183   else if (url.GetProtocol() == "hdhomerun" && strFilename.empty())
184     strFilename = "HDHomerun Devices";
185
186   // Slingbox Devices
187   else if (url.GetProtocol() == "sling")
188     strFilename = "Slingbox";
189
190   // ReplayTV Devices
191   else if (url.GetProtocol() == "rtv")
192     strFilename = "ReplayTV Devices";
193
194   // HTS Tvheadend client
195   else if (url.GetProtocol() == "htsp")
196     strFilename = g_localizeStrings.Get(20256);
197
198   // VDR Streamdev client
199   else if (url.GetProtocol() == "vtp")
200     strFilename = g_localizeStrings.Get(20257);
201   
202   // MythTV client
203   else if (url.GetProtocol() == "myth")
204     strFilename = g_localizeStrings.Get(20258);
205
206   // SAP Streams
207   else if (url.GetProtocol() == "sap" && strFilename.empty())
208     strFilename = "SAP Streams";
209
210   // Root file views
211   else if (url.GetProtocol() == "sources")
212     strFilename = g_localizeStrings.Get(744);
213
214   // Music Playlists
215   else if (StringUtils::StartsWithNoCase(path, "special://musicplaylists"))
216     strFilename = g_localizeStrings.Get(136);
217
218   // Video Playlists
219   else if (StringUtils::StartsWithNoCase(path, "special://videoplaylists"))
220     strFilename = g_localizeStrings.Get(136);
221
222   else if (URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFilename.empty())
223     strFilename = URIUtils::GetFileName(url.GetHostName());
224
225   // now remove the extension if needed
226   if (!CSettings::Get().GetBool("filelists.showextensions") && !bIsFolder)
227   {
228     URIUtils::RemoveExtension(strFilename);
229     return strFilename;
230   }
231   
232   // URLDecode since the original path may be an URL
233   strFilename = CURL::Decode(strFilename);
234   return strFilename;
235 }
236
237 void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
238 {
239   strTitleAndYear = strFileName;
240
241   if (strFileName.Equals(".."))
242    return;
243
244   const CStdStringArray &regexps = g_advancedSettings.m_videoCleanStringRegExps;
245
246   CRegExp reTags(true, CRegExp::autoUtf8);
247   CRegExp reYear(false, CRegExp::autoUtf8);
248
249   if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
250   {
251     CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
252   }
253   else
254   {
255     if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
256     {
257       strTitleAndYear = reYear.GetMatch(1);
258       strYear = reYear.GetMatch(2);
259     }
260   }
261
262   URIUtils::RemoveExtension(strTitleAndYear);
263
264   for (unsigned int i = 0; i < regexps.size(); i++)
265   {
266     if (!reTags.RegComp(regexps[i].c_str()))
267     { // invalid regexp - complain in logs
268       CLog::Log(LOGERROR, "%s: Invalid string clean RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
269       continue;
270     }
271     int j=0;
272     if ((j=reTags.RegFind(strTitleAndYear.c_str())) > 0)
273       strTitleAndYear = strTitleAndYear.substr(0, j);
274   }
275
276   // final cleanup - special characters used instead of spaces:
277   // all '_' tokens should be replaced by spaces
278   // if the file contains no spaces, all '.' tokens should be replaced by
279   // spaces - one possibility of a mistake here could be something like:
280   // "Dr..StrangeLove" - hopefully no one would have anything like this.
281   if (bCleanChars)
282   {
283     bool initialDots = true;
284     bool alreadyContainsSpace = (strTitleAndYear.find(' ') != std::string::npos);
285
286     for (size_t i = 0; i < strTitleAndYear.size(); i++)
287     {
288       char c = strTitleAndYear[i];
289
290       if (c != '.')
291         initialDots = false;
292
293       if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
294       {
295         strTitleAndYear[i] = ' ';
296       }
297     }
298   }
299
300   StringUtils::Trim(strTitleAndYear);
301   strTitle = strTitleAndYear;
302
303   // append year
304   if (!strYear.empty())
305     strTitleAndYear = strTitle + " (" + strYear + ")";
306
307   // restore extension if needed
308   if (!bRemoveExtension)
309     strTitleAndYear += URIUtils::GetExtension(strFileName);
310 }
311
312 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
313 {
314   // Check if the filename is a fully qualified URL such as protocol://path/to/file
315   CURL plItemUrl(strFilename);
316   if (!plItemUrl.GetProtocol().empty())
317     return;
318
319   // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
320   if (strFilename.size() > 1)
321 #ifdef TARGET_POSIX
322     if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
323 #else
324     if ( strFilename[1] == ':' || (strFilename[0] == '\\' && strFilename[1] == '\\'))
325 #endif
326       return;
327
328   // add to base path and then clean
329   strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
330
331   // get rid of any /./ or \.\ that happen to be there
332   StringUtils::Replace(strFilename, "\\.\\", "\\");
333   StringUtils::Replace(strFilename, "/./", "/");
334
335   // now find any "\\..\\" and remove them via GetParentPath
336   size_t pos;
337   while ((pos = strFilename.find("/../")) != std::string::npos)
338   {
339     CStdString basePath = strFilename.substr(0, pos + 1);
340     strFilename.erase(0, pos + 4);
341     basePath = URIUtils::GetParentPath(basePath);
342     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
343   }
344   while ((pos = strFilename.find("\\..\\")) != std::string::npos)
345   {
346     CStdString basePath = strFilename.substr(0, pos + 1);
347     strFilename.erase(0, pos + 4);
348     basePath = URIUtils::GetParentPath(basePath);
349     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
350   }
351 }
352
353 #ifdef UNIT_TESTING
354 bool CUtil::TestGetQualifiedFilename()
355 {
356   CStdString file = "../foo"; GetQualifiedFilename("smb://", file);
357   if (file != "foo") return false;
358   file = "C:\\foo\\bar"; GetQualifiedFilename("smb://", file);
359   if (file != "C:\\foo\\bar") return false;
360   file = "../foo/./bar"; GetQualifiedFilename("smb://my/path", file);
361   if (file != "smb://my/foo/bar") return false;
362   file = "smb://foo/bar/"; GetQualifiedFilename("upnp://", file);
363   if (file != "smb://foo/bar/") return false;
364   return true;
365 }
366
367 bool CUtil::TestMakeLegalPath()
368 {
369   CStdString path;
370 #ifdef TARGET_WINDOWS
371   path = "C:\\foo\\bar"; path = MakeLegalPath(path);
372   if (path != "C:\\foo\\bar") return false;
373   path = "C:\\foo:\\bar\\"; path = MakeLegalPath(path);
374   if (path != "C:\\foo_\\bar\\") return false;
375 #elif
376   path = "/foo/bar/"; path = MakeLegalPath(path);
377   if (path != "/foo/bar/") return false;
378   path = "/foo?/bar"; path = MakeLegalPath(path);
379   if (path != "/foo_/bar") return false;
380 #endif
381   path = "smb://foo/bar"; path = MakeLegalPath(path);
382   if (path != "smb://foo/bar") return false;
383   path = "smb://foo/bar?/"; path = MakeLegalPath(path);
384   if (path != "smb://foo/bar_/") return false;
385   return true;
386 }
387 #endif
388
389 void CUtil::RunShortcut(const char* szShortcutPath)
390 {
391 }
392
393 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
394 {
395   strPath = CEnvironment::getenv(strTarget);
396
397 #ifdef TARGET_WINDOWS
398   if (strPath.find("..") != std::string::npos)
399   {
400     //expand potential relative path to full path
401     CStdStringW strPathW;
402     g_charsetConverter.utf8ToW(strPath, strPathW, false);
403     CWIN32Util::AddExtraLongPathPrefix(strPathW);
404     const unsigned int bufSize = GetFullPathNameW(strPathW, 0, NULL, NULL);
405     if (bufSize != 0)
406     {
407       wchar_t * buf = new wchar_t[bufSize];
408       if (GetFullPathNameW(strPathW, bufSize, buf, NULL) <= bufSize-1)
409       {
410         std::wstring expandedPathW(buf);
411         CWIN32Util::RemoveExtraLongPathPrefix(expandedPathW);
412         g_charsetConverter.wToUTF8(expandedPathW, strPath);
413       }
414
415       delete [] buf;
416     }
417   }
418 #endif
419
420   if (strPath.empty())
421   {
422     CStdString strHomePath = ResolveExecutablePath();
423 #if defined(TARGET_DARWIN)
424     int      result = -1;
425     char     given_path[2*MAXPATHLEN];
426     uint32_t path_size =2*MAXPATHLEN;
427
428     result = GetDarwinExecutablePath(given_path, &path_size);
429     if (result == 0)
430     {
431       // Move backwards to last /.
432       for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
433         given_path[n] = '\0';
434
435       #if defined(TARGET_DARWIN_IOS)
436         strcat(given_path, "/XBMCData/XBMCHome/");
437       #else
438         // Assume local path inside application bundle.
439         strcat(given_path, "../Resources/XBMC/");
440       #endif
441
442       // Convert to real path.
443       char real_path[2*MAXPATHLEN];
444       if (realpath(given_path, real_path) != NULL)
445       {
446         strPath = real_path;
447         return;
448       }
449     }
450 #endif
451     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
452     if (last_sep != string::npos)
453       strPath = strHomePath.substr(0, last_sep);
454     else
455       strPath = strHomePath;
456   }
457
458 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
459   /* Change strPath accordingly when target is XBMC_HOME and when INSTALL_PATH
460    * and BIN_INSTALL_PATH differ
461    */
462   CStdString installPath = INSTALL_PATH;
463   CStdString binInstallPath = BIN_INSTALL_PATH;
464   if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
465   {
466     int pos = strPath.length() - binInstallPath.length();
467     CStdString tmp = strPath;
468     tmp.erase(0, pos);
469     if (!tmp.compare(binInstallPath))
470     {
471       strPath.erase(pos, strPath.length());
472       strPath.append(installPath);
473     }
474   }
475 #endif
476 }
477
478 bool CUtil::IsPVR(const CStdString& strFile)
479 {
480   return StringUtils::StartsWithNoCase(strFile, "pvr:");
481 }
482
483 bool CUtil::IsHTSP(const CStdString& strFile)
484 {
485   return StringUtils::StartsWithNoCase(strFile, "htsp:");
486 }
487
488 bool CUtil::IsLiveTV(const CStdString& strFile)
489 {
490   if (StringUtils::StartsWithNoCase(strFile, "pvr://channels"))
491     return true;
492
493   if(URIUtils::IsTuxBox(strFile)
494   || URIUtils::IsVTP(strFile)
495   || URIUtils::IsHDHomeRun(strFile)
496   || URIUtils::IsHTSP(strFile)
497   || StringUtils::StartsWithNoCase(strFile, "sap:"))
498     return true;
499
500   if (URIUtils::IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
501     return true;
502
503   return false;
504 }
505
506 bool CUtil::IsTVRecording(const CStdString& strFile)
507 {
508   return StringUtils::StartsWithNoCase(strFile, "pvr://recording");
509 }
510
511 bool CUtil::IsPicture(const CStdString& strFile)
512 {
513   return URIUtils::HasExtension(strFile,
514                   g_advancedSettings.m_pictureExtensions + "|.tbn|.dds");
515 }
516
517 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
518 {
519   if (strFileOrFolder.empty())
520     return false;
521
522   CRegExp regExExcludes(true, CRegExp::autoUtf8);  // case insensitive regex
523
524   for (unsigned int i = 0; i < regexps.size(); i++)
525   {
526     if (!regExExcludes.RegComp(regexps[i].c_str()))
527     { // invalid regexp - complain in logs
528       CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
529       continue;
530     }
531     if (regExExcludes.RegFind(strFileOrFolder) > -1)
532     {
533       CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
534       return true;
535     }
536   }
537   return false;
538 }
539
540 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
541 {
542   strDir = strURL;
543   if (!URIUtils::IsRemote(strURL)) return ;
544   if (URIUtils::IsDVD(strURL)) return ;
545
546   CURL url(strURL);
547   strDir = StringUtils::Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
548 }
549
550 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
551 {
552   CStdString strFilename = URIUtils::GetFileName(strFile);
553   if (strFilename.Equals("video_ts.ifo")) return 0;
554   //VTS_[TITLE]_0.IFO
555   return atoi(strFilename.substr(4, 2).c_str());
556 }
557
558 CStdString CUtil::GetFileMD5(const CStdString& strPath)
559 {
560   CFile file;
561   CStdString result;
562   if (file.Open(strPath))
563   {
564     XBMC::XBMC_MD5 md5;
565     char temp[1024];
566     int pos=0;
567     int read=1;
568     while (read > 0)
569     {
570       read = file.Read(temp,1024);
571       pos += read;
572       md5.append(temp,read);
573     }
574     md5.getDigest(result);
575     file.Close();
576   }
577
578   return result;
579 }
580
581 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
582 {
583   CStdString strFName = URIUtils::GetFileName(strFileName);
584   strDescription = URIUtils::GetDirectory(strFileName);
585   URIUtils::RemoveSlashAtEnd(strDescription);
586
587   size_t iPos = strDescription.find_last_of("/\\");
588   if (iPos != std::string::npos)
589     strDescription = strDescription.substr(iPos + 1);
590   else if (strDescription.size() <= 0)
591     strDescription = strFName;
592   return true;
593 }
594
595 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
596 {
597   if ( !g_mediaManager.IsDiscInDrive() )
598   {
599     strIcon = "DefaultDVDEmpty.png";
600     return ;
601   }
602
603   if ( URIUtils::IsDVD(strPath) )
604   {
605 #ifdef HAS_DVD_DRIVE
606     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
607     //  xbox DVD
608     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
609     {
610       strIcon = "DefaultXboxDVD.png";
611       return ;
612     }
613 #endif
614     strIcon = "DefaultDVDRom.png";
615     return ;
616   }
617
618   if ( URIUtils::IsISO9660(strPath) )
619   {
620 #ifdef HAS_DVD_DRIVE
621     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
622     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
623     {
624       strIcon = "DefaultVCD.png";
625       return ;
626     }
627 #endif
628     strIcon = "DefaultDVDRom.png";
629     return ;
630   }
631
632   if ( URIUtils::IsCDDA(strPath) )
633   {
634     strIcon = "DefaultCDDA.png";
635     return ;
636   }
637 }
638
639 void CUtil::RemoveTempFiles()
640 {
641   CStdString searchPath = CProfilesManager::Get().GetDatabaseFolder();
642   CFileItemList items;
643   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
644     return;
645
646   for (int i = 0; i < items.Size(); ++i)
647   {
648     if (items[i]->m_bIsFolder)
649       continue;
650     XFILE::CFile::Delete(items[i]->GetPath());
651   }
652 }
653
654 void CUtil::ClearSubtitles()
655 {
656   //delete cached subs
657   CFileItemList items;
658   CDirectory::GetDirectory("special://temp/",items);
659   for( int i=0;i<items.Size();++i)
660   {
661     if (!items[i]->m_bIsFolder)
662     {
663       if (items[i]->GetPath().find("subtitle") != std::string::npos ||
664           items[i]->GetPath().find("vobsub_queue") != std::string::npos)
665       {
666         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
667         CFile::Delete(items[i]->GetPath());
668       }
669     }
670   }
671 }
672
673 void CUtil::ClearTempFonts()
674 {
675   CStdString searchPath = "special://temp/fonts/";
676
677   if (!CFile::Exists(searchPath))
678     return;
679
680   CFileItemList items;
681   CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE);
682
683   for (int i=0; i<items.Size(); ++i)
684   {
685     if (items[i]->m_bIsFolder)
686       continue;
687     CFile::Delete(items[i]->GetPath());
688   }
689 }
690
691 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
692
693 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
694 {
695   int64_t n;
696   n = high;
697   n <<= 32;
698   n += low;
699   return n;
700 }
701
702 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
703 {
704   if (fn_template.find("%03d") == std::string::npos)
705     return "";
706
707   CStdString searchPath = URIUtils::GetDirectory(fn_template);
708   CStdString mask = URIUtils::GetExtension(fn_template);
709   CStdString name = StringUtils::Format(fn_template.c_str(), 0);
710
711   CFileItemList items;
712   if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
713     return name;
714
715   items.SetFastLookup(true);
716   for (int i = 0; i <= max; i++)
717   {
718     CStdString name = StringUtils::Format(fn_template.c_str(), i);
719     if (!items.Get(name))
720       return name;
721   }
722   return "";
723 }
724
725 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
726 {
727   if (path_template.find("%04d") == std::string::npos)
728     return "";
729   
730   for (int i = 0; i <= max; i++)
731   {
732     CStdString name = StringUtils::Format(path_template.c_str(), i);
733     if (!CFile::Exists(name))
734       return name;
735   }
736   return "";
737 }
738
739 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
740 {
741   result->st_dev = stat->st_dev;
742   result->st_ino = stat->st_ino;
743   result->st_mode = stat->st_mode;
744   result->st_nlink = stat->st_nlink;
745   result->st_uid = stat->st_uid;
746   result->st_gid = stat->st_gid;
747   result->st_rdev = stat->st_rdev;
748   result->st_size = (int64_t)stat->st_size;
749
750 #ifndef TARGET_POSIX
751   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
752   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
753   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
754 #else
755   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
756   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
757   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
758 #endif
759 }
760
761 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
762 {
763   result->st_dev = stat->st_dev;
764   result->st_ino = stat->st_ino;
765   result->st_mode = stat->st_mode;
766   result->st_nlink = stat->st_nlink;
767   result->st_uid = stat->st_uid;
768   result->st_gid = stat->st_gid;
769   result->st_rdev = stat->st_rdev;
770   result->st_size = stat->st_size;
771 #ifndef TARGET_POSIX
772   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
773   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
774   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
775 #else
776   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
777   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
778   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
779 #endif
780 }
781
782 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
783 {
784   result->st_dev = stat->st_dev;
785   result->st_ino = stat->st_ino;
786   result->st_mode = stat->st_mode;
787   result->st_nlink = stat->st_nlink;
788   result->st_uid = stat->st_uid;
789   result->st_gid = stat->st_gid;
790   result->st_rdev = stat->st_rdev;
791   result->st_size = stat->st_size;
792 #ifndef TARGET_POSIX
793   result->st_atime = stat->st_atime;
794   result->st_mtime = stat->st_mtime;
795   result->st_ctime = stat->st_ctime;
796 #else
797   result->st_atime = stat->_st_atime;
798   result->st_mtime = stat->_st_mtime;
799   result->st_ctime = stat->_st_ctime;
800 #endif
801 }
802
803 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
804 {
805   result->st_dev = stat->st_dev;
806   result->st_ino = stat->st_ino;
807   result->st_mode = stat->st_mode;
808   result->st_nlink = stat->st_nlink;
809   result->st_uid = stat->st_uid;
810   result->st_gid = stat->st_gid;
811   result->st_rdev = stat->st_rdev;
812 #ifndef TARGET_POSIX
813   if (stat->st_size <= LONG_MAX)
814     result->st_size = (_off_t)stat->st_size;
815 #else
816   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
817     result->st_size = stat->st_size;
818 #endif
819   else
820   {
821     result->st_size = 0;
822     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
823   }
824   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
825   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
826   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
827 }
828
829 #ifdef TARGET_WINDOWS
830 void CUtil::Stat64ToStat64i32(struct _stat64i32 *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 #ifndef TARGET_POSIX
852   result->st_atime = stat->st_atime;
853   result->st_mtime = stat->st_mtime;
854   result->st_ctime = stat->st_ctime;
855 #else
856   result->st_atime = stat->_st_atime;
857   result->st_mtime = stat->_st_mtime;
858   result->st_ctime = stat->_st_ctime;
859 #endif
860 }
861 #endif
862
863 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
864 {
865   // Function to create all directories at once instead
866   // of calling CreateDirectory for every subdir.
867   // Creates the directory and subdirectories if needed.
868
869   // return true if directory already exist
870   if (CDirectory::Exists(strPath)) return true;
871
872   // we currently only allow HD and smb, nfs and afp paths
873   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
874   {
875     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
876     return false;
877   }
878
879   CStdStringArray dirs = URIUtils::SplitPath(strPath);
880   CStdString dir(dirs.front());
881   URIUtils::AddSlashAtEnd(dir);
882   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
883   {
884     dir = URIUtils::AddFileToFolder(dir, *it);
885     CDirectory::Create(dir);
886   }
887
888   // was the final destination directory successfully created ?
889   if (!CDirectory::Exists(strPath)) return false;
890   return true;
891 }
892
893 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
894 {
895   CStdString result = strFile;
896
897   StringUtils::Replace(result, '/', '_');
898   StringUtils::Replace(result, '\\', '_');
899   StringUtils::Replace(result, '?', '_');
900
901   if (LegalType == LEGAL_WIN32_COMPAT)
902   {
903     // just filter out some illegal characters on windows
904     StringUtils::Replace(result, ':', '_');
905     StringUtils::Replace(result, '*', '_');
906     StringUtils::Replace(result, '?', '_');
907     StringUtils::Replace(result, '\"', '_');
908     StringUtils::Replace(result, '<', '_');
909     StringUtils::Replace(result, '>', '_');
910     StringUtils::Replace(result, '|', '_');
911     StringUtils::TrimRight(result, ". ");
912   }
913   return result;
914 }
915
916 // legalize entire path
917 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
918 {
919   if (URIUtils::IsStack(strPathAndFile))
920     return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
921   if (URIUtils::IsMultiPath(strPathAndFile))
922     return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
923   if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile) && !URIUtils::IsAfp(strPathAndFile))
924     return strPathAndFile; // we don't support writing anywhere except HD, SMB, NFS and AFP - no need to legalize path
925
926   bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
927   CStdStringArray dirs = URIUtils::SplitPath(strPathAndFile);
928   // we just add first token to path and don't legalize it - possible values: 
929   // "X:" (local win32), "" (local unix - empty string before '/') or
930   // "protocol://domain"
931   CStdString dir(dirs.front());
932   URIUtils::AddSlashAtEnd(dir);
933   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
934     dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
935   if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
936   return dir;
937 }
938
939 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
940 {
941   CStdString result = path;
942
943   // Don't do any stuff on URLs containing %-characters or protocols that embed
944   // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
945   // recurse and crash XBMC
946   if (URIUtils::IsURL(path) && 
947       (path.find('%') != std::string::npos ||
948       StringUtils::StartsWithNoCase(path, "apk:") ||
949       StringUtils::StartsWithNoCase(path, "zip:") ||
950       StringUtils::StartsWithNoCase(path, "rar:") ||
951       StringUtils::StartsWithNoCase(path, "stack:") ||
952       StringUtils::StartsWithNoCase(path, "bluray:") ||
953       StringUtils::StartsWithNoCase(path, "multipath:") ))
954     return result;
955
956   // check the path for incorrect slashes
957 #ifdef TARGET_WINDOWS
958   if (URIUtils::IsDOSPath(path))
959   {
960     StringUtils::Replace(result, '/', '\\');
961     /* The double slash correction should only be used when *absolutely*
962        necessary! This applies to certain DLLs or use from Python DLLs/scripts
963        that incorrectly generate double (back) slashes.
964     */
965     if (bFixDoubleSlashes && !result.empty())
966     {
967       // Fixup for double back slashes (but ignore the \\ of unc-paths)
968       for (size_t x = 1; x < result.size() - 1; x++)
969       {
970         if (result[x] == '\\' && result[x+1] == '\\')
971           result.erase(x);
972       }
973     }
974   }
975   else if (path.find("://") != std::string::npos || path.find(":\\\\") != std::string::npos)
976 #endif
977   {
978     StringUtils::Replace(result, '\\', '/');
979     /* The double slash correction should only be used when *absolutely*
980        necessary! This applies to certain DLLs or use from Python DLLs/scripts
981        that incorrectly generate double (back) slashes.
982     */
983     if (bFixDoubleSlashes && !result.empty())
984     {
985       // Fixup for double forward slashes(/) but don't touch the :// of URLs
986       for (size_t x = 2; x < result.size() - 1; x++)
987       {
988         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
989           result.erase(x);
990       }
991     }
992   }
993   return result;
994 }
995
996 bool CUtil::IsUsingTTFSubtitles()
997 {
998   return URIUtils::HasExtension(CSettings::Get().GetString("subtitles.font"), ".ttf");
999 }
1000
1001 #ifdef UNIT_TESTING
1002 bool CUtil::TestSplitExec()
1003 {
1004   CStdString function;
1005   vector<CStdString> params;
1006   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\")", function, params);
1007   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo")
1008     return false;
1009   params.clear();
1010   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
1011   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
1012     return false;
1013   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")", function, params);
1014   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\\")
1015     return false;
1016   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")", function, params);
1017   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\\\test\\\\foo\\")
1018     return false;
1019   CUtil::SplitExecFunction("SetProperty(Foo,\"\")", function, params);
1020   if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
1021    return false;
1022   CUtil::SplitExecFunction("SetProperty(foo,ba(\"ba black )\",sheep))", function, params);
1023   if (function != "SetProperty" || params.size() != 2 || params[0] != "foo" || params[1] != "ba(\"ba black )\",sheep)")
1024     return false;
1025   return true;
1026 }
1027 #endif
1028
1029 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
1030 {
1031   CStdString paramString;
1032
1033   size_t iPos = execString.find("(");
1034   size_t iPos2 = execString.rfind(")");
1035   if (iPos != std::string::npos && iPos2 != std::string::npos)
1036   {
1037     paramString = execString.substr(iPos + 1, iPos2 - iPos - 1);
1038     function = execString.substr(0, iPos);
1039   }
1040   else
1041     function = execString;
1042
1043   // remove any whitespace, and the standard prefix (if it exists)
1044   StringUtils::Trim(function);
1045   if( StringUtils::StartsWithNoCase(function, "xbmc.") )
1046     function.erase(0, 5);
1047
1048   SplitParams(paramString, parameters);
1049 }
1050
1051 void CUtil::SplitParams(const CStdString &paramString, std::vector<CStdString> &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   CStdString 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 signfies 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
1146 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1147 {
1148   if (strPath1.empty())
1149     return -1;
1150
1151   // copy as we may change strPath
1152   CStdString 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.GetProtocol() == "stack")
1162     strPath.erase(0, 8); // remove the stack protocol
1163
1164   if (checkURL.GetProtocol() == "shout")
1165     strPath = checkURL.GetHostName();
1166   if (checkURL.GetProtocol() == "tuxbox")
1167     return 1;
1168   if (checkURL.GetProtocol() == "plugin")
1169     return 1;
1170   if (checkURL.GetProtocol() == "multipath")
1171     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1172
1173   bIsSourceName = false;
1174   int iIndex = -1;
1175
1176   // we first test the NAME of a source
1177   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1178   {
1179     CMediaSource share = VECSOURCES.at(i);
1180     CStdString strName = share.strName;
1181
1182     // special cases for dvds
1183     if (URIUtils::IsOnDVD(share.strPath))
1184     {
1185       if (URIUtils::IsOnDVD(strPath))
1186         return i;
1187
1188       // not a path, so we need to modify the source name
1189       // since we add the drive status and disc name to the source
1190       // "Name (Drive Status/Disc Name)"
1191       size_t iPos = strName.rfind('(');
1192       if (iPos != std::string::npos && iPos > 1)
1193         strName = strName.substr(0, iPos - 1);
1194     }
1195     if (strPath.Equals(strName))
1196     {
1197       bIsSourceName = true;
1198       return i;
1199     }
1200   }
1201
1202   // now test the paths
1203
1204   // remove user details, and ensure path only uses forward slashes
1205   // and ends with a trailing slash so as not to match a substring
1206   CURL urlDest(strPath);
1207   urlDest.SetOptions("");
1208   CStdString strDest = urlDest.GetWithoutUserDetails();
1209   ForceForwardSlashes(strDest);
1210   if (!URIUtils::HasSlashAtEnd(strDest))
1211     strDest += "/";
1212
1213   size_t iLength = 0;
1214   size_t iLenPath = strDest.size();
1215   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1216   {
1217     CMediaSource share = VECSOURCES.at(i);
1218
1219     // does it match a source name?
1220     if (share.strPath.substr(0,8) == "shout://")
1221     {
1222       CURL url(share.strPath);
1223       if (strPath.Equals(url.GetHostName()))
1224         return i;
1225     }
1226
1227     // doesnt match a name, so try the source path
1228     vector<CStdString> vecPaths;
1229
1230     // add any concatenated paths if they exist
1231     if (share.vecPaths.size() > 0)
1232       vecPaths = share.vecPaths;
1233
1234     // add the actual share path at the front of the vector
1235     vecPaths.insert(vecPaths.begin(), share.strPath);
1236
1237     // test each path
1238     for (int j = 0; j < (int)vecPaths.size(); ++j)
1239     {
1240       // remove user details, and ensure path only uses forward slashes
1241       // and ends with a trailing slash so as not to match a substring
1242       CURL urlShare(vecPaths[j]);
1243       urlShare.SetOptions("");
1244       CStdString strShare = urlShare.GetWithoutUserDetails();
1245       ForceForwardSlashes(strShare);
1246       if (!URIUtils::HasSlashAtEnd(strShare))
1247         strShare += "/";
1248       size_t iLenShare = strShare.size();
1249
1250       if ((iLenPath >= iLenShare) && StringUtils::StartsWithNoCase(strDest, strShare) && (iLenShare > iLength))
1251       {
1252         // if exact match, return it immediately
1253         if (iLenPath == iLenShare)
1254         {
1255           // if the path EXACTLY matches an item in a concatentated path
1256           // set source name to true to load the full virtualpath
1257           bIsSourceName = false;
1258           if (vecPaths.size() > 1)
1259             bIsSourceName = true;
1260           return i;
1261         }
1262         iIndex = i;
1263         iLength = iLenShare;
1264       }
1265     }
1266   }
1267
1268   // return the index of the share with the longest match
1269   if (iIndex == -1)
1270   {
1271
1272     // rar:// and zip://
1273     // if archive wasn't mounted, look for a matching share for the archive instead
1274     if( StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://") )
1275     {
1276       // get the hostname portion of the url since it contains the archive file
1277       strPath = checkURL.GetHostName();
1278
1279       bIsSourceName = false;
1280       bool bDummy;
1281       return GetMatchingSource(strPath, VECSOURCES, bDummy);
1282     }
1283
1284     CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource: no matching source found for [%s]", strPath1.c_str());
1285   }
1286   return iIndex;
1287 }
1288
1289 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1290 {
1291   if (!strSpecial.empty() && strSpecial[0] == '$')
1292   {
1293     if (StringUtils::StartsWithNoCase(strSpecial, "$home"))
1294       return URIUtils::AddFileToFolder("special://home/", strSpecial.substr(5));
1295     else if (StringUtils::StartsWithNoCase(strSpecial, "$subtitles"))
1296       return URIUtils::AddFileToFolder("special://subtitles/", strSpecial.substr(10));
1297     else if (StringUtils::StartsWithNoCase(strSpecial, "$userdata"))
1298       return URIUtils::AddFileToFolder("special://userdata/", strSpecial.substr(9));
1299     else if (StringUtils::StartsWithNoCase(strSpecial, "$database"))
1300       return URIUtils::AddFileToFolder("special://database/", strSpecial.substr(9));
1301     else if (StringUtils::StartsWithNoCase(strSpecial, "$thumbnails"))
1302       return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.substr(11));
1303     else if (StringUtils::StartsWithNoCase(strSpecial, "$recordings"))
1304       return URIUtils::AddFileToFolder("special://recordings/", strSpecial.substr(11));
1305     else if (StringUtils::StartsWithNoCase(strSpecial, "$screenshots"))
1306       return URIUtils::AddFileToFolder("special://screenshots/", strSpecial.substr(12));
1307     else if (StringUtils::StartsWithNoCase(strSpecial, "$musicplaylists"))
1308       return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.substr(15));
1309     else if (StringUtils::StartsWithNoCase(strSpecial, "$videoplaylists"))
1310       return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.substr(15));
1311     else if (StringUtils::StartsWithNoCase(strSpecial, "$cdrips"))
1312       return URIUtils::AddFileToFolder("special://cdrips/", strSpecial.substr(7));
1313     // this one will be removed post 2.0
1314     else if (StringUtils::StartsWithNoCase(strSpecial, "$playlists"))
1315       return URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), strSpecial.substr(10));
1316   }
1317   return strSpecial;
1318 }
1319
1320 CStdString CUtil::MusicPlaylistsLocation()
1321 {
1322   vector<CStdString> vec;
1323   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "music"));
1324   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1325   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1326 }
1327
1328 CStdString CUtil::VideoPlaylistsLocation()
1329 {
1330   vector<CStdString> vec;
1331   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "video"));
1332   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1333   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1334 }
1335
1336 void CUtil::DeleteMusicDatabaseDirectoryCache()
1337 {
1338   CUtil::DeleteDirectoryCache("mdb-");
1339   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1340 }
1341
1342 void CUtil::DeleteVideoDatabaseDirectoryCache()
1343 {
1344   CUtil::DeleteDirectoryCache("vdb-");
1345   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1346 }
1347
1348 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1349 {
1350   CStdString searchPath = "special://temp/";
1351   CFileItemList items;
1352   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", DIR_FLAG_NO_FILE_DIRS))
1353     return;
1354
1355   for (int i = 0; i < items.Size(); ++i)
1356   {
1357     if (items[i]->m_bIsFolder)
1358       continue;
1359     CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
1360     if (StringUtils::StartsWith(fileName, prefix))
1361       XFILE::CFile::Delete(items[i]->GetPath());
1362   }
1363 }
1364
1365
1366 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1367 {
1368   CFileItemList myItems;
1369   int flags = DIR_FLAG_DEFAULTS;
1370   if (!bUseFileDirectories)
1371     flags |= DIR_FLAG_NO_FILE_DIRS;
1372   CDirectory::GetDirectory(strPath,myItems,strMask,flags);
1373   for (int i=0;i<myItems.Size();++i)
1374   {
1375     if (myItems[i]->m_bIsFolder)
1376       CUtil::GetRecursiveListing(myItems[i]->GetPath(),items,strMask,bUseFileDirectories);
1377     else
1378       items.Add(myItems[i]);
1379   }
1380 }
1381
1382 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1383 {
1384   CFileItemList myItems;
1385   CDirectory::GetDirectory(strPath,myItems,"",DIR_FLAG_NO_FILE_DIRS);
1386   for (int i=0;i<myItems.Size();++i)
1387   {
1388     if (myItems[i]->m_bIsFolder && !myItems[i]->GetPath().Equals(".."))
1389     {
1390       item.Add(myItems[i]);
1391       CUtil::GetRecursiveDirsListing(myItems[i]->GetPath(),item);
1392     }
1393   }
1394 }
1395
1396 void CUtil::ForceForwardSlashes(CStdString& strPath)
1397 {
1398   size_t iPos = strPath.rfind('\\');
1399   while (iPos != string::npos)
1400   {
1401     strPath.at(iPos) = '/';
1402     iPos = strPath.rfind('\\');
1403   }
1404 }
1405
1406 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
1407 {
1408   // case-insensitive fuzzy string comparison on the album and artist for relevance
1409   // weighting is identical, both album and artist are 50% of the total relevance
1410   // a missing artist means the maximum relevance can only be 0.50
1411   CStdString strAlbumTemp = strAlbumTemp1;
1412   StringUtils::ToLower(strAlbumTemp);
1413   CStdString strAlbum = strAlbum1;
1414   StringUtils::ToLower(strAlbum);
1415   double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1416   double fArtistPercentage = 0.0f;
1417   if (!strArtist1.empty())
1418   {
1419     CStdString strArtistTemp = strArtistTemp1;
1420     StringUtils::ToLower(strArtistTemp);
1421     CStdString strArtist = strArtist1;
1422     StringUtils::ToLower(strArtist);
1423     fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1424   }
1425   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1426   return fRelevance;
1427 }
1428
1429 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, size_t iTextMaxLength)
1430 {
1431   size_t iStrInputSize = StrInput.size();
1432   if(iStrInputSize <= 0 || iTextMaxLength >= iStrInputSize)
1433     return false;
1434
1435   char cDelim = '\0';
1436   size_t nGreaterDelim, nPos;
1437
1438   nPos = StrInput.find_last_of( '\\' );
1439   if (nPos != std::string::npos)
1440     cDelim = '\\';
1441   else
1442   {
1443     nPos = StrInput.find_last_of( '/' );
1444     if (nPos != std::string::npos)
1445       cDelim = '/';
1446   }
1447   if ( cDelim == '\0' )
1448     return false;
1449
1450   if (nPos == StrInput.size() - 1)
1451   {
1452     StrInput.erase(StrInput.size() - 1);
1453     nPos = StrInput.find_last_of(cDelim);
1454   }
1455   while( iTextMaxLength < iStrInputSize )
1456   {
1457     nPos = StrInput.find_last_of( cDelim, nPos );
1458     nGreaterDelim = nPos;
1459     if ( nPos != std::string::npos )
1460       nPos = StrInput.find_last_of( cDelim, nPos - 1 );
1461     if ( nPos == CStdString::npos ) break;
1462     if ( nGreaterDelim > nPos ) StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
1463     iStrInputSize = StrInput.size();
1464   }
1465   // replace any additional /../../ with just /../ if necessary
1466   CStdString replaceDots = StringUtils::Format("..%c..", cDelim);
1467   while (StrInput.size() > (unsigned int)iTextMaxLength)
1468     if (!StringUtils::Replace(StrInput, replaceDots, ".."))
1469       break;
1470   // finally, truncate our string to force inside our max text length,
1471   // replacing the last 2 characters with ".."
1472
1473   // eg end up with:
1474   // "smb://../Playboy Swimsuit Cal.."
1475   if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1476   {
1477     StrInput.erase(iTextMaxLength - 2);
1478     StrInput += "..";
1479   }
1480   StrOutput = StrInput;
1481   return true;
1482 }
1483
1484 bool CUtil::SupportsWriteFileOperations(const CStdString& strPath)
1485 {
1486   // currently only hd, smb, nfs, afp and dav support delete and rename
1487   if (URIUtils::IsHD(strPath))
1488     return true;
1489   if (URIUtils::IsSmb(strPath))
1490     return true;
1491   if (CUtil::IsTVRecording(strPath))
1492     return CPVRDirectory::SupportsWriteFileOperations(strPath);
1493   if (URIUtils::IsNfs(strPath))
1494     return true;
1495   if (URIUtils::IsAfp(strPath))
1496     return true;
1497   if (URIUtils::IsDAV(strPath))
1498     return true;
1499   if (URIUtils::IsMythTV(strPath))
1500   {
1501     /*
1502      * Can't use CFile::Exists() to check whether the myth:// path supports file operations because
1503      * it hits the directory cache on the way through, which has the Live Channels and Guide
1504      * items cached.
1505      */
1506     return CMythDirectory::SupportsWriteFileOperations(strPath);
1507   }
1508   if (URIUtils::IsStack(strPath))
1509     return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
1510   if (URIUtils::IsMultiPath(strPath))
1511     return CMultiPathDirectory::SupportsWriteFileOperations(strPath);
1512
1513   return false;
1514 }
1515
1516 bool CUtil::SupportsReadFileOperations(const CStdString& strPath)
1517 {
1518   if (URIUtils::IsVideoDb(strPath))
1519     return false;
1520
1521   return true;
1522 }
1523
1524 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1525 {
1526   if (g_TextureManager.HasTexture(folderThumb))
1527     return folderThumb;
1528   return "";
1529 }
1530
1531 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
1532 {
1533   CStdString strPath = URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(), "media");
1534   CFileItemList items;
1535   CDirectory::GetDirectory(strPath, items);
1536   // Search for Themes in the Current skin!
1537   for (int i = 0; i < items.Size(); ++i)
1538   {
1539     CFileItemPtr pItem = items[i];
1540     if (!pItem->m_bIsFolder)
1541     {
1542       CStdString strExtension = URIUtils::GetExtension(pItem->GetPath());
1543       if ((strExtension == ".xpr" && !StringUtils::EqualsNoCase(pItem->GetLabel(), "Textures.xpr")) ||
1544           (strExtension == ".xbt" && !StringUtils::EqualsNoCase(pItem->GetLabel(), "Textures.xbt")))
1545       {
1546         CStdString strLabel = pItem->GetLabel();
1547         vecTheme.push_back(strLabel.substr(0, strLabel.size() - 4));
1548       }
1549     }
1550   }
1551   sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1552 }
1553
1554 void CUtil::InitRandomSeed()
1555 {
1556   // Init random seed
1557   int64_t now;
1558   now = CurrentHostCounter();
1559   unsigned int seed = (unsigned int)now;
1560 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1561   srand(seed);
1562 }
1563
1564 #ifdef TARGET_POSIX
1565 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1566 {
1567   CStdStringArray args;
1568
1569   StringUtils::SplitString(cmdLine, ",", args);
1570
1571   // Strip quotes and whitespace around the arguments, or exec will fail.
1572   // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1573   // But it's still limited, for example quotes inside the strings are not expanded, etc.
1574   // TODO: Maybe some python library routine can parse this more properly ?
1575   for (CStdStringArray::iterator it = args.begin(); it != args.end(); ++it)
1576   {
1577     size_t pos;
1578     pos = it->find_first_not_of(" \t\n\"'");
1579     if (pos != std::string::npos)
1580     {
1581       it->erase(0, pos);
1582     }
1583
1584     pos = it->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1585     {
1586       it->erase(++pos, it->size());
1587     }
1588   }
1589
1590   return Command(args, waitExit);
1591 }
1592
1593 //
1594 // FIXME, this should be merged with the function below.
1595 //
1596 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1597 {
1598 #ifdef _DEBUG
1599   printf("Executing: ");
1600   for (size_t i=0; i<arrArgs.size(); i++)
1601     printf("%s ", arrArgs[i].c_str());
1602   printf("\n");
1603 #endif
1604
1605   pid_t child = fork();
1606   int n = 0;
1607   if (child == 0)
1608   {
1609     if (!waitExit)
1610     {
1611       // fork again in order not to leave a zombie process
1612       child = fork();
1613       if (child == -1)
1614         _exit(2);
1615       else if (child != 0)
1616         _exit(0);
1617     }
1618     close(0);
1619     close(1);
1620     close(2);
1621     if (arrArgs.size() > 0)
1622     {
1623       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1624       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1625       for (size_t i=0; i<arrArgs.size(); i++)
1626         args[i] = (char *)arrArgs[i].c_str();
1627       execvp(args[0], args);
1628     }
1629   }
1630   else
1631   {
1632     waitpid(child, &n, 0);
1633   }
1634
1635   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
1636 }
1637
1638 bool CUtil::SudoCommand(const CStdString &strCommand)
1639 {
1640   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
1641   pid_t child = fork();
1642   int n = 0;
1643   if (child == 0)
1644   {
1645     close(0); // close stdin to avoid sudo request password
1646     close(1);
1647     close(2);
1648     CStdStringArray arrArgs;
1649     StringUtils::SplitString(strCommand, " ", arrArgs);
1650     if (arrArgs.size() > 0)
1651     {
1652       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1653       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1654       args[0] = (char *)"/usr/bin/sudo";
1655       args[1] = (char *)"-S";
1656       for (size_t i=0; i<arrArgs.size(); i++)
1657       {
1658         args[i+2] = (char *)arrArgs[i].c_str();
1659       }
1660       execvp("/usr/bin/sudo", args);
1661     }
1662   }
1663   else
1664     waitpid(child, &n, 0);
1665
1666   return WEXITSTATUS(n) == 0;
1667 }
1668 #endif
1669
1670 int CUtil::LookupRomanDigit(char roman_digit)
1671 {
1672   switch (roman_digit)
1673   {
1674     case 'i':
1675     case 'I':
1676       return 1;
1677     case 'v':
1678     case 'V':
1679       return 5;
1680     case 'x':
1681     case 'X':
1682       return 10;
1683     case 'l':
1684     case 'L':
1685       return 50;
1686     case 'c':
1687     case 'C':
1688       return 100;
1689     case 'd':
1690     case 'D':
1691       return 500;
1692     case 'm':
1693     case 'M':
1694       return 1000;
1695     default:
1696       return 0;
1697   }
1698 }
1699
1700 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
1701 {
1702   
1703   int decimal = -1;
1704
1705   if (roman_numeral && roman_numeral[0])
1706   {
1707     int temp_sum  = 0,
1708         last      = 0,
1709         repeat    = 0,
1710         trend     = 1;
1711     decimal = 0;
1712     while (*roman_numeral)
1713     {
1714       int digit = CUtil::LookupRomanDigit(*roman_numeral);
1715       int test  = last;
1716       
1717       // General sanity checks
1718
1719       // numeral not in LUT
1720       if (!digit)
1721         return -1;
1722       
1723       while (test > 5)
1724         test /= 10;
1725       
1726       // N = 10^n may not precede (N+1) > 10^(N+1)
1727       if (test == 1 && digit > last * 10)
1728         return -1;
1729       
1730       // N = 5*10^n may not precede (N+1) >= N
1731       if (test == 5 && digit >= last) 
1732         return -1;
1733
1734       // End general sanity checks
1735
1736       if (last < digit)
1737       {
1738         // smaller numerals may not repeat before a larger one
1739         if (repeat) 
1740           return -1;
1741
1742         temp_sum += digit;
1743         
1744         repeat  = 0;
1745         trend   = 0;
1746       }
1747       else if (last == digit)
1748       {
1749         temp_sum += digit;
1750         repeat++;
1751         trend = 1;
1752       }
1753       else
1754       {
1755         if (!repeat)
1756           decimal += 2 * last - temp_sum;
1757         else
1758           decimal += temp_sum;
1759         
1760         temp_sum = digit;
1761
1762         trend   = 1;
1763         repeat  = 0;
1764       }
1765       // Post general sanity checks
1766
1767       // numerals may not repeat more than thrice
1768       if (repeat == 3)
1769         return -1;
1770
1771       last = digit;
1772       roman_numeral++;
1773     }
1774
1775     if (trend)
1776       decimal += temp_sum;
1777     else
1778       decimal += 2 * last - temp_sum;
1779   }
1780   return decimal;
1781 }
1782
1783 CStdString CUtil::ResolveExecutablePath()
1784 {
1785   CStdString strExecutablePath;
1786 #ifdef TARGET_WINDOWS
1787   static const size_t bufSize = MAX_PATH * 2;
1788   wchar_t* buf = new wchar_t[bufSize];
1789   buf[0] = 0;
1790   ::GetModuleFileNameW(0, buf, bufSize);
1791   buf[bufSize-1] = 0;
1792   g_charsetConverter.wToUTF8(buf,strExecutablePath);
1793   delete[] buf;
1794 #elif defined(TARGET_DARWIN)
1795   char     given_path[2*MAXPATHLEN];
1796   uint32_t path_size =2*MAXPATHLEN;
1797
1798   GetDarwinExecutablePath(given_path, &path_size);
1799   strExecutablePath = given_path;
1800 #elif defined(TARGET_FREEBSD)                                                                                                                                                                   
1801   char buf[PATH_MAX];
1802   size_t buflen;
1803   int mib[4];
1804
1805   mib[0] = CTL_KERN;
1806   mib[1] = KERN_PROC;
1807   mib[2] = KERN_PROC_PATHNAME;
1808   mib[3] = getpid();
1809
1810   buflen = sizeof(buf) - 1;
1811   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
1812     strExecutablePath = "";
1813   else
1814     strExecutablePath = buf;
1815 #else
1816   /* Get our PID and build the name of the link in /proc */
1817   pid_t pid = getpid();
1818   char linkname[64]; /* /proc/<pid>/exe */
1819   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
1820
1821   /* Now read the symbolic link */
1822   char buf[PATH_MAX + 1];
1823   buf[0] = 0;
1824
1825   int ret = readlink(linkname, buf, sizeof(buf) - 1);
1826   if (ret != -1)
1827     buf[ret] = 0;
1828
1829   strExecutablePath = buf;
1830 #endif
1831   return strExecutablePath;
1832 }
1833
1834 CStdString CUtil::GetFrameworksPath(bool forPython)
1835 {
1836   CStdString strFrameworksPath;
1837 #if defined(TARGET_DARWIN)
1838   char     given_path[2*MAXPATHLEN];
1839   uint32_t path_size =2*MAXPATHLEN;
1840
1841   GetDarwinFrameworkPath(forPython, given_path, &path_size);
1842   strFrameworksPath = given_path;
1843 #endif
1844   return strFrameworksPath;
1845 }
1846
1847 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
1848 {
1849   unsigned int startTimer = XbmcThreads::SystemClockMillis();
1850   
1851   // new array for commons sub dirs
1852   const char * common_sub_dirs[] = {"subs",
1853     "Subs",
1854     "subtitles",
1855     "Subtitles",
1856     "vobsubs",
1857     "Vobsubs",
1858     "sub",
1859     "Sub",
1860     "vobsub",
1861     "Vobsub",
1862     "subtitle",
1863     "Subtitle",
1864     NULL};
1865   
1866   vector<CStdString> vecExtensionsCached;
1867   
1868   CFileItem item(strMovie, false);
1869   if ( item.IsInternetStream()
1870     || item.IsHDHomeRun()
1871     || item.IsSlingbox()
1872     || item.IsPlayList()
1873     || item.IsLiveTV()
1874     || !item.IsVideo())
1875     return;
1876   
1877   vector<CStdString> strLookInPaths;
1878   
1879   CStdString strMovieFileName;
1880   CStdString strPath;
1881   
1882   URIUtils::Split(strMovie, strPath, strMovieFileName);
1883   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
1884   strLookInPaths.push_back(strPath);
1885   
1886   if (!CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() && !CSettings::Get().GetString("subtitles.custompath").empty()) // to avoid checking non-existent directories (network) every time..
1887   {
1888     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(CSettings::Get().GetString("subtitles.custompath")))
1889     {
1890       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
1891       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1892     }
1893     else if (!CDirectory::Exists(CSettings::Get().GetString("subtitles.custompath")))
1894     {
1895       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
1896       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1897     }
1898     
1899     CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(1);
1900   }
1901   
1902   if (StringUtils::StartsWith(strMovie, "rar://")) // <--- if this is found in main path then ignore it!
1903   {
1904     CURL url(strMovie);
1905     CStdString strArchive = url.GetHostName();
1906     URIUtils::Split(strArchive, strPath, strMovieFileName);
1907     strLookInPaths.push_back(strPath);
1908   }
1909   
1910   int iSize = strLookInPaths.size();
1911   for (int i=0; i<iSize; ++i)
1912   {
1913     CStdStringArray directories;
1914     int nTokens = StringUtils::SplitString( strLookInPaths[i], "/", directories );
1915     if (nTokens == 1)
1916       StringUtils::SplitString( strLookInPaths[i], "\\", directories );
1917
1918     // if it's inside a cdX dir, add parent path
1919     if (directories.size() >= 2 && directories[directories.size()-2].size() == 3 && StringUtils::StartsWithNoCase(directories[directories.size()-2], "cd")) // SplitString returns empty token as last item, hence size-2
1920     {
1921       CStdString strPath2;
1922       URIUtils::GetParentPath(strLookInPaths[i], strPath2);
1923       strLookInPaths.push_back(strPath2);
1924     }
1925   }
1926
1927   // checking if any of the common subdirs exist ..
1928   iSize = strLookInPaths.size();
1929   for (int i=0;i<iSize;++i)
1930   {
1931     for (int j=0; common_sub_dirs[j]; j++)
1932     {
1933       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
1934       URIUtils::AddSlashAtEnd(strPath2);
1935       if (CDirectory::Exists(strPath2))
1936         strLookInPaths.push_back(strPath2);
1937     }
1938   }
1939   // .. done checking for common subdirs
1940   
1941   // check if there any cd-directories in the paths we have added so far
1942   iSize = strLookInPaths.size();
1943   for (int i=0;i<9;++i) // 9 cd's
1944   {
1945     CStdString cdDir = StringUtils::Format("cd%i",i+1);
1946     for (int i=0;i<iSize;++i)
1947     {
1948       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
1949       URIUtils::AddSlashAtEnd(strPath2);
1950       bool pathAlreadyAdded = false;
1951       for (unsigned int i=0; i<strLookInPaths.size(); i++)
1952       {
1953         // if movie file is inside cd-dir, this directory can exist in vector already
1954         if (strLookInPaths[i].Equals( strPath2 ) )
1955           pathAlreadyAdded = true;
1956       }
1957       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
1958         strLookInPaths.push_back(strPath2);
1959     }
1960   }
1961   // .. done checking for cd-dirs
1962   
1963   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
1964   if (CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() == 1)
1965   {
1966     strPath = CSettings::Get().GetString("subtitles.custompath");
1967     URIUtils::AddSlashAtEnd(strPath);
1968     strLookInPaths.push_back(strPath);
1969   }
1970   
1971   CStdString strLExt;
1972   CStdString strDest;
1973   CStdString strItem;
1974   
1975   // 2 steps for movie directory and alternate subtitles directory
1976   CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
1977   for (unsigned int step = 0; step < strLookInPaths.size(); step++)
1978   {
1979     if (strLookInPaths[step].length() != 0)
1980     {
1981       CFileItemList items;
1982       
1983       CDirectory::GetDirectory(strLookInPaths[step], items, g_advancedSettings.m_subtitlesExtensions, DIR_FLAG_NO_FILE_DIRS);
1984       
1985       for (int j = 0; j < items.Size(); j++)
1986       {
1987         URIUtils::Split(items[j]->GetPath(), strPath, strItem);
1988         
1989         if (StringUtils::StartsWithNoCase(strItem, strMovieFileNameNoExt))
1990         {
1991           // is this a rar or zip-file
1992           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
1993           {
1994             // zip-file name equals strMovieFileNameNoExt, don't check in zip-file
1995             ScanArchiveForSubtitles( items[j]->GetPath(), "", vecSubtitles );
1996           }
1997           else    // not a rar/zip file
1998           {
1999             for (int i = 0; sub_exts[i]; i++)
2000             {
2001               //Cache subtitle with same name as movie
2002               if (URIUtils::HasExtension(strItem, sub_exts[i]))
2003               {
2004                 vecSubtitles.push_back( items[j]->GetPath() ); 
2005                 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2006               }
2007             }
2008           }
2009         }
2010         else
2011         {
2012           // is this a rar or zip-file
2013           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2014           {
2015             // check strMovieFileNameNoExt in zip-file
2016             ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2017           }
2018         }
2019       }
2020     }
2021   }
2022
2023   iSize = vecSubtitles.size();
2024   for (int i = 0; i < iSize; i++)
2025   {
2026     if (URIUtils::HasExtension(vecSubtitles[i], ".smi"))
2027     {
2028       //Cache multi-language sami subtitle
2029       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2030       if(pStream->Open(vecSubtitles[i]))
2031       {
2032         CDVDSubtitleTagSami TagConv;
2033         TagConv.LoadHead(pStream);
2034         if (TagConv.m_Langclass.size() >= 2)
2035         {
2036           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2037           {
2038             strDest = StringUtils::Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name.c_str(), i);
2039             if (CFile::Cache(vecSubtitles[i], strDest))
2040             {
2041               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2042               vecSubtitles.push_back(strDest);
2043             }
2044           }
2045         }
2046       }
2047       delete pStream;
2048     }
2049   }
2050   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2051 }
2052
2053 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2054 {
2055   int nSubtitlesAdded = 0;
2056   CFileItemList ItemList;
2057  
2058   // zip only gets the root dir
2059   if (URIUtils::HasExtension(strArchivePath, ".zip"))
2060   {
2061    CStdString strZipPath;
2062    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2063    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",DIR_FLAG_NO_FILE_DIRS))
2064     return false;
2065   }
2066   else
2067   {
2068  #ifdef HAS_FILESYSTEM_RAR
2069    // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2070    // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2071    if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2072     return false;
2073  #else
2074    return false;
2075  #endif
2076   }
2077   for (int it= 0 ; it <ItemList.Size();++it)
2078   {
2079    CStdString strPathInRar = ItemList[it]->GetPath();
2080    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2081    
2082    CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2083    // always check any embedded rar archives
2084    // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2085    if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2086    {
2087     CStdString strRarInRar;
2088     if (strExt == ".rar")
2089       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2090     else
2091       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2092     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2093    }
2094    // done checking if this is a rar-in-rar
2095
2096    // check that the found filename matches the movie filename
2097    int fnl = strMovieFileNameNoExt.size();
2098    if (fnl && !StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), strMovieFileNameNoExt))
2099      continue;
2100
2101    int iPos=0;
2102     while (sub_exts[iPos])
2103     {
2104      if (StringUtils::EqualsNoCase(strExt, sub_exts[iPos]))
2105      {
2106       CStdString strSourceUrl;
2107       if (URIUtils::HasExtension(strArchivePath, ".rar"))
2108        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2109       else
2110        strSourceUrl = strPathInRar;
2111       
2112        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2113        vecSubtitles.push_back( strSourceUrl );
2114        nSubtitlesAdded++;
2115      }
2116      
2117      iPos++;
2118     }
2119   }
2120
2121   return nSubtitlesAdded;
2122 }
2123
2124 void CUtil::GetExternalStreamDetailsFromFilename(const CStdString& strVideo, const CStdString& strStream, ExternalStreamInfo& info)
2125 {
2126   CStdString videoBaseName = URIUtils::GetFileName(strVideo);
2127   URIUtils::RemoveExtension(videoBaseName);
2128
2129   CStdString toParse = URIUtils::GetFileName(strStream);
2130   URIUtils::RemoveExtension(toParse);
2131
2132   // we check left part - if it's same as video base name - strip it
2133   if (StringUtils::StartsWithNoCase(toParse, videoBaseName))
2134     toParse = toParse.substr(videoBaseName.length());
2135
2136   // trim any non-alphanumeric char in the begining
2137   std::string::iterator result = std::find_if(toParse.begin(), toParse.end(), ::isalnum);
2138
2139   std::string name;
2140   if (result != toParse.end()) // if we have anything to parse
2141   {
2142     std::string inputString(result, toParse.end());
2143     std::string delimiters(" .-");
2144     std::vector<std::string> tokens;
2145     StringUtils::Tokenize(inputString, tokens, delimiters);
2146
2147     for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
2148     {
2149       if (info.language.empty())
2150       {
2151         CStdString langTmp(*it);
2152         CStdString langCode;
2153         // try to recognize language
2154         if (g_LangCodeExpander.ConvertToThreeCharCode(langCode, langTmp))
2155         {
2156           info.language = langCode;
2157           continue;
2158         }
2159       }
2160
2161       // try to recognize a flag
2162       std::string flag_tmp(*it);
2163       StringUtils::ToLower(flag_tmp);
2164       if (!flag_tmp.compare("none"))
2165       {
2166         info.flag |= CDemuxStream::FLAG_NONE;
2167         continue;
2168       }
2169       else if (!flag_tmp.compare("default"))
2170       {
2171         info.flag |= CDemuxStream::FLAG_DEFAULT;
2172         continue;
2173       }
2174       else if (!flag_tmp.compare("forced"))
2175       {
2176         info.flag |= CDemuxStream::FLAG_FORCED;
2177         continue;
2178       }
2179
2180       name += " " + (*it);
2181     }
2182   }
2183   name += " ";
2184   name += g_localizeStrings.Get(21602); // External
2185   StringUtils::Trim(name);
2186   info.name = StringUtils::RemoveDuplicatedSpacesAndTabs(name);
2187   if (info.flag == 0)
2188     info.flag = CDemuxStream::FLAG_NONE;
2189
2190   CLog::Log(LOGDEBUG, "%s - Language = '%s' / Name = '%s' / Flag = '%u' from %s", __FUNCTION__, info.language.c_str(), info.name.c_str(), info.flag, strStream.c_str());
2191 }
2192
2193 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2194  */
2195 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2196 {
2197   if (URIUtils::HasExtension(strIdxPath, ".idx"))
2198   {
2199     CStdString strIdxFile;
2200     CStdString strIdxDirectory;
2201     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2202     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2203     {
2204       CStdString strSubFile;
2205       CStdString strSubDirectory;
2206       URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2207       if (URIUtils::IsInArchive(vecSubtitles[j]))
2208         strSubDirectory = CURL::Decode(strSubDirectory);
2209       if (URIUtils::HasExtension(strSubFile, ".sub") &&
2210           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2211            (strSubDirectory.size() >= 11 &&
2212             StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(strIdxPath,"")))))
2213       {
2214         strSubPath = vecSubtitles[j];
2215         return true;
2216       }
2217     }
2218   }
2219   return false;
2220 }
2221
2222 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2223  */
2224 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2225 {
2226   if (URIUtils::HasExtension(strSubPath, ".sub"))
2227   {
2228     CStdString strSubFile;
2229     CStdString strSubDirectory;
2230     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2231     if (URIUtils::IsInArchive(strSubPath))
2232       strSubDirectory = CURL::Decode(strSubDirectory);
2233     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2234     {
2235       CStdString strIdxFile;
2236       CStdString strIdxDirectory;
2237       URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
2238       if (URIUtils::HasExtension(strIdxFile, ".idx") &&
2239           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2240            (strSubDirectory.size() >= 11 &&
2241             StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(vecSubtitles[j],"")))))
2242         return true;
2243     }
2244   }
2245   return false;
2246 }
2247
2248 bool CUtil::CanBindPrivileged()
2249 {
2250 #ifdef TARGET_POSIX
2251
2252   if (geteuid() == 0)
2253     return true; //root user can always bind to privileged ports
2254
2255 #ifdef HAVE_LIBCAP
2256
2257   //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2258   bool canbind = false;
2259   cap_t capabilities = cap_get_proc();
2260   if (capabilities)
2261   {
2262     cap_flag_value_t value;
2263     if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
2264       canbind = value;
2265
2266     cap_free(capabilities);
2267   }
2268
2269   return canbind;
2270
2271 #else //HAVE_LIBCAP
2272
2273   return false;
2274
2275 #endif //HAVE_LIBCAP
2276
2277 #else //TARGET_POSIX
2278
2279   return true;
2280
2281 #endif //TARGET_POSIX
2282 }
2283
2284 bool CUtil::ValidatePort(int port)
2285 {
2286   // check that it's a valid port
2287 #ifdef TARGET_POSIX
2288   if (!CUtil::CanBindPrivileged() && (port < 1024 || port > 65535))
2289     return false;
2290   else
2291 #endif
2292   if (port <= 0 || port > 65535)
2293     return false;
2294
2295   return true;
2296 }
2297
2298 int CUtil::GetRandomNumber()
2299 {
2300 #ifdef TARGET_WINDOWS
2301   unsigned int number;
2302   if (rand_s(&number) == 0)
2303     return (int)number;
2304 #else
2305   return rand_r(&s_randomSeed);
2306 #endif
2307
2308   return rand();
2309 }
2310