[cstdstring] demise left/right and replace with other options
[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   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.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(' ') >= 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   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   strFilename.Replace("\\.\\", "\\");
333   strFilename.Replace("/./", "/");
334
335   // now find any "\\..\\" and remove them via GetParentPath
336   int pos;
337   while ((pos = strFilename.Find("/../")) > 0)
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("\\..\\")) > 0)
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, true);  // 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   int iPos = strDescription.ReverseFind("\\");
588   if (iPos < 0)
589     iPos = strDescription.ReverseFind("/");
590   if (iPos >= 0)
591     strDescription = strDescription.substr(iPos + 1);
592   else if (strDescription.size() <= 0)
593     strDescription = strFName;
594   return true;
595 }
596
597 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
598 {
599   if ( !g_mediaManager.IsDiscInDrive() )
600   {
601     strIcon = "DefaultDVDEmpty.png";
602     return ;
603   }
604
605   if ( URIUtils::IsDVD(strPath) )
606   {
607 #ifdef HAS_DVD_DRIVE
608     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
609     //  xbox DVD
610     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
611     {
612       strIcon = "DefaultXboxDVD.png";
613       return ;
614     }
615 #endif
616     strIcon = "DefaultDVDRom.png";
617     return ;
618   }
619
620   if ( URIUtils::IsISO9660(strPath) )
621   {
622 #ifdef HAS_DVD_DRIVE
623     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
624     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
625     {
626       strIcon = "DefaultVCD.png";
627       return ;
628     }
629 #endif
630     strIcon = "DefaultDVDRom.png";
631     return ;
632   }
633
634   if ( URIUtils::IsCDDA(strPath) )
635   {
636     strIcon = "DefaultCDDA.png";
637     return ;
638   }
639 }
640
641 void CUtil::RemoveTempFiles()
642 {
643   CStdString searchPath = CProfilesManager::Get().GetDatabaseFolder();
644   CFileItemList items;
645   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
646     return;
647
648   for (int i = 0; i < items.Size(); ++i)
649   {
650     if (items[i]->m_bIsFolder)
651       continue;
652     XFILE::CFile::Delete(items[i]->GetPath());
653   }
654 }
655
656 void CUtil::ClearSubtitles()
657 {
658   //delete cached subs
659   CFileItemList items;
660   CDirectory::GetDirectory("special://temp/",items);
661   for( int i=0;i<items.Size();++i)
662   {
663     if (!items[i]->m_bIsFolder)
664     {
665       if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
666       {
667         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
668         CFile::Delete(items[i]->GetPath());
669       }
670     }
671   }
672 }
673
674 void CUtil::ClearTempFonts()
675 {
676   CStdString searchPath = "special://temp/fonts/";
677
678   if (!CFile::Exists(searchPath))
679     return;
680
681   CFileItemList items;
682   CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE);
683
684   for (int i=0; i<items.Size(); ++i)
685   {
686     if (items[i]->m_bIsFolder)
687       continue;
688     CFile::Delete(items[i]->GetPath());
689   }
690 }
691
692 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
693
694 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
695 {
696   int64_t n;
697   n = high;
698   n <<= 32;
699   n += low;
700   return n;
701 }
702
703 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
704 {
705   if (!fn_template.Find("%03d"))
706     return "";
707
708   CStdString searchPath = URIUtils::GetDirectory(fn_template);
709   CStdString mask = URIUtils::GetExtension(fn_template);
710   CStdString name = StringUtils::Format(fn_template.c_str(), 0);
711
712   CFileItemList items;
713   if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
714     return name;
715
716   items.SetFastLookup(true);
717   for (int i = 0; i <= max; i++)
718   {
719     CStdString name = StringUtils::Format(fn_template.c_str(), i);
720     if (!items.Get(name))
721       return name;
722   }
723   return "";
724 }
725
726 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
727 {
728   if (!path_template.Find("%04d"))
729     return "";
730   
731   for (int i = 0; i <= max; i++)
732   {
733     CStdString name = StringUtils::Format(path_template.c_str(), i);
734     if (!CFile::Exists(name))
735       return name;
736   }
737   return "";
738 }
739
740 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
741 {
742   result->st_dev = stat->st_dev;
743   result->st_ino = stat->st_ino;
744   result->st_mode = stat->st_mode;
745   result->st_nlink = stat->st_nlink;
746   result->st_uid = stat->st_uid;
747   result->st_gid = stat->st_gid;
748   result->st_rdev = stat->st_rdev;
749   result->st_size = (int64_t)stat->st_size;
750
751 #ifndef TARGET_POSIX
752   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
753   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
754   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
755 #else
756   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
757   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
758   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
759 #endif
760 }
761
762 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
763 {
764   result->st_dev = stat->st_dev;
765   result->st_ino = stat->st_ino;
766   result->st_mode = stat->st_mode;
767   result->st_nlink = stat->st_nlink;
768   result->st_uid = stat->st_uid;
769   result->st_gid = stat->st_gid;
770   result->st_rdev = stat->st_rdev;
771   result->st_size = stat->st_size;
772 #ifndef TARGET_POSIX
773   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
774   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
775   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
776 #else
777   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
778   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
779   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
780 #endif
781 }
782
783 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
784 {
785   result->st_dev = stat->st_dev;
786   result->st_ino = stat->st_ino;
787   result->st_mode = stat->st_mode;
788   result->st_nlink = stat->st_nlink;
789   result->st_uid = stat->st_uid;
790   result->st_gid = stat->st_gid;
791   result->st_rdev = stat->st_rdev;
792   result->st_size = stat->st_size;
793 #ifndef TARGET_POSIX
794   result->st_atime = stat->st_atime;
795   result->st_mtime = stat->st_mtime;
796   result->st_ctime = stat->st_ctime;
797 #else
798   result->st_atime = stat->_st_atime;
799   result->st_mtime = stat->_st_mtime;
800   result->st_ctime = stat->_st_ctime;
801 #endif
802 }
803
804 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
805 {
806   result->st_dev = stat->st_dev;
807   result->st_ino = stat->st_ino;
808   result->st_mode = stat->st_mode;
809   result->st_nlink = stat->st_nlink;
810   result->st_uid = stat->st_uid;
811   result->st_gid = stat->st_gid;
812   result->st_rdev = stat->st_rdev;
813 #ifndef TARGET_POSIX
814   if (stat->st_size <= LONG_MAX)
815     result->st_size = (_off_t)stat->st_size;
816 #else
817   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
818     result->st_size = stat->st_size;
819 #endif
820   else
821   {
822     result->st_size = 0;
823     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
824   }
825   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
826   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
827   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
828 }
829
830 #ifdef TARGET_WINDOWS
831 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
832 {
833   result->st_dev = stat->st_dev;
834   result->st_ino = stat->st_ino;
835   result->st_mode = stat->st_mode;
836   result->st_nlink = stat->st_nlink;
837   result->st_uid = stat->st_uid;
838   result->st_gid = stat->st_gid;
839   result->st_rdev = stat->st_rdev;
840 #ifndef TARGET_POSIX
841   if (stat->st_size <= LONG_MAX)
842     result->st_size = (_off_t)stat->st_size;
843 #else
844   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
845     result->st_size = stat->st_size;
846 #endif
847   else
848   {
849     result->st_size = 0;
850     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
851   }
852 #ifndef TARGET_POSIX
853   result->st_atime = stat->st_atime;
854   result->st_mtime = stat->st_mtime;
855   result->st_ctime = stat->st_ctime;
856 #else
857   result->st_atime = stat->_st_atime;
858   result->st_mtime = stat->_st_mtime;
859   result->st_ctime = stat->_st_ctime;
860 #endif
861 }
862 #endif
863
864 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
865 {
866   // Function to create all directories at once instead
867   // of calling CreateDirectory for every subdir.
868   // Creates the directory and subdirectories if needed.
869
870   // return true if directory already exist
871   if (CDirectory::Exists(strPath)) return true;
872
873   // we currently only allow HD and smb, nfs and afp paths
874   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
875   {
876     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
877     return false;
878   }
879
880   CStdStringArray dirs = URIUtils::SplitPath(strPath);
881   CStdString dir(dirs.front());
882   URIUtils::AddSlashAtEnd(dir);
883   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
884   {
885     dir = URIUtils::AddFileToFolder(dir, *it);
886     CDirectory::Create(dir);
887   }
888
889   // was the final destination directory successfully created ?
890   if (!CDirectory::Exists(strPath)) return false;
891   return true;
892 }
893
894 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
895 {
896   CStdString result = strFile;
897
898   result.Replace('/', '_');
899   result.Replace('\\', '_');
900   result.Replace('?', '_');
901
902   if (LegalType == LEGAL_WIN32_COMPAT)
903   {
904     // just filter out some illegal characters on windows
905     result.Replace(':', '_');
906     result.Replace('*', '_');
907     result.Replace('?', '_');
908     result.Replace('\"', '_');
909     result.Replace('<', '_');
910     result.Replace('>', '_');
911     result.Replace('|', '_');
912     StringUtils::TrimRight(result, ". ");
913   }
914   return result;
915 }
916
917 // legalize entire path
918 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
919 {
920   if (URIUtils::IsStack(strPathAndFile))
921     return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
922   if (URIUtils::IsMultiPath(strPathAndFile))
923     return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
924   if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile) && !URIUtils::IsAfp(strPathAndFile))
925     return strPathAndFile; // we don't support writing anywhere except HD, SMB, NFS and AFP - no need to legalize path
926
927   bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
928   CStdStringArray dirs = URIUtils::SplitPath(strPathAndFile);
929   // we just add first token to path and don't legalize it - possible values: 
930   // "X:" (local win32), "" (local unix - empty string before '/') or
931   // "protocol://domain"
932   CStdString dir(dirs.front());
933   URIUtils::AddSlashAtEnd(dir);
934   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
935     dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
936   if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
937   return dir;
938 }
939
940 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
941 {
942   CStdString result = path;
943
944   // Don't do any stuff on URLs containing %-characters or protocols that embed
945   // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
946   // recurse and crash XBMC
947   if (URIUtils::IsURL(path) && 
948      (path.Find('%') >= 0 ||
949       StringUtils::StartsWithNoCase(path, "apk:") ||
950       StringUtils::StartsWithNoCase(path, "zip:") ||
951       StringUtils::StartsWithNoCase(path, "rar:") ||
952       StringUtils::StartsWithNoCase(path, "stack:") ||
953       StringUtils::StartsWithNoCase(path, "bluray:") ||
954       StringUtils::StartsWithNoCase(path, "multipath:") ))
955     return result;
956
957   // check the path for incorrect slashes
958 #ifdef TARGET_WINDOWS
959   if (URIUtils::IsDOSPath(path))
960   {
961     result.Replace('/', '\\');
962     /* The double slash correction should only be used when *absolutely*
963        necessary! This applies to certain DLLs or use from Python DLLs/scripts
964        that incorrectly generate double (back) slashes.
965     */
966     if (bFixDoubleSlashes)
967     {
968       // Fixup for double back slashes (but ignore the \\ of unc-paths)
969       for (int x = 1; x < result.GetLength() - 1; x++)
970       {
971         if (result[x] == '\\' && result[x+1] == '\\')
972           result.Delete(x);
973       }
974     }
975   }
976   else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
977 #endif
978   {
979     result.Replace('\\', '/');
980     /* The double slash correction should only be used when *absolutely*
981        necessary! This applies to certain DLLs or use from Python DLLs/scripts
982        that incorrectly generate double (back) slashes.
983     */
984     if (bFixDoubleSlashes)
985     {
986       // Fixup for double forward slashes(/) but don't touch the :// of URLs
987       for (int x = 2; x < result.GetLength() - 1; x++)
988       {
989         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
990           result.Delete(x);
991       }
992     }
993   }
994   return result;
995 }
996
997 bool CUtil::IsUsingTTFSubtitles()
998 {
999   return URIUtils::HasExtension(CSettings::Get().GetString("subtitles.font"), ".ttf");
1000 }
1001
1002 #ifdef UNIT_TESTING
1003 bool CUtil::TestSplitExec()
1004 {
1005   CStdString function;
1006   vector<CStdString> params;
1007   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\")", function, params);
1008   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo")
1009     return false;
1010   params.clear();
1011   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
1012   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
1013     return false;
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("SetProperty(Foo,\"\")", function, params);
1021   if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
1022    return false;
1023   CUtil::SplitExecFunction("SetProperty(foo,ba(\"ba black )\",sheep))", function, params);
1024   if (function != "SetProperty" || params.size() != 2 || params[0] != "foo" || params[1] != "ba(\"ba black )\",sheep)")
1025     return false;
1026   return true;
1027 }
1028 #endif
1029
1030 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
1031 {
1032   CStdString paramString;
1033
1034   int iPos = execString.Find("(");
1035   int iPos2 = execString.ReverseFind(")");
1036   if (iPos > 0 && iPos2 > 0)
1037   {
1038     paramString = execString.substr(iPos + 1, iPos2 - iPos - 1);
1039     function = execString.substr(0, iPos);
1040   }
1041   else
1042     function = execString;
1043
1044   // remove any whitespace, and the standard prefix (if it exists)
1045   StringUtils::Trim(function);
1046   if( StringUtils::StartsWithNoCase(function, "xbmc.") )
1047     function.Delete(0, 5);
1048
1049   SplitParams(paramString, parameters);
1050 }
1051
1052 void CUtil::SplitParams(const CStdString &paramString, std::vector<CStdString> &parameters)
1053 {
1054   bool inQuotes = false;
1055   bool lastEscaped = false; // only every second character can be escaped
1056   int inFunction = 0;
1057   size_t whiteSpacePos = 0;
1058   CStdString parameter;
1059   parameters.clear();
1060   for (size_t pos = 0; pos < paramString.size(); pos++)
1061   {
1062     char ch = paramString[pos];
1063     bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1064     lastEscaped = escaped;
1065     if (inQuotes)
1066     { // if we're in a quote, we accept everything until the closing quote
1067       if (ch == '"' && !escaped)
1068       { // finished a quote - no need to add the end quote to our string
1069         inQuotes = false;
1070       }
1071     }
1072     else
1073     { // not in a quote, so check if we should be starting one
1074       if (ch == '"' && !escaped)
1075       { // start of quote - no need to add the quote to our string
1076         inQuotes = true;
1077       }
1078       if (inFunction && ch == ')')
1079       { // end of a function
1080         inFunction--;
1081       }
1082       if (ch == '(')
1083       { // start of function
1084         inFunction++;
1085       }
1086       if (!inFunction && ch == ',')
1087       { // not in a function, so a comma signfies the end of this parameter
1088         if (whiteSpacePos)
1089           parameter = parameter.substr(0, whiteSpacePos);
1090         // trim off start and end quotes
1091         if (parameter.length() > 1 && parameter[0] == '"' && parameter[parameter.length() - 1] == '"')
1092           parameter = parameter.substr(1, parameter.length() - 2);
1093         else if (parameter.length() > 3 && parameter[parameter.length() - 1] == '"')
1094         {
1095           // check name="value" style param.
1096           int quotaPos = parameter.Find('"');
1097           if (quotaPos > 1 && quotaPos < parameter.GetLength() - 1 && parameter[quotaPos - 1] == '=')
1098           {
1099             parameter.Delete(parameter.GetLength() - 1);
1100             parameter.Delete(quotaPos);
1101           }
1102         }
1103         parameters.push_back(parameter);
1104         parameter.clear();
1105         whiteSpacePos = 0;
1106         continue;
1107       }
1108     }
1109     if ((ch == '"' || ch == '\\') && escaped)
1110     { // escaped quote or backslash
1111       parameter[parameter.size()-1] = ch;
1112       continue;
1113     }
1114     // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1115     if (ch == ' ' && !inQuotes)
1116     {
1117       if (parameter.empty()) // skip whitespace on left
1118         continue;
1119       if (!whiteSpacePos) // make a note of where whitespace starts on the right
1120         whiteSpacePos = parameter.size();
1121     }
1122     else
1123       whiteSpacePos = 0;
1124     parameter += ch;
1125   }
1126   if (inFunction || inQuotes)
1127     CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
1128   if (whiteSpacePos)
1129     parameter.erase(whiteSpacePos);
1130   // trim off start and end quotes
1131   if (parameter.GetLength() > 1 && parameter[0] == '"' && parameter[parameter.GetLength() - 1] == '"')
1132     parameter = parameter.substr(1,parameter.GetLength() - 2);
1133   else if (parameter.GetLength() > 3 && parameter[parameter.GetLength() - 1] == '"')
1134   {
1135     // check name="value" style param.
1136     int quotaPos = parameter.Find('"');
1137     if (quotaPos > 1 && quotaPos < parameter.GetLength() - 1 && parameter[quotaPos - 1] == '=')
1138     {
1139       parameter.Delete(parameter.GetLength() - 1);
1140       parameter.Delete(quotaPos);
1141     }
1142   }
1143   if (!parameter.empty() || parameters.size())
1144     parameters.push_back(parameter);
1145 }
1146
1147 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1148 {
1149   if (strPath1.empty())
1150     return -1;
1151
1152   // copy as we may change strPath
1153   CStdString strPath = strPath1;
1154
1155   // Check for special protocols
1156   CURL checkURL(strPath);
1157
1158   // stack://
1159   if (checkURL.GetProtocol() == "stack")
1160     strPath.Delete(0, 8); // remove the stack protocol
1161
1162   if (checkURL.GetProtocol() == "shout")
1163     strPath = checkURL.GetHostName();
1164   if (checkURL.GetProtocol() == "tuxbox")
1165     return 1;
1166   if (checkURL.GetProtocol() == "plugin")
1167     return 1;
1168   if (checkURL.GetProtocol() == "multipath")
1169     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1170
1171   bIsSourceName = false;
1172   int iIndex = -1;
1173   int iLength = -1;
1174   // we first test the NAME of a source
1175   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1176   {
1177     CMediaSource share = VECSOURCES.at(i);
1178     CStdString strName = share.strName;
1179
1180     // special cases for dvds
1181     if (URIUtils::IsOnDVD(share.strPath))
1182     {
1183       if (URIUtils::IsOnDVD(strPath))
1184         return i;
1185
1186       // not a path, so we need to modify the source name
1187       // since we add the drive status and disc name to the source
1188       // "Name (Drive Status/Disc Name)"
1189       int iPos = strName.ReverseFind('(');
1190       if (iPos > 1)
1191         strName = strName.substr(0, iPos - 1);
1192     }
1193     if (strPath.Equals(strName))
1194     {
1195       bIsSourceName = true;
1196       return i;
1197     }
1198   }
1199
1200   // now test the paths
1201
1202   // remove user details, and ensure path only uses forward slashes
1203   // and ends with a trailing slash so as not to match a substring
1204   CURL urlDest(strPath);
1205   urlDest.SetOptions("");
1206   CStdString strDest = urlDest.GetWithoutUserDetails();
1207   ForceForwardSlashes(strDest);
1208   if (!URIUtils::HasSlashAtEnd(strDest))
1209     strDest += "/";
1210   int iLenPath = strDest.size();
1211
1212   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1213   {
1214     CMediaSource share = VECSOURCES.at(i);
1215
1216     // does it match a source name?
1217     if (share.strPath.substr(0,8) == "shout://")
1218     {
1219       CURL url(share.strPath);
1220       if (strPath.Equals(url.GetHostName()))
1221         return i;
1222     }
1223
1224     // doesnt match a name, so try the source path
1225     vector<CStdString> vecPaths;
1226
1227     // add any concatenated paths if they exist
1228     if (share.vecPaths.size() > 0)
1229       vecPaths = share.vecPaths;
1230
1231     // add the actual share path at the front of the vector
1232     vecPaths.insert(vecPaths.begin(), share.strPath);
1233
1234     // test each path
1235     for (int j = 0; j < (int)vecPaths.size(); ++j)
1236     {
1237       // remove user details, and ensure path only uses forward slashes
1238       // and ends with a trailing slash so as not to match a substring
1239       CURL urlShare(vecPaths[j]);
1240       urlShare.SetOptions("");
1241       CStdString strShare = urlShare.GetWithoutUserDetails();
1242       ForceForwardSlashes(strShare);
1243       if (!URIUtils::HasSlashAtEnd(strShare))
1244         strShare += "/";
1245       int iLenShare = strShare.size();
1246
1247       if ((iLenPath >= iLenShare) && StringUtils::StartsWithNoCase(strDest, strShare) && (iLenShare > iLength))
1248       {
1249         // if exact match, return it immediately
1250         if (iLenPath == iLenShare)
1251         {
1252           // if the path EXACTLY matches an item in a concatentated path
1253           // set source name to true to load the full virtualpath
1254           bIsSourceName = false;
1255           if (vecPaths.size() > 1)
1256             bIsSourceName = true;
1257           return i;
1258         }
1259         iIndex = i;
1260         iLength = iLenShare;
1261       }
1262     }
1263   }
1264
1265   // return the index of the share with the longest match
1266   if (iIndex == -1)
1267   {
1268
1269     // rar:// and zip://
1270     // if archive wasn't mounted, look for a matching share for the archive instead
1271     if( StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://") )
1272     {
1273       // get the hostname portion of the url since it contains the archive file
1274       strPath = checkURL.GetHostName();
1275
1276       bIsSourceName = false;
1277       bool bDummy;
1278       return GetMatchingSource(strPath, VECSOURCES, bDummy);
1279     }
1280
1281     CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource: no matching source found for [%s]", strPath1.c_str());
1282   }
1283   return iIndex;
1284 }
1285
1286 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1287 {
1288   if (!strSpecial.empty() && strSpecial[0] == '$')
1289   {
1290     if (StringUtils::StartsWithNoCase(strSpecial, "$home"))
1291       return URIUtils::AddFileToFolder("special://home/", strSpecial.substr(5));
1292     else if (StringUtils::StartsWithNoCase(strSpecial, "$subtitles"))
1293       return URIUtils::AddFileToFolder("special://subtitles/", strSpecial.substr(10));
1294     else if (StringUtils::StartsWithNoCase(strSpecial, "$userdata"))
1295       return URIUtils::AddFileToFolder("special://userdata/", strSpecial.substr(9));
1296     else if (StringUtils::StartsWithNoCase(strSpecial, "$database"))
1297       return URIUtils::AddFileToFolder("special://database/", strSpecial.substr(9));
1298     else if (StringUtils::StartsWithNoCase(strSpecial, "$thumbnails"))
1299       return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.substr(11));
1300     else if (StringUtils::StartsWithNoCase(strSpecial, "$recordings"))
1301       return URIUtils::AddFileToFolder("special://recordings/", strSpecial.substr(11));
1302     else if (StringUtils::StartsWithNoCase(strSpecial, "$screenshots"))
1303       return URIUtils::AddFileToFolder("special://screenshots/", strSpecial.substr(12));
1304     else if (StringUtils::StartsWithNoCase(strSpecial, "$musicplaylists"))
1305       return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.substr(15));
1306     else if (StringUtils::StartsWithNoCase(strSpecial, "$videoplaylists"))
1307       return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.substr(15));
1308     else if (StringUtils::StartsWithNoCase(strSpecial, "$cdrips"))
1309       return URIUtils::AddFileToFolder("special://cdrips/", strSpecial.substr(7));
1310     // this one will be removed post 2.0
1311     else if (StringUtils::StartsWithNoCase(strSpecial, "$playlists"))
1312       return URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), strSpecial.substr(10));
1313   }
1314   return strSpecial;
1315 }
1316
1317 CStdString CUtil::MusicPlaylistsLocation()
1318 {
1319   vector<CStdString> vec;
1320   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "music"));
1321   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1322   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1323 }
1324
1325 CStdString CUtil::VideoPlaylistsLocation()
1326 {
1327   vector<CStdString> vec;
1328   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "video"));
1329   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1330   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1331 }
1332
1333 void CUtil::DeleteMusicDatabaseDirectoryCache()
1334 {
1335   CUtil::DeleteDirectoryCache("mdb-");
1336   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1337 }
1338
1339 void CUtil::DeleteVideoDatabaseDirectoryCache()
1340 {
1341   CUtil::DeleteDirectoryCache("vdb-");
1342   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1343 }
1344
1345 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1346 {
1347   CStdString searchPath = "special://temp/";
1348   CFileItemList items;
1349   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", DIR_FLAG_NO_FILE_DIRS))
1350     return;
1351
1352   for (int i = 0; i < items.Size(); ++i)
1353   {
1354     if (items[i]->m_bIsFolder)
1355       continue;
1356     CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
1357     if (StringUtils::StartsWith(fileName, prefix))
1358       XFILE::CFile::Delete(items[i]->GetPath());
1359   }
1360 }
1361
1362
1363 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1364 {
1365   CFileItemList myItems;
1366   int flags = DIR_FLAG_DEFAULTS;
1367   if (!bUseFileDirectories)
1368     flags |= DIR_FLAG_NO_FILE_DIRS;
1369   CDirectory::GetDirectory(strPath,myItems,strMask,flags);
1370   for (int i=0;i<myItems.Size();++i)
1371   {
1372     if (myItems[i]->m_bIsFolder)
1373       CUtil::GetRecursiveListing(myItems[i]->GetPath(),items,strMask,bUseFileDirectories);
1374     else
1375       items.Add(myItems[i]);
1376   }
1377 }
1378
1379 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1380 {
1381   CFileItemList myItems;
1382   CDirectory::GetDirectory(strPath,myItems,"",DIR_FLAG_NO_FILE_DIRS);
1383   for (int i=0;i<myItems.Size();++i)
1384   {
1385     if (myItems[i]->m_bIsFolder && !myItems[i]->GetPath().Equals(".."))
1386     {
1387       item.Add(myItems[i]);
1388       CUtil::GetRecursiveDirsListing(myItems[i]->GetPath(),item);
1389     }
1390   }
1391 }
1392
1393 void CUtil::ForceForwardSlashes(CStdString& strPath)
1394 {
1395   int iPos = strPath.ReverseFind('\\');
1396   while (iPos > 0)
1397   {
1398     strPath.at(iPos) = '/';
1399     iPos = strPath.ReverseFind('\\');
1400   }
1401 }
1402
1403 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
1404 {
1405   // case-insensitive fuzzy string comparison on the album and artist for relevance
1406   // weighting is identical, both album and artist are 50% of the total relevance
1407   // a missing artist means the maximum relevance can only be 0.50
1408   CStdString strAlbumTemp = strAlbumTemp1;
1409   StringUtils::ToLower(strAlbumTemp);
1410   CStdString strAlbum = strAlbum1;
1411   StringUtils::ToLower(strAlbum);
1412   double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1413   double fArtistPercentage = 0.0f;
1414   if (!strArtist1.empty())
1415   {
1416     CStdString strArtistTemp = strArtistTemp1;
1417     StringUtils::ToLower(strArtistTemp);
1418     CStdString strArtist = strArtist1;
1419     StringUtils::ToLower(strArtist);
1420     fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1421   }
1422   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1423   return fRelevance;
1424 }
1425
1426 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
1427 {
1428   int iStrInputSize = StrInput.size();
1429   if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
1430     return false;
1431
1432   char cDelim = '\0';
1433   size_t nGreaterDelim, nPos;
1434
1435   nPos = StrInput.find_last_of( '\\' );
1436   if ( nPos != CStdString::npos )
1437     cDelim = '\\';
1438   else
1439   {
1440     nPos = StrInput.find_last_of( '/' );
1441     if ( nPos != CStdString::npos )
1442       cDelim = '/';
1443   }
1444   if ( cDelim == '\0' )
1445     return false;
1446
1447   if (nPos == StrInput.size() - 1)
1448   {
1449     StrInput.erase(StrInput.size() - 1);
1450     nPos = StrInput.find_last_of( cDelim );
1451   }
1452   while( iTextMaxLength < iStrInputSize )
1453   {
1454     nPos = StrInput.find_last_of( cDelim, nPos );
1455     nGreaterDelim = nPos;
1456     if ( nPos != CStdString::npos )
1457       nPos = StrInput.find_last_of( cDelim, nPos - 1 );
1458     if ( nPos == CStdString::npos ) break;
1459     if ( nGreaterDelim > nPos ) StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
1460     iStrInputSize = StrInput.size();
1461   }
1462   // replace any additional /../../ with just /../ if necessary
1463   CStdString replaceDots = StringUtils::Format("..%c..", cDelim);
1464   while (StrInput.size() > (unsigned int)iTextMaxLength)
1465     if (!StrInput.Replace(replaceDots, ".."))
1466       break;
1467   // finally, truncate our string to force inside our max text length,
1468   // replacing the last 2 characters with ".."
1469
1470   // eg end up with:
1471   // "smb://../Playboy Swimsuit Cal.."
1472   if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1473   {
1474     StrInput.erase(iTextMaxLength - 2);
1475     StrInput += "..";
1476   }
1477   StrOutput = StrInput;
1478   return true;
1479 }
1480
1481 bool CUtil::SupportsWriteFileOperations(const CStdString& strPath)
1482 {
1483   // currently only hd, smb, nfs, afp and dav support delete and rename
1484   if (URIUtils::IsHD(strPath))
1485     return true;
1486   if (URIUtils::IsSmb(strPath))
1487     return true;
1488   if (CUtil::IsTVRecording(strPath))
1489     return CPVRDirectory::SupportsWriteFileOperations(strPath);
1490   if (URIUtils::IsNfs(strPath))
1491     return true;
1492   if (URIUtils::IsAfp(strPath))
1493     return true;
1494   if (URIUtils::IsDAV(strPath))
1495     return true;
1496   if (URIUtils::IsMythTV(strPath))
1497   {
1498     /*
1499      * Can't use CFile::Exists() to check whether the myth:// path supports file operations because
1500      * it hits the directory cache on the way through, which has the Live Channels and Guide
1501      * items cached.
1502      */
1503     return CMythDirectory::SupportsWriteFileOperations(strPath);
1504   }
1505   if (URIUtils::IsStack(strPath))
1506     return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
1507   if (URIUtils::IsMultiPath(strPath))
1508     return CMultiPathDirectory::SupportsWriteFileOperations(strPath);
1509
1510   return false;
1511 }
1512
1513 bool CUtil::SupportsReadFileOperations(const CStdString& strPath)
1514 {
1515   if (URIUtils::IsVideoDb(strPath))
1516     return false;
1517
1518   return true;
1519 }
1520
1521 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1522 {
1523   if (g_TextureManager.HasTexture(folderThumb))
1524     return folderThumb;
1525   return "";
1526 }
1527
1528 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
1529 {
1530   CStdString strPath = URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(), "media");
1531   CFileItemList items;
1532   CDirectory::GetDirectory(strPath, items);
1533   // Search for Themes in the Current skin!
1534   for (int i = 0; i < items.Size(); ++i)
1535   {
1536     CFileItemPtr pItem = items[i];
1537     if (!pItem->m_bIsFolder)
1538     {
1539       CStdString strExtension = URIUtils::GetExtension(pItem->GetPath());
1540       if ((strExtension == ".xpr" && pItem->GetLabel().CompareNoCase("Textures.xpr")) ||
1541           (strExtension == ".xbt" && pItem->GetLabel().CompareNoCase("Textures.xbt")))
1542       {
1543         CStdString strLabel = pItem->GetLabel();
1544         vecTheme.push_back(strLabel.substr(0, strLabel.size() - 4));
1545       }
1546     }
1547   }
1548   sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1549 }
1550
1551 void CUtil::InitRandomSeed()
1552 {
1553   // Init random seed
1554   int64_t now;
1555   now = CurrentHostCounter();
1556   unsigned int seed = (unsigned int)now;
1557 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1558   srand(seed);
1559 }
1560
1561 #ifdef TARGET_POSIX
1562 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1563 {
1564   CStdStringArray args;
1565
1566   StringUtils::SplitString(cmdLine, ",", args);
1567
1568   // Strip quotes and whitespace around the arguments, or exec will fail.
1569   // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1570   // But it's still limited, for example quotes inside the strings are not expanded, etc.
1571   // TODO: Maybe some python library routine can parse this more properly ?
1572   for (CStdStringArray::iterator it = args.begin(); it != args.end(); ++it)
1573   {
1574     size_t pos;
1575     pos = it->find_first_not_of(" \t\n\"'");
1576     if (pos != std::string::npos)
1577     {
1578       it->erase(0, pos);
1579     }
1580
1581     pos = it->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1582     {
1583       it->erase(++pos, it->size());
1584     }
1585   }
1586
1587   return Command(args, waitExit);
1588 }
1589
1590 //
1591 // FIXME, this should be merged with the function below.
1592 //
1593 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1594 {
1595 #ifdef _DEBUG
1596   printf("Executing: ");
1597   for (size_t i=0; i<arrArgs.size(); i++)
1598     printf("%s ", arrArgs[i].c_str());
1599   printf("\n");
1600 #endif
1601
1602   pid_t child = fork();
1603   int n = 0;
1604   if (child == 0)
1605   {
1606     if (!waitExit)
1607     {
1608       // fork again in order not to leave a zombie process
1609       child = fork();
1610       if (child == -1)
1611         _exit(2);
1612       else if (child != 0)
1613         _exit(0);
1614     }
1615     close(0);
1616     close(1);
1617     close(2);
1618     if (arrArgs.size() > 0)
1619     {
1620       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1621       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1622       for (size_t i=0; i<arrArgs.size(); i++)
1623         args[i] = (char *)arrArgs[i].c_str();
1624       execvp(args[0], args);
1625     }
1626   }
1627   else
1628   {
1629     waitpid(child, &n, 0);
1630   }
1631
1632   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
1633 }
1634
1635 bool CUtil::SudoCommand(const CStdString &strCommand)
1636 {
1637   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
1638   pid_t child = fork();
1639   int n = 0;
1640   if (child == 0)
1641   {
1642     close(0); // close stdin to avoid sudo request password
1643     close(1);
1644     close(2);
1645     CStdStringArray arrArgs;
1646     StringUtils::SplitString(strCommand, " ", arrArgs);
1647     if (arrArgs.size() > 0)
1648     {
1649       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1650       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1651       args[0] = (char *)"/usr/bin/sudo";
1652       args[1] = (char *)"-S";
1653       for (size_t i=0; i<arrArgs.size(); i++)
1654       {
1655         args[i+2] = (char *)arrArgs[i].c_str();
1656       }
1657       execvp("/usr/bin/sudo", args);
1658     }
1659   }
1660   else
1661     waitpid(child, &n, 0);
1662
1663   return WEXITSTATUS(n) == 0;
1664 }
1665 #endif
1666
1667 int CUtil::LookupRomanDigit(char roman_digit)
1668 {
1669   switch (roman_digit)
1670   {
1671     case 'i':
1672     case 'I':
1673       return 1;
1674     case 'v':
1675     case 'V':
1676       return 5;
1677     case 'x':
1678     case 'X':
1679       return 10;
1680     case 'l':
1681     case 'L':
1682       return 50;
1683     case 'c':
1684     case 'C':
1685       return 100;
1686     case 'd':
1687     case 'D':
1688       return 500;
1689     case 'm':
1690     case 'M':
1691       return 1000;
1692     default:
1693       return 0;
1694   }
1695 }
1696
1697 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
1698 {
1699   
1700   int decimal = -1;
1701
1702   if (roman_numeral && roman_numeral[0])
1703   {
1704     int temp_sum  = 0,
1705         last      = 0,
1706         repeat    = 0,
1707         trend     = 1;
1708     decimal = 0;
1709     while (*roman_numeral)
1710     {
1711       int digit = CUtil::LookupRomanDigit(*roman_numeral);
1712       int test  = last;
1713       
1714       // General sanity checks
1715
1716       // numeral not in LUT
1717       if (!digit)
1718         return -1;
1719       
1720       while (test > 5)
1721         test /= 10;
1722       
1723       // N = 10^n may not precede (N+1) > 10^(N+1)
1724       if (test == 1 && digit > last * 10)
1725         return -1;
1726       
1727       // N = 5*10^n may not precede (N+1) >= N
1728       if (test == 5 && digit >= last) 
1729         return -1;
1730
1731       // End general sanity checks
1732
1733       if (last < digit)
1734       {
1735         // smaller numerals may not repeat before a larger one
1736         if (repeat) 
1737           return -1;
1738
1739         temp_sum += digit;
1740         
1741         repeat  = 0;
1742         trend   = 0;
1743       }
1744       else if (last == digit)
1745       {
1746         temp_sum += digit;
1747         repeat++;
1748         trend = 1;
1749       }
1750       else
1751       {
1752         if (!repeat)
1753           decimal += 2 * last - temp_sum;
1754         else
1755           decimal += temp_sum;
1756         
1757         temp_sum = digit;
1758
1759         trend   = 1;
1760         repeat  = 0;
1761       }
1762       // Post general sanity checks
1763
1764       // numerals may not repeat more than thrice
1765       if (repeat == 3)
1766         return -1;
1767
1768       last = digit;
1769       roman_numeral++;
1770     }
1771
1772     if (trend)
1773       decimal += temp_sum;
1774     else
1775       decimal += 2 * last - temp_sum;
1776   }
1777   return decimal;
1778 }
1779
1780 CStdString CUtil::ResolveExecutablePath()
1781 {
1782   CStdString strExecutablePath;
1783 #ifdef TARGET_WINDOWS
1784   static const size_t bufSize = MAX_PATH * 2;
1785   wchar_t* buf = new wchar_t[bufSize];
1786   buf[0] = 0;
1787   ::GetModuleFileNameW(0, buf, bufSize);
1788   buf[bufSize-1] = 0;
1789   g_charsetConverter.wToUTF8(buf,strExecutablePath);
1790   delete[] buf;
1791 #elif defined(TARGET_DARWIN)
1792   char     given_path[2*MAXPATHLEN];
1793   uint32_t path_size =2*MAXPATHLEN;
1794
1795   GetDarwinExecutablePath(given_path, &path_size);
1796   strExecutablePath = given_path;
1797 #elif defined(TARGET_FREEBSD)                                                                                                                                                                   
1798   char buf[PATH_MAX];
1799   size_t buflen;
1800   int mib[4];
1801
1802   mib[0] = CTL_KERN;
1803   mib[1] = KERN_PROC;
1804   mib[2] = KERN_PROC_PATHNAME;
1805   mib[3] = getpid();
1806
1807   buflen = sizeof(buf) - 1;
1808   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
1809     strExecutablePath = "";
1810   else
1811     strExecutablePath = buf;
1812 #else
1813   /* Get our PID and build the name of the link in /proc */
1814   pid_t pid = getpid();
1815   char linkname[64]; /* /proc/<pid>/exe */
1816   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
1817
1818   /* Now read the symbolic link */
1819   char buf[PATH_MAX + 1];
1820   buf[0] = 0;
1821
1822   int ret = readlink(linkname, buf, sizeof(buf) - 1);
1823   if (ret != -1)
1824     buf[ret] = 0;
1825
1826   strExecutablePath = buf;
1827 #endif
1828   return strExecutablePath;
1829 }
1830
1831 CStdString CUtil::GetFrameworksPath(bool forPython)
1832 {
1833   CStdString strFrameworksPath;
1834 #if defined(TARGET_DARWIN)
1835   char     given_path[2*MAXPATHLEN];
1836   uint32_t path_size =2*MAXPATHLEN;
1837
1838   GetDarwinFrameworkPath(forPython, given_path, &path_size);
1839   strFrameworksPath = given_path;
1840 #endif
1841   return strFrameworksPath;
1842 }
1843
1844 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
1845 {
1846   unsigned int startTimer = XbmcThreads::SystemClockMillis();
1847   
1848   // new array for commons sub dirs
1849   const char * common_sub_dirs[] = {"subs",
1850     "Subs",
1851     "subtitles",
1852     "Subtitles",
1853     "vobsubs",
1854     "Vobsubs",
1855     "sub",
1856     "Sub",
1857     "vobsub",
1858     "Vobsub",
1859     "subtitle",
1860     "Subtitle",
1861     NULL};
1862   
1863   vector<CStdString> vecExtensionsCached;
1864   
1865   CFileItem item(strMovie, false);
1866   if ( item.IsInternetStream()
1867     || item.IsHDHomeRun()
1868     || item.IsSlingbox()
1869     || item.IsPlayList()
1870     || item.IsLiveTV()
1871     || !item.IsVideo())
1872     return;
1873   
1874   vector<CStdString> strLookInPaths;
1875   
1876   CStdString strMovieFileName;
1877   CStdString strPath;
1878   
1879   URIUtils::Split(strMovie, strPath, strMovieFileName);
1880   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
1881   strLookInPaths.push_back(strPath);
1882   
1883   if (!CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() && !CSettings::Get().GetString("subtitles.custompath").empty()) // to avoid checking non-existent directories (network) every time..
1884   {
1885     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(CSettings::Get().GetString("subtitles.custompath")))
1886     {
1887       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
1888       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1889     }
1890     else if (!CDirectory::Exists(CSettings::Get().GetString("subtitles.custompath")))
1891     {
1892       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
1893       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1894     }
1895     
1896     CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(1);
1897   }
1898   
1899   if (StringUtils::StartsWith(strMovie, "rar://")) // <--- if this is found in main path then ignore it!
1900   {
1901     CURL url(strMovie);
1902     CStdString strArchive = url.GetHostName();
1903     URIUtils::Split(strArchive, strPath, strMovieFileName);
1904     strLookInPaths.push_back(strPath);
1905   }
1906   
1907   int iSize = strLookInPaths.size();
1908   for (int i=0; i<iSize; ++i)
1909   {
1910     CStdStringArray directories;
1911     int nTokens = StringUtils::SplitString( strLookInPaths[i], "/", directories );
1912     if (nTokens == 1)
1913       StringUtils::SplitString( strLookInPaths[i], "\\", directories );
1914
1915     // if it's inside a cdX dir, add parent path
1916     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
1917     {
1918       CStdString strPath2;
1919       URIUtils::GetParentPath(strLookInPaths[i], strPath2);
1920       strLookInPaths.push_back(strPath2);
1921     }
1922   }
1923
1924   // checking if any of the common subdirs exist ..
1925   iSize = strLookInPaths.size();
1926   for (int i=0;i<iSize;++i)
1927   {
1928     for (int j=0; common_sub_dirs[j]; j++)
1929     {
1930       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
1931       URIUtils::AddSlashAtEnd(strPath2);
1932       if (CDirectory::Exists(strPath2))
1933         strLookInPaths.push_back(strPath2);
1934     }
1935   }
1936   // .. done checking for common subdirs
1937   
1938   // check if there any cd-directories in the paths we have added so far
1939   iSize = strLookInPaths.size();
1940   for (int i=0;i<9;++i) // 9 cd's
1941   {
1942     CStdString cdDir = StringUtils::Format("cd%i",i+1);
1943     for (int i=0;i<iSize;++i)
1944     {
1945       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
1946       URIUtils::AddSlashAtEnd(strPath2);
1947       bool pathAlreadyAdded = false;
1948       for (unsigned int i=0; i<strLookInPaths.size(); i++)
1949       {
1950         // if movie file is inside cd-dir, this directory can exist in vector already
1951         if (strLookInPaths[i].Equals( strPath2 ) )
1952           pathAlreadyAdded = true;
1953       }
1954       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
1955         strLookInPaths.push_back(strPath2);
1956     }
1957   }
1958   // .. done checking for cd-dirs
1959   
1960   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
1961   if (CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() == 1)
1962   {
1963     strPath = CSettings::Get().GetString("subtitles.custompath");
1964     URIUtils::AddSlashAtEnd(strPath);
1965     strLookInPaths.push_back(strPath);
1966   }
1967   
1968   CStdString strLExt;
1969   CStdString strDest;
1970   CStdString strItem;
1971   
1972   // 2 steps for movie directory and alternate subtitles directory
1973   CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
1974   for (unsigned int step = 0; step < strLookInPaths.size(); step++)
1975   {
1976     if (strLookInPaths[step].length() != 0)
1977     {
1978       CFileItemList items;
1979       
1980       CDirectory::GetDirectory(strLookInPaths[step], items, g_advancedSettings.m_subtitlesExtensions, DIR_FLAG_NO_FILE_DIRS);
1981       
1982       for (int j = 0; j < items.Size(); j++)
1983       {
1984         URIUtils::Split(items[j]->GetPath(), strPath, strItem);
1985         
1986         if (StringUtils::StartsWithNoCase(strItem, strMovieFileNameNoExt))
1987         {
1988           // is this a rar or zip-file
1989           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
1990           {
1991             // zip-file name equals strMovieFileNameNoExt, don't check in zip-file
1992             ScanArchiveForSubtitles( items[j]->GetPath(), "", vecSubtitles );
1993           }
1994           else    // not a rar/zip file
1995           {
1996             for (int i = 0; sub_exts[i]; i++)
1997             {
1998               //Cache subtitle with same name as movie
1999               if (URIUtils::HasExtension(strItem, sub_exts[i]))
2000               {
2001                 vecSubtitles.push_back( items[j]->GetPath() ); 
2002                 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2003               }
2004             }
2005           }
2006         }
2007         else
2008         {
2009           // is this a rar or zip-file
2010           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2011           {
2012             // check strMovieFileNameNoExt in zip-file
2013             ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2014           }
2015         }
2016       }
2017     }
2018   }
2019
2020   iSize = vecSubtitles.size();
2021   for (int i = 0; i < iSize; i++)
2022   {
2023     if (URIUtils::HasExtension(vecSubtitles[i], ".smi"))
2024     {
2025       //Cache multi-language sami subtitle
2026       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2027       if(pStream->Open(vecSubtitles[i]))
2028       {
2029         CDVDSubtitleTagSami TagConv;
2030         TagConv.LoadHead(pStream);
2031         if (TagConv.m_Langclass.size() >= 2)
2032         {
2033           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2034           {
2035             strDest = StringUtils::Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name.c_str(), i);
2036             if (CFile::Cache(vecSubtitles[i], strDest))
2037             {
2038               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2039               vecSubtitles.push_back(strDest);
2040             }
2041           }
2042         }
2043       }
2044       delete pStream;
2045     }
2046   }
2047   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2048 }
2049
2050 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2051 {
2052   int nSubtitlesAdded = 0;
2053   CFileItemList ItemList;
2054  
2055   // zip only gets the root dir
2056   if (URIUtils::HasExtension(strArchivePath, ".zip"))
2057   {
2058    CStdString strZipPath;
2059    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2060    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",DIR_FLAG_NO_FILE_DIRS))
2061     return false;
2062   }
2063   else
2064   {
2065  #ifdef HAS_FILESYSTEM_RAR
2066    // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2067    // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2068    if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2069     return false;
2070  #else
2071    return false;
2072  #endif
2073   }
2074   for (int it= 0 ; it <ItemList.Size();++it)
2075   {
2076    CStdString strPathInRar = ItemList[it]->GetPath();
2077    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2078    
2079    CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2080    // always check any embedded rar archives
2081    // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2082    if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2083    {
2084     CStdString strRarInRar;
2085     if (strExt == ".rar")
2086       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2087     else
2088       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2089     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2090    }
2091    // done checking if this is a rar-in-rar
2092
2093    // check that the found filename matches the movie filename
2094    int fnl = strMovieFileNameNoExt.size();
2095    if (fnl && !StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), strMovieFileNameNoExt))
2096      continue;
2097
2098    int iPos=0;
2099     while (sub_exts[iPos])
2100     {
2101      if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2102      {
2103       CStdString strSourceUrl;
2104       if (URIUtils::HasExtension(strArchivePath, ".rar"))
2105        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2106       else
2107        strSourceUrl = strPathInRar;
2108       
2109        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2110        vecSubtitles.push_back( strSourceUrl );
2111        nSubtitlesAdded++;
2112      }
2113      
2114      iPos++;
2115     }
2116   }
2117
2118   return nSubtitlesAdded;
2119 }
2120
2121 void CUtil::GetExternalStreamDetailsFromFilename(const CStdString& strVideo, const CStdString& strStream, ExternalStreamInfo& info)
2122 {
2123   CStdString videoBaseName = URIUtils::GetFileName(strVideo);
2124   URIUtils::RemoveExtension(videoBaseName);
2125
2126   CStdString toParse = URIUtils::GetFileName(strStream);
2127   URIUtils::RemoveExtension(toParse);
2128
2129   // we check left part - if it's same as video base name - strip it
2130   if (StringUtils::StartsWithNoCase(toParse, videoBaseName))
2131     toParse = toParse.substr(videoBaseName.length());
2132
2133   // trim any non-alphanumeric char in the begining
2134   std::string::iterator result = std::find_if(toParse.begin(), toParse.end(), ::isalnum);
2135
2136   std::string name;
2137   if (result != toParse.end()) // if we have anything to parse
2138   {
2139     std::string inputString(result, toParse.end());
2140     std::string delimiters(" .-");
2141     std::vector<std::string> tokens;
2142     StringUtils::Tokenize(inputString, tokens, delimiters);
2143
2144     for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
2145     {
2146       if (info.language.empty())
2147       {
2148         CStdString langTmp(*it);
2149         CStdString langCode;
2150         // try to recognize language
2151         if (g_LangCodeExpander.ConvertToThreeCharCode(langCode, langTmp))
2152         {
2153           info.language = langCode;
2154           continue;
2155         }
2156       }
2157       if (info.flag == 0x1111)
2158       {
2159         std::string  flag_tmp(*it);
2160         StringUtils::ToLower(flag_tmp);
2161         if (!flag_tmp.compare("none"))
2162         {
2163           info.flag = CDemuxStream::FLAG_NONE;
2164           continue;
2165         }
2166         else if (!flag_tmp.compare("default"))
2167         {
2168           info.flag = CDemuxStream::FLAG_DEFAULT;
2169           continue;
2170         }
2171         else if (!flag_tmp.compare("forced"))
2172         {
2173           info.flag = CDemuxStream::FLAG_FORCED;
2174           continue;
2175         }
2176       }
2177       name += " " + (*it);
2178     }
2179   }
2180   name += " ";
2181   name += g_localizeStrings.Get(21602); // External
2182   StringUtils::Trim(name);
2183   info.name = StringUtils::RemoveDuplicatedSpacesAndTabs(name);
2184   if (info.flag == 0x1111)
2185     info.flag = CDemuxStream::FLAG_NONE;
2186
2187   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());
2188 }
2189
2190 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2191  */
2192 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2193 {
2194   if (URIUtils::HasExtension(strIdxPath, ".idx"))
2195   {
2196     CStdString strIdxFile;
2197     CStdString strIdxDirectory;
2198     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2199     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2200     {
2201       CStdString strSubFile;
2202       CStdString strSubDirectory;
2203       URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2204       if (URIUtils::IsInArchive(vecSubtitles[j]))
2205         CURL::Decode(strSubDirectory);
2206       if (URIUtils::HasExtension(strSubFile, ".sub") &&
2207           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2208            StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(strIdxPath,""))))
2209       {
2210         strSubPath = vecSubtitles[j];
2211         return true;
2212       }
2213     }
2214   }
2215   return false;
2216 }
2217
2218 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2219  */
2220 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2221 {
2222   if (URIUtils::HasExtension(strSubPath, ".sub"))
2223   {
2224     CStdString strSubFile;
2225     CStdString strSubDirectory;
2226     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2227     if (URIUtils::IsInArchive(strSubPath))
2228       CURL::Decode(strSubDirectory);
2229     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2230     {
2231       CStdString strIdxFile;
2232       CStdString strIdxDirectory;
2233       URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
2234       if (URIUtils::HasExtension(strIdxFile, ".idx") &&
2235           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2236            StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(vecSubtitles[j],""))))
2237         return true;
2238     }
2239   }
2240   return false;
2241 }
2242
2243 bool CUtil::CanBindPrivileged()
2244 {
2245 #ifdef TARGET_POSIX
2246
2247   if (geteuid() == 0)
2248     return true; //root user can always bind to privileged ports
2249
2250 #ifdef HAVE_LIBCAP
2251
2252   //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2253   bool canbind = false;
2254   cap_t capabilities = cap_get_proc();
2255   if (capabilities)
2256   {
2257     cap_flag_value_t value;
2258     if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
2259       canbind = value;
2260
2261     cap_free(capabilities);
2262   }
2263
2264   return canbind;
2265
2266 #else //HAVE_LIBCAP
2267
2268   return false;
2269
2270 #endif //HAVE_LIBCAP
2271
2272 #else //TARGET_POSIX
2273
2274   return true;
2275
2276 #endif //TARGET_POSIX
2277 }
2278
2279 bool CUtil::ValidatePort(int port)
2280 {
2281   // check that it's a valid port
2282 #ifdef TARGET_POSIX
2283   if (!CUtil::CanBindPrivileged() && (port < 1024 || port > 65535))
2284     return false;
2285   else
2286 #endif
2287   if (port <= 0 || port > 65535)
2288     return false;
2289
2290   return true;
2291 }
2292
2293 int CUtil::GetRandomNumber()
2294 {
2295 #ifdef TARGET_WINDOWS
2296   unsigned int number;
2297   if (rand_s(&number) == 0)
2298     return (int)number;
2299 #else
2300   return rand_r(&s_randomSeed);
2301 #endif
2302
2303   return rand();
2304 }
2305