[cstdstring] demise Format, replacing with StringUtils::Format
[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.IsEmpty())
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.IsEmpty())
168   {
169     if (url.GetHostName().IsEmpty())
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.IsEmpty())
180     strFilename = g_localizeStrings.Get(20174);
181
182   // HDHomerun Devices
183   else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
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.IsEmpty())
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.IsEmpty())
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   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, true);
247   CRegExp reYear(false, true);
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.Mid(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(' ') >= 0);
285
286     for (int i = 0; i < (int)strTitleAndYear.size(); i++)
287     {
288       char c = strTitleAndYear.GetAt(i);
289
290       if (c != '.')
291         initialDots = false;
292
293       if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
294       {
295         strTitleAndYear.SetAt(i, ' ');
296       }
297     }
298   }
299
300   strTitle = strTitleAndYear.Trim();
301
302   // append year
303   if (!strYear.IsEmpty())
304     strTitleAndYear = strTitle + " (" + strYear + ")";
305
306   // restore extension if needed
307   if (!bRemoveExtension)
308     strTitleAndYear += URIUtils::GetExtension(strFileName);
309 }
310
311 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
312 {
313   // Check if the filename is a fully qualified URL such as protocol://path/to/file
314   CURL plItemUrl(strFilename);
315   if (!plItemUrl.GetProtocol().IsEmpty())
316     return;
317
318   // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
319   if (strFilename.size() > 1)
320 #ifdef TARGET_POSIX
321     if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
322 #else
323     if ( strFilename[1] == ':' || (strFilename[0] == '\\' && strFilename[1] == '\\'))
324 #endif
325       return;
326
327   // add to base path and then clean
328   strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
329
330   // get rid of any /./ or \.\ that happen to be there
331   strFilename.Replace("\\.\\", "\\");
332   strFilename.Replace("/./", "/");
333
334   // now find any "\\..\\" and remove them via GetParentPath
335   int pos;
336   while ((pos = strFilename.Find("/../")) > 0)
337   {
338     CStdString basePath = strFilename.Left(pos+1);
339     strFilename = strFilename.Mid(pos+4);
340     basePath = URIUtils::GetParentPath(basePath);
341     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
342   }
343   while ((pos = strFilename.Find("\\..\\")) > 0)
344   {
345     CStdString basePath = strFilename.Left(pos+1);
346     strFilename = strFilename.Mid(pos+4);
347     basePath = URIUtils::GetParentPath(basePath);
348     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
349   }
350 }
351
352 #ifdef UNIT_TESTING
353 bool CUtil::TestGetQualifiedFilename()
354 {
355   CStdString file = "../foo"; GetQualifiedFilename("smb://", file);
356   if (file != "foo") return false;
357   file = "C:\\foo\\bar"; GetQualifiedFilename("smb://", file);
358   if (file != "C:\\foo\\bar") return false;
359   file = "../foo/./bar"; GetQualifiedFilename("smb://my/path", file);
360   if (file != "smb://my/foo/bar") return false;
361   file = "smb://foo/bar/"; GetQualifiedFilename("upnp://", file);
362   if (file != "smb://foo/bar/") return false;
363   return true;
364 }
365
366 bool CUtil::TestMakeLegalPath()
367 {
368   CStdString path;
369 #ifdef TARGET_WINDOWS
370   path = "C:\\foo\\bar"; path = MakeLegalPath(path);
371   if (path != "C:\\foo\\bar") return false;
372   path = "C:\\foo:\\bar\\"; path = MakeLegalPath(path);
373   if (path != "C:\\foo_\\bar\\") return false;
374 #elif
375   path = "/foo/bar/"; path = MakeLegalPath(path);
376   if (path != "/foo/bar/") return false;
377   path = "/foo?/bar"; path = MakeLegalPath(path);
378   if (path != "/foo_/bar") return false;
379 #endif
380   path = "smb://foo/bar"; path = MakeLegalPath(path);
381   if (path != "smb://foo/bar") return false;
382   path = "smb://foo/bar?/"; path = MakeLegalPath(path);
383   if (path != "smb://foo/bar_/") return false;
384   return true;
385 }
386 #endif
387
388 void CUtil::RunShortcut(const char* szShortcutPath)
389 {
390 }
391
392 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
393 {
394   strPath = CEnvironment::getenv(strTarget);
395
396 #ifdef TARGET_WINDOWS
397   if (strPath.find("..") != std::string::npos)
398   {
399     //expand potential relative path to full path
400     CStdStringW strPathW;
401     g_charsetConverter.utf8ToW(strPath, strPathW, false);
402     CWIN32Util::AddExtraLongPathPrefix(strPathW);
403     const unsigned int bufSize = GetFullPathNameW(strPathW, 0, NULL, NULL);
404     if (bufSize != 0)
405     {
406       wchar_t * buf = new wchar_t[bufSize];
407       if (GetFullPathNameW(strPathW, bufSize, buf, NULL) <= bufSize-1)
408       {
409         std::wstring expandedPathW(buf);
410         CWIN32Util::RemoveExtraLongPathPrefix(expandedPathW);
411         g_charsetConverter.wToUTF8(expandedPathW, strPath);
412       }
413
414       delete [] buf;
415     }
416   }
417 #endif
418
419   if (strPath.IsEmpty())
420   {
421     CStdString strHomePath = ResolveExecutablePath();
422 #if defined(TARGET_DARWIN)
423     int      result = -1;
424     char     given_path[2*MAXPATHLEN];
425     uint32_t path_size =2*MAXPATHLEN;
426
427     result = GetDarwinExecutablePath(given_path, &path_size);
428     if (result == 0)
429     {
430       // Move backwards to last /.
431       for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
432         given_path[n] = '\0';
433
434       #if defined(TARGET_DARWIN_IOS)
435         strcat(given_path, "/XBMCData/XBMCHome/");
436       #else
437         // Assume local path inside application bundle.
438         strcat(given_path, "../Resources/XBMC/");
439       #endif
440
441       // Convert to real path.
442       char real_path[2*MAXPATHLEN];
443       if (realpath(given_path, real_path) != NULL)
444       {
445         strPath = real_path;
446         return;
447       }
448     }
449 #endif
450     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
451     if (last_sep != string::npos)
452       strPath = strHomePath.Left(last_sep);
453     else
454       strPath = strHomePath;
455   }
456
457 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
458   /* Change strPath accordingly when target is XBMC_HOME and when INSTALL_PATH
459    * and BIN_INSTALL_PATH differ
460    */
461   CStdString installPath = INSTALL_PATH;
462   CStdString binInstallPath = BIN_INSTALL_PATH;
463   if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
464   {
465     int pos = strPath.length() - binInstallPath.length();
466     CStdString tmp = strPath;
467     tmp.erase(0, pos);
468     if (!tmp.compare(binInstallPath))
469     {
470       strPath.erase(pos, strPath.length());
471       strPath.append(installPath);
472     }
473   }
474 #endif
475 }
476
477 bool CUtil::IsPVR(const CStdString& strFile)
478 {
479   return StringUtils::StartsWithNoCase(strFile, "pvr:");
480 }
481
482 bool CUtil::IsHTSP(const CStdString& strFile)
483 {
484   return StringUtils::StartsWithNoCase(strFile, "htsp:");
485 }
486
487 bool CUtil::IsLiveTV(const CStdString& strFile)
488 {
489   if (StringUtils::StartsWithNoCase(strFile, "pvr://channels"))
490     return true;
491
492   if(URIUtils::IsTuxBox(strFile)
493   || URIUtils::IsVTP(strFile)
494   || URIUtils::IsHDHomeRun(strFile)
495   || URIUtils::IsHTSP(strFile)
496   || StringUtils::StartsWithNoCase(strFile, "sap:"))
497     return true;
498
499   if (URIUtils::IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
500     return true;
501
502   return false;
503 }
504
505 bool CUtil::IsTVRecording(const CStdString& strFile)
506 {
507   return StringUtils::StartsWithNoCase(strFile, "pvr://recording");
508 }
509
510 bool CUtil::IsPicture(const CStdString& strFile)
511 {
512   return URIUtils::HasExtension(strFile,
513                   g_advancedSettings.m_pictureExtensions + "|.tbn|.dds");
514 }
515
516 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
517 {
518   if (strFileOrFolder.IsEmpty())
519     return false;
520
521   CRegExp regExExcludes(true, true);  // case insensitive regex
522
523   for (unsigned int i = 0; i < regexps.size(); i++)
524   {
525     if (!regExExcludes.RegComp(regexps[i].c_str()))
526     { // invalid regexp - complain in logs
527       CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
528       continue;
529     }
530     if (regExExcludes.RegFind(strFileOrFolder) > -1)
531     {
532       CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
533       return true;
534     }
535   }
536   return false;
537 }
538
539 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
540 {
541   strDir = strURL;
542   if (!URIUtils::IsRemote(strURL)) return ;
543   if (URIUtils::IsDVD(strURL)) return ;
544
545   CURL url(strURL);
546   strDir = StringUtils::Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
547 }
548
549 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
550 {
551   CStdString strFilename = URIUtils::GetFileName(strFile);
552   if (strFilename.Equals("video_ts.ifo")) return 0;
553   //VTS_[TITLE]_0.IFO
554   return atoi(strFilename.Mid(4, 2).c_str());
555 }
556
557 CStdString CUtil::GetFileMD5(const CStdString& strPath)
558 {
559   CFile file;
560   CStdString result;
561   if (file.Open(strPath))
562   {
563     XBMC::XBMC_MD5 md5;
564     char temp[1024];
565     int pos=0;
566     int read=1;
567     while (read > 0)
568     {
569       read = file.Read(temp,1024);
570       pos += read;
571       md5.append(temp,read);
572     }
573     md5.getDigest(result);
574     file.Close();
575   }
576
577   return result;
578 }
579
580 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
581 {
582   CStdString strFName = URIUtils::GetFileName(strFileName);
583   strDescription = strFileName.Left(strFileName.size() - strFName.size());
584   URIUtils::RemoveSlashAtEnd(strDescription);
585
586   int iPos = strDescription.ReverseFind("\\");
587   if (iPos < 0)
588     iPos = strDescription.ReverseFind("/");
589   if (iPos >= 0)
590   {
591     CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
592     strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
593   }
594   else if (strDescription.size() <= 0)
595     strDescription = strFName;
596   return true;
597 }
598
599 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
600 {
601   if ( !g_mediaManager.IsDiscInDrive() )
602   {
603     strIcon = "DefaultDVDEmpty.png";
604     return ;
605   }
606
607   if ( URIUtils::IsDVD(strPath) )
608   {
609 #ifdef HAS_DVD_DRIVE
610     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
611     //  xbox DVD
612     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
613     {
614       strIcon = "DefaultXboxDVD.png";
615       return ;
616     }
617 #endif
618     strIcon = "DefaultDVDRom.png";
619     return ;
620   }
621
622   if ( URIUtils::IsISO9660(strPath) )
623   {
624 #ifdef HAS_DVD_DRIVE
625     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
626     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
627     {
628       strIcon = "DefaultVCD.png";
629       return ;
630     }
631 #endif
632     strIcon = "DefaultDVDRom.png";
633     return ;
634   }
635
636   if ( URIUtils::IsCDDA(strPath) )
637   {
638     strIcon = "DefaultCDDA.png";
639     return ;
640   }
641 }
642
643 void CUtil::RemoveTempFiles()
644 {
645   CStdString searchPath = CProfilesManager::Get().GetDatabaseFolder();
646   CFileItemList items;
647   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
648     return;
649
650   for (int i = 0; i < items.Size(); ++i)
651   {
652     if (items[i]->m_bIsFolder)
653       continue;
654     XFILE::CFile::Delete(items[i]->GetPath());
655   }
656 }
657
658 void CUtil::ClearSubtitles()
659 {
660   //delete cached subs
661   CFileItemList items;
662   CDirectory::GetDirectory("special://temp/",items);
663   for( int i=0;i<items.Size();++i)
664   {
665     if (!items[i]->m_bIsFolder)
666     {
667       if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
668       {
669         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
670         CFile::Delete(items[i]->GetPath());
671       }
672     }
673   }
674 }
675
676 void CUtil::ClearTempFonts()
677 {
678   CStdString searchPath = "special://temp/fonts/";
679
680   if (!CFile::Exists(searchPath))
681     return;
682
683   CFileItemList items;
684   CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE);
685
686   for (int i=0; i<items.Size(); ++i)
687   {
688     if (items[i]->m_bIsFolder)
689       continue;
690     CFile::Delete(items[i]->GetPath());
691   }
692 }
693
694 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
695
696 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
697 {
698   int64_t n;
699   n = high;
700   n <<= 32;
701   n += low;
702   return n;
703 }
704
705 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
706 {
707   if (!fn_template.Find("%03d"))
708     return "";
709
710   CStdString searchPath = URIUtils::GetDirectory(fn_template);
711   CStdString mask = URIUtils::GetExtension(fn_template);
712   CStdString name = StringUtils::Format(fn_template.c_str(), 0);
713
714   CFileItemList items;
715   if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
716     return name;
717
718   items.SetFastLookup(true);
719   for (int i = 0; i <= max; i++)
720   {
721     CStdString name = StringUtils::Format(fn_template.c_str(), i);
722     if (!items.Get(name))
723       return name;
724   }
725   return "";
726 }
727
728 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
729 {
730   if (!path_template.Find("%04d"))
731     return "";
732   
733   for (int i = 0; i <= max; i++)
734   {
735     CStdString name = StringUtils::Format(path_template.c_str(), i);
736     if (!CFile::Exists(name))
737       return name;
738   }
739   return "";
740 }
741
742 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
743 {
744   result->st_dev = stat->st_dev;
745   result->st_ino = stat->st_ino;
746   result->st_mode = stat->st_mode;
747   result->st_nlink = stat->st_nlink;
748   result->st_uid = stat->st_uid;
749   result->st_gid = stat->st_gid;
750   result->st_rdev = stat->st_rdev;
751   result->st_size = (int64_t)stat->st_size;
752
753 #ifndef TARGET_POSIX
754   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
755   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
756   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
757 #else
758   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
759   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
760   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
761 #endif
762 }
763
764 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
765 {
766   result->st_dev = stat->st_dev;
767   result->st_ino = stat->st_ino;
768   result->st_mode = stat->st_mode;
769   result->st_nlink = stat->st_nlink;
770   result->st_uid = stat->st_uid;
771   result->st_gid = stat->st_gid;
772   result->st_rdev = stat->st_rdev;
773   result->st_size = stat->st_size;
774 #ifndef TARGET_POSIX
775   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
776   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
777   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
778 #else
779   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
780   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
781   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
782 #endif
783 }
784
785 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
786 {
787   result->st_dev = stat->st_dev;
788   result->st_ino = stat->st_ino;
789   result->st_mode = stat->st_mode;
790   result->st_nlink = stat->st_nlink;
791   result->st_uid = stat->st_uid;
792   result->st_gid = stat->st_gid;
793   result->st_rdev = stat->st_rdev;
794   result->st_size = stat->st_size;
795 #ifndef TARGET_POSIX
796   result->st_atime = stat->st_atime;
797   result->st_mtime = stat->st_mtime;
798   result->st_ctime = stat->st_ctime;
799 #else
800   result->st_atime = stat->_st_atime;
801   result->st_mtime = stat->_st_mtime;
802   result->st_ctime = stat->_st_ctime;
803 #endif
804 }
805
806 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
807 {
808   result->st_dev = stat->st_dev;
809   result->st_ino = stat->st_ino;
810   result->st_mode = stat->st_mode;
811   result->st_nlink = stat->st_nlink;
812   result->st_uid = stat->st_uid;
813   result->st_gid = stat->st_gid;
814   result->st_rdev = stat->st_rdev;
815 #ifndef TARGET_POSIX
816   if (stat->st_size <= LONG_MAX)
817     result->st_size = (_off_t)stat->st_size;
818 #else
819   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
820     result->st_size = stat->st_size;
821 #endif
822   else
823   {
824     result->st_size = 0;
825     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
826   }
827   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
828   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
829   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
830 }
831
832 #ifdef TARGET_WINDOWS
833 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
834 {
835   result->st_dev = stat->st_dev;
836   result->st_ino = stat->st_ino;
837   result->st_mode = stat->st_mode;
838   result->st_nlink = stat->st_nlink;
839   result->st_uid = stat->st_uid;
840   result->st_gid = stat->st_gid;
841   result->st_rdev = stat->st_rdev;
842 #ifndef TARGET_POSIX
843   if (stat->st_size <= LONG_MAX)
844     result->st_size = (_off_t)stat->st_size;
845 #else
846   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
847     result->st_size = stat->st_size;
848 #endif
849   else
850   {
851     result->st_size = 0;
852     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
853   }
854 #ifndef TARGET_POSIX
855   result->st_atime = stat->st_atime;
856   result->st_mtime = stat->st_mtime;
857   result->st_ctime = stat->st_ctime;
858 #else
859   result->st_atime = stat->_st_atime;
860   result->st_mtime = stat->_st_mtime;
861   result->st_ctime = stat->_st_ctime;
862 #endif
863 }
864 #endif
865
866 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
867 {
868   // Function to create all directories at once instead
869   // of calling CreateDirectory for every subdir.
870   // Creates the directory and subdirectories if needed.
871
872   // return true if directory already exist
873   if (CDirectory::Exists(strPath)) return true;
874
875   // we currently only allow HD and smb, nfs and afp paths
876   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
877   {
878     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
879     return false;
880   }
881
882   CStdStringArray dirs = URIUtils::SplitPath(strPath);
883   CStdString dir(dirs.front());
884   URIUtils::AddSlashAtEnd(dir);
885   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
886   {
887     dir = URIUtils::AddFileToFolder(dir, *it);
888     CDirectory::Create(dir);
889   }
890
891   // was the final destination directory successfully created ?
892   if (!CDirectory::Exists(strPath)) return false;
893   return true;
894 }
895
896 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
897 {
898   CStdString result = strFile;
899
900   result.Replace('/', '_');
901   result.Replace('\\', '_');
902   result.Replace('?', '_');
903
904   if (LegalType == LEGAL_WIN32_COMPAT)
905   {
906     // just filter out some illegal characters on windows
907     result.Replace(':', '_');
908     result.Replace('*', '_');
909     result.Replace('?', '_');
910     result.Replace('\"', '_');
911     result.Replace('<', '_');
912     result.Replace('>', '_');
913     result.Replace('|', '_');
914     result.TrimRight(".");
915     result.TrimRight(" ");
916   }
917   return result;
918 }
919
920 // legalize entire path
921 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
922 {
923   if (URIUtils::IsStack(strPathAndFile))
924     return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
925   if (URIUtils::IsMultiPath(strPathAndFile))
926     return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
927   if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile) && !URIUtils::IsAfp(strPathAndFile))
928     return strPathAndFile; // we don't support writing anywhere except HD, SMB, NFS and AFP - no need to legalize path
929
930   bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
931   CStdStringArray dirs = URIUtils::SplitPath(strPathAndFile);
932   // we just add first token to path and don't legalize it - possible values: 
933   // "X:" (local win32), "" (local unix - empty string before '/') or
934   // "protocol://domain"
935   CStdString dir(dirs.front());
936   URIUtils::AddSlashAtEnd(dir);
937   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
938     dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
939   if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
940   return dir;
941 }
942
943 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
944 {
945   CStdString result = path;
946
947   // Don't do any stuff on URLs containing %-characters or protocols that embed
948   // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
949   // recurse and crash XBMC
950   if (URIUtils::IsURL(path) && 
951      (path.Find('%') >= 0 ||
952       StringUtils::StartsWithNoCase(path, "apk:") ||
953       StringUtils::StartsWithNoCase(path, "zip:") ||
954       StringUtils::StartsWithNoCase(path, "rar:") ||
955       StringUtils::StartsWithNoCase(path, "stack:") ||
956       StringUtils::StartsWithNoCase(path, "bluray:") ||
957       StringUtils::StartsWithNoCase(path, "multipath:") ))
958     return result;
959
960   // check the path for incorrect slashes
961 #ifdef TARGET_WINDOWS
962   if (URIUtils::IsDOSPath(path))
963   {
964     result.Replace('/', '\\');
965     /* The double slash correction should only be used when *absolutely*
966        necessary! This applies to certain DLLs or use from Python DLLs/scripts
967        that incorrectly generate double (back) slashes.
968     */
969     if (bFixDoubleSlashes)
970     {
971       // Fixup for double back slashes (but ignore the \\ of unc-paths)
972       for (int x = 1; x < result.GetLength() - 1; x++)
973       {
974         if (result[x] == '\\' && result[x+1] == '\\')
975           result.Delete(x);
976       }
977     }
978   }
979   else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
980 #endif
981   {
982     result.Replace('\\', '/');
983     /* The double slash correction should only be used when *absolutely*
984        necessary! This applies to certain DLLs or use from Python DLLs/scripts
985        that incorrectly generate double (back) slashes.
986     */
987     if (bFixDoubleSlashes)
988     {
989       // Fixup for double forward slashes(/) but don't touch the :// of URLs
990       for (int x = 2; x < result.GetLength() - 1; x++)
991       {
992         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
993           result.Delete(x);
994       }
995     }
996   }
997   return result;
998 }
999
1000 bool CUtil::IsUsingTTFSubtitles()
1001 {
1002   return URIUtils::HasExtension(CSettings::Get().GetString("subtitles.font"), ".ttf");
1003 }
1004
1005 #ifdef UNIT_TESTING
1006 bool CUtil::TestSplitExec()
1007 {
1008   CStdString function;
1009   vector<CStdString> params;
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   params.clear();
1014   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
1015   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
1016     return false;
1017   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")", function, params);
1018   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\\")
1019     return false;
1020   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")", function, params);
1021   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\\\test\\\\foo\\")
1022     return false;
1023   CUtil::SplitExecFunction("SetProperty(Foo,\"\")", function, params);
1024   if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
1025    return false;
1026   CUtil::SplitExecFunction("SetProperty(foo,ba(\"ba black )\",sheep))", function, params);
1027   if (function != "SetProperty" || params.size() != 2 || params[0] != "foo" || params[1] != "ba(\"ba black )\",sheep)")
1028     return false;
1029   return true;
1030 }
1031 #endif
1032
1033 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
1034 {
1035   CStdString paramString;
1036
1037   int iPos = execString.Find("(");
1038   int iPos2 = execString.ReverseFind(")");
1039   if (iPos > 0 && iPos2 > 0)
1040   {
1041     paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
1042     function = execString.Left(iPos);
1043   }
1044   else
1045     function = execString;
1046
1047   // remove any whitespace, and the standard prefix (if it exists)
1048   function.Trim();
1049   if( StringUtils::StartsWithNoCase(function, "xbmc.") )
1050     function.Delete(0, 5);
1051
1052   SplitParams(paramString, parameters);
1053 }
1054
1055 void CUtil::SplitParams(const CStdString &paramString, std::vector<CStdString> &parameters)
1056 {
1057   bool inQuotes = false;
1058   bool lastEscaped = false; // only every second character can be escaped
1059   int inFunction = 0;
1060   size_t whiteSpacePos = 0;
1061   CStdString parameter;
1062   parameters.clear();
1063   for (size_t pos = 0; pos < paramString.size(); pos++)
1064   {
1065     char ch = paramString[pos];
1066     bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1067     lastEscaped = escaped;
1068     if (inQuotes)
1069     { // if we're in a quote, we accept everything until the closing quote
1070       if (ch == '"' && !escaped)
1071       { // finished a quote - no need to add the end quote to our string
1072         inQuotes = false;
1073       }
1074     }
1075     else
1076     { // not in a quote, so check if we should be starting one
1077       if (ch == '"' && !escaped)
1078       { // start of quote - no need to add the quote to our string
1079         inQuotes = true;
1080       }
1081       if (inFunction && ch == ')')
1082       { // end of a function
1083         inFunction--;
1084       }
1085       if (ch == '(')
1086       { // start of function
1087         inFunction++;
1088       }
1089       if (!inFunction && ch == ',')
1090       { // not in a function, so a comma signfies the end of this parameter
1091         if (whiteSpacePos)
1092           parameter = parameter.Left(whiteSpacePos);
1093         // trim off start and end quotes
1094         if (parameter.GetLength() > 1 && parameter[0] == '"' && parameter[parameter.GetLength() - 1] == '"')
1095           parameter = parameter.Mid(1,parameter.GetLength() - 2);
1096         else if (parameter.GetLength() > 3 && parameter[parameter.GetLength() - 1] == '"')
1097         {
1098           // check name="value" style param.
1099           int quotaPos = parameter.Find('"');
1100           if (quotaPos > 1 && quotaPos < parameter.GetLength() - 1 && parameter[quotaPos - 1] == '=')
1101           {
1102             parameter.Delete(parameter.GetLength() - 1);
1103             parameter.Delete(quotaPos);
1104           }
1105         }
1106         parameters.push_back(parameter);
1107         parameter.Empty();
1108         whiteSpacePos = 0;
1109         continue;
1110       }
1111     }
1112     if ((ch == '"' || ch == '\\') && escaped)
1113     { // escaped quote or backslash
1114       parameter[parameter.size()-1] = ch;
1115       continue;
1116     }
1117     // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1118     if (ch == ' ' && !inQuotes)
1119     {
1120       if (parameter.IsEmpty()) // skip whitespace on left
1121         continue;
1122       if (!whiteSpacePos) // make a note of where whitespace starts on the right
1123         whiteSpacePos = parameter.size();
1124     }
1125     else
1126       whiteSpacePos = 0;
1127     parameter += ch;
1128   }
1129   if (inFunction || inQuotes)
1130     CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
1131   if (whiteSpacePos)
1132     parameter = parameter.Left(whiteSpacePos);
1133   // trim off start and end quotes
1134   if (parameter.GetLength() > 1 && parameter[0] == '"' && parameter[parameter.GetLength() - 1] == '"')
1135     parameter = parameter.Mid(1,parameter.GetLength() - 2);
1136   else if (parameter.GetLength() > 3 && parameter[parameter.GetLength() - 1] == '"')
1137   {
1138     // check name="value" style param.
1139     int quotaPos = parameter.Find('"');
1140     if (quotaPos > 1 && quotaPos < parameter.GetLength() - 1 && parameter[quotaPos - 1] == '=')
1141     {
1142       parameter.Delete(parameter.GetLength() - 1);
1143       parameter.Delete(quotaPos);
1144     }
1145   }
1146   if (!parameter.IsEmpty() || parameters.size())
1147     parameters.push_back(parameter);
1148 }
1149
1150 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1151 {
1152   if (strPath1.IsEmpty())
1153     return -1;
1154
1155   // copy as we may change strPath
1156   CStdString strPath = strPath1;
1157
1158   // Check for special protocols
1159   CURL checkURL(strPath);
1160
1161   // stack://
1162   if (checkURL.GetProtocol() == "stack")
1163     strPath.Delete(0, 8); // remove the stack protocol
1164
1165   if (checkURL.GetProtocol() == "shout")
1166     strPath = checkURL.GetHostName();
1167   if (checkURL.GetProtocol() == "tuxbox")
1168     return 1;
1169   if (checkURL.GetProtocol() == "plugin")
1170     return 1;
1171   if (checkURL.GetProtocol() == "multipath")
1172     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1173
1174   bIsSourceName = false;
1175   int iIndex = -1;
1176   int iLength = -1;
1177   // we first test the NAME of a source
1178   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1179   {
1180     CMediaSource share = VECSOURCES.at(i);
1181     CStdString 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       int iPos = strName.ReverseFind('(');
1193       if (iPos > 1)
1194         strName = strName.Mid(0, iPos - 1);
1195     }
1196     if (strPath.Equals(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   CStdString strDest = urlDest.GetWithoutUserDetails();
1210   ForceForwardSlashes(strDest);
1211   if (!URIUtils::HasSlashAtEnd(strDest))
1212     strDest += "/";
1213   int iLenPath = strDest.size();
1214
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       int 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.IsEmpty() && strSpecial[0] == '$')
1292   {
1293     if (StringUtils::StartsWithNoCase(strSpecial, "$home"))
1294       return URIUtils::AddFileToFolder("special://home/", strSpecial.Mid(5));
1295     else if (StringUtils::StartsWithNoCase(strSpecial, "$subtitles"))
1296       return URIUtils::AddFileToFolder("special://subtitles/", strSpecial.Mid(10));
1297     else if (StringUtils::StartsWithNoCase(strSpecial, "$userdata"))
1298       return URIUtils::AddFileToFolder("special://userdata/", strSpecial.Mid(9));
1299     else if (StringUtils::StartsWithNoCase(strSpecial, "$database"))
1300       return URIUtils::AddFileToFolder("special://database/", strSpecial.Mid(9));
1301     else if (StringUtils::StartsWithNoCase(strSpecial, "$thumbnails"))
1302       return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.Mid(11));
1303     else if (StringUtils::StartsWithNoCase(strSpecial, "$recordings"))
1304       return URIUtils::AddFileToFolder("special://recordings/", strSpecial.Mid(11));
1305     else if (StringUtils::StartsWithNoCase(strSpecial, "$screenshots"))
1306       return URIUtils::AddFileToFolder("special://screenshots/", strSpecial.Mid(12));
1307     else if (StringUtils::StartsWithNoCase(strSpecial, "$musicplaylists"))
1308       return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.Mid(15));
1309     else if (StringUtils::StartsWithNoCase(strSpecial, "$videoplaylists"))
1310       return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.Mid(15));
1311     else if (StringUtils::StartsWithNoCase(strSpecial, "$cdrips"))
1312       return URIUtils::AddFileToFolder("special://cdrips/", strSpecial.Mid(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.Mid(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 (fileName.Left(prefix.GetLength()) == 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   int iPos = strPath.ReverseFind('\\');
1399   while (iPos > 0)
1400   {
1401     strPath.at(iPos) = '/';
1402     iPos = strPath.ReverseFind('\\');
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   strAlbumTemp.MakeLower();
1413   CStdString strAlbum = strAlbum1;
1414   strAlbum.MakeLower();
1415   double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1416   double fArtistPercentage = 0.0f;
1417   if (!strArtist1.IsEmpty())
1418   {
1419     CStdString strArtistTemp = strArtistTemp1;
1420     strArtistTemp.MakeLower();
1421     CStdString strArtist = strArtist1;
1422     strArtist.MakeLower();
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, int iTextMaxLength)
1430 {
1431   int 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 != CStdString::npos )
1440     cDelim = '\\';
1441   else
1442   {
1443     nPos = StrInput.find_last_of( '/' );
1444     if ( nPos != CStdString::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 != CStdString::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 (!StrInput.Replace(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 = StrInput.Left(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" && pItem->GetLabel().CompareNoCase("Textures.xpr")) ||
1544           (strExtension == ".xbt" && pItem->GetLabel().CompareNoCase("Textures.xbt")))
1545       {
1546         CStdString strLabel = pItem->GetLabel();
1547         vecTheme.push_back(strLabel.Mid(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 (size_t i=0; i<args.size(); i++)
1576   {
1577     CStdString &s = args[i];
1578     CStdString stripd = s.Trim();
1579     if (stripd[0] == '"' || stripd[0] == '\'')
1580     {
1581       s = s.TrimLeft();
1582       s = s.Right(s.size() - 1);
1583     }
1584     if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
1585     {
1586       s = s.TrimRight();
1587       s = s.Left(s.size() - 1);
1588     }
1589   }
1590
1591   return Command(args, waitExit);
1592 }
1593
1594 //
1595 // FIXME, this should be merged with the function below.
1596 //
1597 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1598 {
1599 #ifdef _DEBUG
1600   printf("Executing: ");
1601   for (size_t i=0; i<arrArgs.size(); i++)
1602     printf("%s ", arrArgs[i].c_str());
1603   printf("\n");
1604 #endif
1605
1606   pid_t child = fork();
1607   int n = 0;
1608   if (child == 0)
1609   {
1610     if (!waitExit)
1611     {
1612       // fork again in order not to leave a zombie process
1613       child = fork();
1614       if (child == -1)
1615         _exit(2);
1616       else if (child != 0)
1617         _exit(0);
1618     }
1619     close(0);
1620     close(1);
1621     close(2);
1622     if (arrArgs.size() > 0)
1623     {
1624       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1625       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1626       for (size_t i=0; i<arrArgs.size(); i++)
1627         args[i] = (char *)arrArgs[i].c_str();
1628       execvp(args[0], args);
1629     }
1630   }
1631   else
1632   {
1633     waitpid(child, &n, 0);
1634   }
1635
1636   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
1637 }
1638
1639 bool CUtil::SudoCommand(const CStdString &strCommand)
1640 {
1641   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
1642   pid_t child = fork();
1643   int n = 0;
1644   if (child == 0)
1645   {
1646     close(0); // close stdin to avoid sudo request password
1647     close(1);
1648     close(2);
1649     CStdStringArray arrArgs;
1650     StringUtils::SplitString(strCommand, " ", arrArgs);
1651     if (arrArgs.size() > 0)
1652     {
1653       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1654       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1655       args[0] = (char *)"/usr/bin/sudo";
1656       args[1] = (char *)"-S";
1657       for (size_t i=0; i<arrArgs.size(); i++)
1658       {
1659         args[i+2] = (char *)arrArgs[i].c_str();
1660       }
1661       execvp("/usr/bin/sudo", args);
1662     }
1663   }
1664   else
1665     waitpid(child, &n, 0);
1666
1667   return WEXITSTATUS(n) == 0;
1668 }
1669 #endif
1670
1671 int CUtil::LookupRomanDigit(char roman_digit)
1672 {
1673   switch (roman_digit)
1674   {
1675     case 'i':
1676     case 'I':
1677       return 1;
1678     case 'v':
1679     case 'V':
1680       return 5;
1681     case 'x':
1682     case 'X':
1683       return 10;
1684     case 'l':
1685     case 'L':
1686       return 50;
1687     case 'c':
1688     case 'C':
1689       return 100;
1690     case 'd':
1691     case 'D':
1692       return 500;
1693     case 'm':
1694     case 'M':
1695       return 1000;
1696     default:
1697       return 0;
1698   }
1699 }
1700
1701 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
1702 {
1703   
1704   int decimal = -1;
1705
1706   if (roman_numeral && roman_numeral[0])
1707   {
1708     int temp_sum  = 0,
1709         last      = 0,
1710         repeat    = 0,
1711         trend     = 1;
1712     decimal = 0;
1713     while (*roman_numeral)
1714     {
1715       int digit = CUtil::LookupRomanDigit(*roman_numeral);
1716       int test  = last;
1717       
1718       // General sanity checks
1719
1720       // numeral not in LUT
1721       if (!digit)
1722         return -1;
1723       
1724       while (test > 5)
1725         test /= 10;
1726       
1727       // N = 10^n may not precede (N+1) > 10^(N+1)
1728       if (test == 1 && digit > last * 10)
1729         return -1;
1730       
1731       // N = 5*10^n may not precede (N+1) >= N
1732       if (test == 5 && digit >= last) 
1733         return -1;
1734
1735       // End general sanity checks
1736
1737       if (last < digit)
1738       {
1739         // smaller numerals may not repeat before a larger one
1740         if (repeat) 
1741           return -1;
1742
1743         temp_sum += digit;
1744         
1745         repeat  = 0;
1746         trend   = 0;
1747       }
1748       else if (last == digit)
1749       {
1750         temp_sum += digit;
1751         repeat++;
1752         trend = 1;
1753       }
1754       else
1755       {
1756         if (!repeat)
1757           decimal += 2 * last - temp_sum;
1758         else
1759           decimal += temp_sum;
1760         
1761         temp_sum = digit;
1762
1763         trend   = 1;
1764         repeat  = 0;
1765       }
1766       // Post general sanity checks
1767
1768       // numerals may not repeat more than thrice
1769       if (repeat == 3)
1770         return -1;
1771
1772       last = digit;
1773       roman_numeral++;
1774     }
1775
1776     if (trend)
1777       decimal += temp_sum;
1778     else
1779       decimal += 2 * last - temp_sum;
1780   }
1781   return decimal;
1782 }
1783
1784 CStdString CUtil::ResolveExecutablePath()
1785 {
1786   CStdString strExecutablePath;
1787 #ifdef TARGET_WINDOWS
1788   static const size_t bufSize = MAX_PATH * 2;
1789   wchar_t* buf = new wchar_t[bufSize];
1790   buf[0] = 0;
1791   ::GetModuleFileNameW(0, buf, bufSize);
1792   buf[bufSize-1] = 0;
1793   g_charsetConverter.wToUTF8(buf,strExecutablePath);
1794   delete[] buf;
1795 #elif defined(TARGET_DARWIN)
1796   char     given_path[2*MAXPATHLEN];
1797   uint32_t path_size =2*MAXPATHLEN;
1798
1799   GetDarwinExecutablePath(given_path, &path_size);
1800   strExecutablePath = given_path;
1801 #elif defined(TARGET_FREEBSD)                                                                                                                                                                   
1802   char buf[PATH_MAX];
1803   size_t buflen;
1804   int mib[4];
1805
1806   mib[0] = CTL_KERN;
1807   mib[1] = KERN_PROC;
1808   mib[2] = KERN_PROC_PATHNAME;
1809   mib[3] = getpid();
1810
1811   buflen = sizeof(buf) - 1;
1812   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
1813     strExecutablePath = "";
1814   else
1815     strExecutablePath = buf;
1816 #else
1817   /* Get our PID and build the name of the link in /proc */
1818   pid_t pid = getpid();
1819   char linkname[64]; /* /proc/<pid>/exe */
1820   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
1821
1822   /* Now read the symbolic link */
1823   char buf[PATH_MAX + 1];
1824   buf[0] = 0;
1825
1826   int ret = readlink(linkname, buf, sizeof(buf) - 1);
1827   if (ret != -1)
1828     buf[ret] = 0;
1829
1830   strExecutablePath = buf;
1831 #endif
1832   return strExecutablePath;
1833 }
1834
1835 CStdString CUtil::GetFrameworksPath(bool forPython)
1836 {
1837   CStdString strFrameworksPath;
1838 #if defined(TARGET_DARWIN)
1839   char     given_path[2*MAXPATHLEN];
1840   uint32_t path_size =2*MAXPATHLEN;
1841
1842   GetDarwinFrameworkPath(forPython, given_path, &path_size);
1843   strFrameworksPath = given_path;
1844 #endif
1845   return strFrameworksPath;
1846 }
1847
1848 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
1849 {
1850   unsigned int startTimer = XbmcThreads::SystemClockMillis();
1851   
1852   // new array for commons sub dirs
1853   const char * common_sub_dirs[] = {"subs",
1854     "Subs",
1855     "subtitles",
1856     "Subtitles",
1857     "vobsubs",
1858     "Vobsubs",
1859     "sub",
1860     "Sub",
1861     "vobsub",
1862     "Vobsub",
1863     "subtitle",
1864     "Subtitle",
1865     NULL};
1866   
1867   vector<CStdString> vecExtensionsCached;
1868   
1869   CFileItem item(strMovie, false);
1870   if ( item.IsInternetStream()
1871     || item.IsHDHomeRun()
1872     || item.IsSlingbox()
1873     || item.IsPlayList()
1874     || item.IsLiveTV()
1875     || !item.IsVideo())
1876     return;
1877   
1878   vector<CStdString> strLookInPaths;
1879   
1880   CStdString strMovieFileName;
1881   CStdString strPath;
1882   
1883   URIUtils::Split(strMovie, strPath, strMovieFileName);
1884   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
1885   strLookInPaths.push_back(strPath);
1886   
1887   if (!CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() && !CSettings::Get().GetString("subtitles.custompath").empty()) // to avoid checking non-existent directories (network) every time..
1888   {
1889     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(CSettings::Get().GetString("subtitles.custompath")))
1890     {
1891       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
1892       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1893     }
1894     else if (!CDirectory::Exists(CSettings::Get().GetString("subtitles.custompath")))
1895     {
1896       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
1897       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1898     }
1899     
1900     CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(1);
1901   }
1902   
1903   if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
1904   {
1905     CURL url(strMovie);
1906     CStdString strArchive = url.GetHostName();
1907     URIUtils::Split(strArchive, strPath, strMovieFileName);
1908     strLookInPaths.push_back(strPath);
1909   }
1910   
1911   int iSize = strLookInPaths.size();
1912   for (int i=0; i<iSize; ++i)
1913   {
1914     CStdStringArray directories;
1915     int nTokens = StringUtils::SplitString( strLookInPaths[i], "/", directories );
1916     if (nTokens == 1)
1917       StringUtils::SplitString( strLookInPaths[i], "\\", directories );
1918
1919     // if it's inside a cdX dir, add parent path
1920     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
1921     {
1922       CStdString strPath2;
1923       URIUtils::GetParentPath(strLookInPaths[i], strPath2);
1924       strLookInPaths.push_back(strPath2);
1925     }
1926   }
1927
1928   // checking if any of the common subdirs exist ..
1929   iSize = strLookInPaths.size();
1930   for (int i=0;i<iSize;++i)
1931   {
1932     for (int j=0; common_sub_dirs[j]; j++)
1933     {
1934       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
1935       URIUtils::AddSlashAtEnd(strPath2);
1936       if (CDirectory::Exists(strPath2))
1937         strLookInPaths.push_back(strPath2);
1938     }
1939   }
1940   // .. done checking for common subdirs
1941   
1942   // check if there any cd-directories in the paths we have added so far
1943   iSize = strLookInPaths.size();
1944   for (int i=0;i<9;++i) // 9 cd's
1945   {
1946     CStdString cdDir = StringUtils::Format("cd%i",i+1);
1947     for (int i=0;i<iSize;++i)
1948     {
1949       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
1950       URIUtils::AddSlashAtEnd(strPath2);
1951       bool pathAlreadyAdded = false;
1952       for (unsigned int i=0; i<strLookInPaths.size(); i++)
1953       {
1954         // if movie file is inside cd-dir, this directory can exist in vector already
1955         if (strLookInPaths[i].Equals( strPath2 ) )
1956           pathAlreadyAdded = true;
1957       }
1958       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
1959         strLookInPaths.push_back(strPath2);
1960     }
1961   }
1962   // .. done checking for cd-dirs
1963   
1964   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
1965   if (CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() == 1)
1966   {
1967     strPath = CSettings::Get().GetString("subtitles.custompath");
1968     URIUtils::AddSlashAtEnd(strPath);
1969     strLookInPaths.push_back(strPath);
1970   }
1971   
1972   CStdString strLExt;
1973   CStdString strDest;
1974   CStdString strItem;
1975   
1976   // 2 steps for movie directory and alternate subtitles directory
1977   CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
1978   for (unsigned int step = 0; step < strLookInPaths.size(); step++)
1979   {
1980     if (strLookInPaths[step].length() != 0)
1981     {
1982       CFileItemList items;
1983       
1984       CDirectory::GetDirectory(strLookInPaths[step], items, g_advancedSettings.m_subtitlesExtensions, DIR_FLAG_NO_FILE_DIRS);
1985       
1986       for (int j = 0; j < items.Size(); j++)
1987       {
1988         URIUtils::Split(items[j]->GetPath(), strPath, strItem);
1989         
1990         if (StringUtils::StartsWithNoCase(strItem, strMovieFileNameNoExt))
1991         {
1992           // is this a rar or zip-file
1993           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
1994           {
1995             // zip-file name equals strMovieFileNameNoExt, don't check in zip-file
1996             ScanArchiveForSubtitles( items[j]->GetPath(), "", vecSubtitles );
1997           }
1998           else    // not a rar/zip file
1999           {
2000             for (int i = 0; sub_exts[i]; i++)
2001             {
2002               //Cache subtitle with same name as movie
2003               if (URIUtils::HasExtension(strItem, sub_exts[i]))
2004               {
2005                 vecSubtitles.push_back( items[j]->GetPath() ); 
2006                 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2007               }
2008             }
2009           }
2010         }
2011         else
2012         {
2013           // is this a rar or zip-file
2014           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2015           {
2016             // check strMovieFileNameNoExt in zip-file
2017             ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2018           }
2019         }
2020       }
2021     }
2022   }
2023
2024   iSize = vecSubtitles.size();
2025   for (int i = 0; i < iSize; i++)
2026   {
2027     if (URIUtils::HasExtension(vecSubtitles[i], ".smi"))
2028     {
2029       //Cache multi-language sami subtitle
2030       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2031       if(pStream->Open(vecSubtitles[i]))
2032       {
2033         CDVDSubtitleTagSami TagConv;
2034         TagConv.LoadHead(pStream);
2035         if (TagConv.m_Langclass.size() >= 2)
2036         {
2037           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2038           {
2039             strDest = StringUtils::Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name.c_str(), i);
2040             if (CFile::Cache(vecSubtitles[i], strDest))
2041             {
2042               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2043               vecSubtitles.push_back(strDest);
2044             }
2045           }
2046         }
2047       }
2048       delete pStream;
2049     }
2050   }
2051   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2052 }
2053
2054 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2055 {
2056   int nSubtitlesAdded = 0;
2057   CFileItemList ItemList;
2058  
2059   // zip only gets the root dir
2060   if (URIUtils::HasExtension(strArchivePath, ".zip"))
2061   {
2062    CStdString strZipPath;
2063    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2064    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",DIR_FLAG_NO_FILE_DIRS))
2065     return false;
2066   }
2067   else
2068   {
2069  #ifdef HAS_FILESYSTEM_RAR
2070    // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2071    // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2072    if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2073     return false;
2074  #else
2075    return false;
2076  #endif
2077   }
2078   for (int it= 0 ; it <ItemList.Size();++it)
2079   {
2080    CStdString strPathInRar = ItemList[it]->GetPath();
2081    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2082    
2083    CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2084    // always check any embedded rar archives
2085    // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2086    if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2087    {
2088     CStdString strRarInRar;
2089     if (strExt == ".rar")
2090       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2091     else
2092       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2093     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2094    }
2095    // done checking if this is a rar-in-rar
2096
2097    // check that the found filename matches the movie filename
2098    int fnl = strMovieFileNameNoExt.size();
2099    if (fnl && !StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), strMovieFileNameNoExt))
2100      continue;
2101
2102    int iPos=0;
2103     while (sub_exts[iPos])
2104     {
2105      if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2106      {
2107       CStdString strSourceUrl;
2108       if (URIUtils::HasExtension(strArchivePath, ".rar"))
2109        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2110       else
2111        strSourceUrl = strPathInRar;
2112       
2113        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2114        vecSubtitles.push_back( strSourceUrl );
2115        nSubtitlesAdded++;
2116      }
2117      
2118      iPos++;
2119     }
2120   }
2121
2122   return nSubtitlesAdded;
2123 }
2124
2125 void CUtil::GetExternalStreamDetailsFromFilename(const CStdString& strVideo, const CStdString& strStream, ExternalStreamInfo& info)
2126 {
2127   CStdString videoBaseName = URIUtils::GetFileName(strVideo);
2128   URIUtils::RemoveExtension(videoBaseName);
2129
2130   CStdString toParse = URIUtils::GetFileName(strStream);
2131   URIUtils::RemoveExtension(toParse);
2132
2133   // we check left part - if it's same as video base name - strip it
2134   if (toParse.Left(videoBaseName.length()).Equals(videoBaseName))
2135     toParse = toParse.Mid(videoBaseName.length());
2136
2137   // trim any non-alphanumeric char in the begining
2138   std::string::iterator result = std::find_if(toParse.begin(), toParse.end(), ::isalnum);
2139
2140   std::string name;
2141   if (result != toParse.end()) // if we have anything to parse
2142   {
2143     std::string inputString(result, toParse.end());
2144     std::string delimiters(" .-");
2145     std::vector<std::string> tokens;
2146     StringUtils::Tokenize(inputString, tokens, delimiters);
2147
2148     for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
2149     {
2150       if (info.language.empty())
2151       {
2152         CStdString langTmp(*it);
2153         CStdString langCode;
2154         // try to recognize language
2155         if (g_LangCodeExpander.ConvertToThreeCharCode(langCode, langTmp))
2156         {
2157           info.language = langCode;
2158           continue;
2159         }
2160       }
2161       if (info.flag == 0x1111)
2162       {
2163         std::string  flag_tmp(*it);
2164         StringUtils::ToLower(flag_tmp);
2165         if (!flag_tmp.compare("none"))
2166         {
2167           info.flag = CDemuxStream::FLAG_NONE;
2168           continue;
2169         }
2170         else if (!flag_tmp.compare("default"))
2171         {
2172           info.flag = CDemuxStream::FLAG_DEFAULT;
2173           continue;
2174         }
2175         else if (!flag_tmp.compare("forced"))
2176         {
2177           info.flag = CDemuxStream::FLAG_FORCED;
2178           continue;
2179         }
2180       }
2181       name += " " + (*it);
2182     }
2183   }
2184   name += " ";
2185   name += g_localizeStrings.Get(21602); // External
2186   StringUtils::Trim(name);
2187   info.name = StringUtils::RemoveDuplicatedSpacesAndTabs(name);
2188   if (info.flag == 0x1111)
2189     info.flag = CDemuxStream::FLAG_NONE;
2190
2191   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());
2192 }
2193
2194 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2195  */
2196 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2197 {
2198   if (URIUtils::HasExtension(strIdxPath, ".idx"))
2199   {
2200     CStdString strIdxFile;
2201     CStdString strIdxDirectory;
2202     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2203     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2204     {
2205       CStdString strSubFile;
2206       CStdString strSubDirectory;
2207       URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2208       if (URIUtils::IsInArchive(vecSubtitles[j]))
2209         CURL::Decode(strSubDirectory);
2210       if (URIUtils::HasExtension(strSubFile, ".sub") &&
2211           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2212            strSubDirectory.Mid(6, strSubDirectory.length()-11).Equals(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       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.Mid(6, strSubDirectory.length()-11).Equals(URIUtils::ReplaceExtension(vecSubtitles[j],""))))
2241         return true;
2242     }
2243   }
2244   return false;
2245 }
2246
2247 bool CUtil::CanBindPrivileged()
2248 {
2249 #ifdef TARGET_POSIX
2250
2251   if (geteuid() == 0)
2252     return true; //root user can always bind to privileged ports
2253
2254 #ifdef HAVE_LIBCAP
2255
2256   //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2257   bool canbind = false;
2258   cap_t capabilities = cap_get_proc();
2259   if (capabilities)
2260   {
2261     cap_flag_value_t value;
2262     if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
2263       canbind = value;
2264
2265     cap_free(capabilities);
2266   }
2267
2268   return canbind;
2269
2270 #else //HAVE_LIBCAP
2271
2272   return false;
2273
2274 #endif //HAVE_LIBCAP
2275
2276 #else //TARGET_POSIX
2277
2278   return true;
2279
2280 #endif //TARGET_POSIX
2281 }
2282
2283 bool CUtil::ValidatePort(int port)
2284 {
2285   // check that it's a valid port
2286 #ifdef TARGET_POSIX
2287   if (!CUtil::CanBindPrivileged() && (port < 1024 || port > 65535))
2288     return false;
2289   else
2290 #endif
2291   if (port <= 0 || port > 65535)
2292     return false;
2293
2294   return true;
2295 }
2296
2297 int CUtil::GetRandomNumber()
2298 {
2299 #ifdef TARGET_WINDOWS
2300   unsigned int number;
2301   if (rand_s(&number) == 0)
2302     return (int)number;
2303 #else
2304   return rand_r(&s_randomSeed);
2305 #endif
2306
2307   return rand();
2308 }
2309