[cstdstring] drops Mid(), replacing with substr()
[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.Left(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.Left(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.Left(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 = strFileName.Left(strFileName.size() - strFName.size());
585   URIUtils::RemoveSlashAtEnd(strDescription);
586
587   int iPos = strDescription.ReverseFind("\\");
588   if (iPos < 0)
589     iPos = strDescription.ReverseFind("/");
590   if (iPos >= 0)
591   {
592     CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
593     strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
594   }
595   else if (strDescription.size() <= 0)
596     strDescription = strFName;
597   return true;
598 }
599
600 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
601 {
602   if ( !g_mediaManager.IsDiscInDrive() )
603   {
604     strIcon = "DefaultDVDEmpty.png";
605     return ;
606   }
607
608   if ( URIUtils::IsDVD(strPath) )
609   {
610 #ifdef HAS_DVD_DRIVE
611     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
612     //  xbox DVD
613     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
614     {
615       strIcon = "DefaultXboxDVD.png";
616       return ;
617     }
618 #endif
619     strIcon = "DefaultDVDRom.png";
620     return ;
621   }
622
623   if ( URIUtils::IsISO9660(strPath) )
624   {
625 #ifdef HAS_DVD_DRIVE
626     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
627     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
628     {
629       strIcon = "DefaultVCD.png";
630       return ;
631     }
632 #endif
633     strIcon = "DefaultDVDRom.png";
634     return ;
635   }
636
637   if ( URIUtils::IsCDDA(strPath) )
638   {
639     strIcon = "DefaultCDDA.png";
640     return ;
641   }
642 }
643
644 void CUtil::RemoveTempFiles()
645 {
646   CStdString searchPath = CProfilesManager::Get().GetDatabaseFolder();
647   CFileItemList items;
648   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
649     return;
650
651   for (int i = 0; i < items.Size(); ++i)
652   {
653     if (items[i]->m_bIsFolder)
654       continue;
655     XFILE::CFile::Delete(items[i]->GetPath());
656   }
657 }
658
659 void CUtil::ClearSubtitles()
660 {
661   //delete cached subs
662   CFileItemList items;
663   CDirectory::GetDirectory("special://temp/",items);
664   for( int i=0;i<items.Size();++i)
665   {
666     if (!items[i]->m_bIsFolder)
667     {
668       if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
669       {
670         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
671         CFile::Delete(items[i]->GetPath());
672       }
673     }
674   }
675 }
676
677 void CUtil::ClearTempFonts()
678 {
679   CStdString searchPath = "special://temp/fonts/";
680
681   if (!CFile::Exists(searchPath))
682     return;
683
684   CFileItemList items;
685   CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE);
686
687   for (int i=0; i<items.Size(); ++i)
688   {
689     if (items[i]->m_bIsFolder)
690       continue;
691     CFile::Delete(items[i]->GetPath());
692   }
693 }
694
695 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
696
697 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
698 {
699   int64_t n;
700   n = high;
701   n <<= 32;
702   n += low;
703   return n;
704 }
705
706 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
707 {
708   if (!fn_template.Find("%03d"))
709     return "";
710
711   CStdString searchPath = URIUtils::GetDirectory(fn_template);
712   CStdString mask = URIUtils::GetExtension(fn_template);
713   CStdString name = StringUtils::Format(fn_template.c_str(), 0);
714
715   CFileItemList items;
716   if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
717     return name;
718
719   items.SetFastLookup(true);
720   for (int i = 0; i <= max; i++)
721   {
722     CStdString name = StringUtils::Format(fn_template.c_str(), i);
723     if (!items.Get(name))
724       return name;
725   }
726   return "";
727 }
728
729 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
730 {
731   if (!path_template.Find("%04d"))
732     return "";
733   
734   for (int i = 0; i <= max; i++)
735   {
736     CStdString name = StringUtils::Format(path_template.c_str(), i);
737     if (!CFile::Exists(name))
738       return name;
739   }
740   return "";
741 }
742
743 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
744 {
745   result->st_dev = stat->st_dev;
746   result->st_ino = stat->st_ino;
747   result->st_mode = stat->st_mode;
748   result->st_nlink = stat->st_nlink;
749   result->st_uid = stat->st_uid;
750   result->st_gid = stat->st_gid;
751   result->st_rdev = stat->st_rdev;
752   result->st_size = (int64_t)stat->st_size;
753
754 #ifndef TARGET_POSIX
755   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
756   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
757   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
758 #else
759   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
760   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
761   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
762 #endif
763 }
764
765 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
766 {
767   result->st_dev = stat->st_dev;
768   result->st_ino = stat->st_ino;
769   result->st_mode = stat->st_mode;
770   result->st_nlink = stat->st_nlink;
771   result->st_uid = stat->st_uid;
772   result->st_gid = stat->st_gid;
773   result->st_rdev = stat->st_rdev;
774   result->st_size = stat->st_size;
775 #ifndef TARGET_POSIX
776   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
777   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
778   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
779 #else
780   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
781   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
782   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
783 #endif
784 }
785
786 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
787 {
788   result->st_dev = stat->st_dev;
789   result->st_ino = stat->st_ino;
790   result->st_mode = stat->st_mode;
791   result->st_nlink = stat->st_nlink;
792   result->st_uid = stat->st_uid;
793   result->st_gid = stat->st_gid;
794   result->st_rdev = stat->st_rdev;
795   result->st_size = stat->st_size;
796 #ifndef TARGET_POSIX
797   result->st_atime = stat->st_atime;
798   result->st_mtime = stat->st_mtime;
799   result->st_ctime = stat->st_ctime;
800 #else
801   result->st_atime = stat->_st_atime;
802   result->st_mtime = stat->_st_mtime;
803   result->st_ctime = stat->_st_ctime;
804 #endif
805 }
806
807 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
808 {
809   result->st_dev = stat->st_dev;
810   result->st_ino = stat->st_ino;
811   result->st_mode = stat->st_mode;
812   result->st_nlink = stat->st_nlink;
813   result->st_uid = stat->st_uid;
814   result->st_gid = stat->st_gid;
815   result->st_rdev = stat->st_rdev;
816 #ifndef TARGET_POSIX
817   if (stat->st_size <= LONG_MAX)
818     result->st_size = (_off_t)stat->st_size;
819 #else
820   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
821     result->st_size = stat->st_size;
822 #endif
823   else
824   {
825     result->st_size = 0;
826     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
827   }
828   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
829   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
830   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
831 }
832
833 #ifdef TARGET_WINDOWS
834 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
835 {
836   result->st_dev = stat->st_dev;
837   result->st_ino = stat->st_ino;
838   result->st_mode = stat->st_mode;
839   result->st_nlink = stat->st_nlink;
840   result->st_uid = stat->st_uid;
841   result->st_gid = stat->st_gid;
842   result->st_rdev = stat->st_rdev;
843 #ifndef TARGET_POSIX
844   if (stat->st_size <= LONG_MAX)
845     result->st_size = (_off_t)stat->st_size;
846 #else
847   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
848     result->st_size = stat->st_size;
849 #endif
850   else
851   {
852     result->st_size = 0;
853     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
854   }
855 #ifndef TARGET_POSIX
856   result->st_atime = stat->st_atime;
857   result->st_mtime = stat->st_mtime;
858   result->st_ctime = stat->st_ctime;
859 #else
860   result->st_atime = stat->_st_atime;
861   result->st_mtime = stat->_st_mtime;
862   result->st_ctime = stat->_st_ctime;
863 #endif
864 }
865 #endif
866
867 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
868 {
869   // Function to create all directories at once instead
870   // of calling CreateDirectory for every subdir.
871   // Creates the directory and subdirectories if needed.
872
873   // return true if directory already exist
874   if (CDirectory::Exists(strPath)) return true;
875
876   // we currently only allow HD and smb, nfs and afp paths
877   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
878   {
879     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
880     return false;
881   }
882
883   CStdStringArray dirs = URIUtils::SplitPath(strPath);
884   CStdString dir(dirs.front());
885   URIUtils::AddSlashAtEnd(dir);
886   for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
887   {
888     dir = URIUtils::AddFileToFolder(dir, *it);
889     CDirectory::Create(dir);
890   }
891
892   // was the final destination directory successfully created ?
893   if (!CDirectory::Exists(strPath)) return false;
894   return true;
895 }
896
897 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
898 {
899   CStdString result = strFile;
900
901   result.Replace('/', '_');
902   result.Replace('\\', '_');
903   result.Replace('?', '_');
904
905   if (LegalType == LEGAL_WIN32_COMPAT)
906   {
907     // just filter out some illegal characters on windows
908     result.Replace(':', '_');
909     result.Replace('*', '_');
910     result.Replace('?', '_');
911     result.Replace('\"', '_');
912     result.Replace('<', '_');
913     result.Replace('>', '_');
914     result.Replace('|', '_');
915     StringUtils::TrimRight(result, ". ");
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.substr(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   StringUtils::Trim(function);
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.substr(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.clear();
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.empty()) // 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.substr(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.empty() || parameters.size())
1147     parameters.push_back(parameter);
1148 }
1149
1150 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1151 {
1152   if (strPath1.empty())
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.substr(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.empty() && strSpecial[0] == '$')
1292   {
1293     if (StringUtils::StartsWithNoCase(strSpecial, "$home"))
1294       return URIUtils::AddFileToFolder("special://home/", strSpecial.substr(5));
1295     else if (StringUtils::StartsWithNoCase(strSpecial, "$subtitles"))
1296       return URIUtils::AddFileToFolder("special://subtitles/", strSpecial.substr(10));
1297     else if (StringUtils::StartsWithNoCase(strSpecial, "$userdata"))
1298       return URIUtils::AddFileToFolder("special://userdata/", strSpecial.substr(9));
1299     else if (StringUtils::StartsWithNoCase(strSpecial, "$database"))
1300       return URIUtils::AddFileToFolder("special://database/", strSpecial.substr(9));
1301     else if (StringUtils::StartsWithNoCase(strSpecial, "$thumbnails"))
1302       return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.substr(11));
1303     else if (StringUtils::StartsWithNoCase(strSpecial, "$recordings"))
1304       return URIUtils::AddFileToFolder("special://recordings/", strSpecial.substr(11));
1305     else if (StringUtils::StartsWithNoCase(strSpecial, "$screenshots"))
1306       return URIUtils::AddFileToFolder("special://screenshots/", strSpecial.substr(12));
1307     else if (StringUtils::StartsWithNoCase(strSpecial, "$musicplaylists"))
1308       return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.substr(15));
1309     else if (StringUtils::StartsWithNoCase(strSpecial, "$videoplaylists"))
1310       return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.substr(15));
1311     else if (StringUtils::StartsWithNoCase(strSpecial, "$cdrips"))
1312       return URIUtils::AddFileToFolder("special://cdrips/", strSpecial.substr(7));
1313     // this one will be removed post 2.0
1314     else if (StringUtils::StartsWithNoCase(strSpecial, "$playlists"))
1315       return URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), strSpecial.substr(10));
1316   }
1317   return strSpecial;
1318 }
1319
1320 CStdString CUtil::MusicPlaylistsLocation()
1321 {
1322   vector<CStdString> vec;
1323   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "music"));
1324   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1325   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1326 }
1327
1328 CStdString CUtil::VideoPlaylistsLocation()
1329 {
1330   vector<CStdString> vec;
1331   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "video"));
1332   vec.push_back(URIUtils::AddFileToFolder(CSettings::Get().GetString("system.playlistspath"), "mixed"));
1333   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);
1334 }
1335
1336 void CUtil::DeleteMusicDatabaseDirectoryCache()
1337 {
1338   CUtil::DeleteDirectoryCache("mdb-");
1339   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1340 }
1341
1342 void CUtil::DeleteVideoDatabaseDirectoryCache()
1343 {
1344   CUtil::DeleteDirectoryCache("vdb-");
1345   CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1346 }
1347
1348 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1349 {
1350   CStdString searchPath = "special://temp/";
1351   CFileItemList items;
1352   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", DIR_FLAG_NO_FILE_DIRS))
1353     return;
1354
1355   for (int i = 0; i < items.Size(); ++i)
1356   {
1357     if (items[i]->m_bIsFolder)
1358       continue;
1359     CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
1360     if (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   StringUtils::ToLower(strAlbumTemp);
1413   CStdString strAlbum = strAlbum1;
1414   StringUtils::ToLower(strAlbum);
1415   double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1416   double fArtistPercentage = 0.0f;
1417   if (!strArtist1.empty())
1418   {
1419     CStdString strArtistTemp = strArtistTemp1;
1420     StringUtils::ToLower(strArtistTemp);
1421     CStdString strArtist = strArtist1;
1422     StringUtils::ToLower(strArtist);
1423     fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1424   }
1425   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1426   return fRelevance;
1427 }
1428
1429 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, 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.substr(0, strLabel.size() - 4));
1548       }
1549     }
1550   }
1551   sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1552 }
1553
1554 void CUtil::InitRandomSeed()
1555 {
1556   // Init random seed
1557   int64_t now;
1558   now = CurrentHostCounter();
1559   unsigned int seed = (unsigned int)now;
1560 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1561   srand(seed);
1562 }
1563
1564 #ifdef TARGET_POSIX
1565 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1566 {
1567   CStdStringArray args;
1568
1569   StringUtils::SplitString(cmdLine, ",", args);
1570
1571   // Strip quotes and whitespace around the arguments, or exec will fail.
1572   // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1573   // But it's still limited, for example quotes inside the strings are not expanded, etc.
1574   // TODO: Maybe some python library routine can parse this more properly ?
1575   for (CStdStringArray::iterator it = args.begin(); it != args.end(); ++it)
1576   {
1577     size_t pos;
1578     pos = it->find_first_not_of(" \t\n\"'");
1579     if (pos != std::string::npos)
1580     {
1581       it->erase(0, pos);
1582     }
1583
1584     pos = it->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1585     {
1586       it->erase(++pos, it->size());
1587     }
1588   }
1589
1590   return Command(args, waitExit);
1591 }
1592
1593 //
1594 // FIXME, this should be merged with the function below.
1595 //
1596 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1597 {
1598 #ifdef _DEBUG
1599   printf("Executing: ");
1600   for (size_t i=0; i<arrArgs.size(); i++)
1601     printf("%s ", arrArgs[i].c_str());
1602   printf("\n");
1603 #endif
1604
1605   pid_t child = fork();
1606   int n = 0;
1607   if (child == 0)
1608   {
1609     if (!waitExit)
1610     {
1611       // fork again in order not to leave a zombie process
1612       child = fork();
1613       if (child == -1)
1614         _exit(2);
1615       else if (child != 0)
1616         _exit(0);
1617     }
1618     close(0);
1619     close(1);
1620     close(2);
1621     if (arrArgs.size() > 0)
1622     {
1623       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1624       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1625       for (size_t i=0; i<arrArgs.size(); i++)
1626         args[i] = (char *)arrArgs[i].c_str();
1627       execvp(args[0], args);
1628     }
1629   }
1630   else
1631   {
1632     waitpid(child, &n, 0);
1633   }
1634
1635   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
1636 }
1637
1638 bool CUtil::SudoCommand(const CStdString &strCommand)
1639 {
1640   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
1641   pid_t child = fork();
1642   int n = 0;
1643   if (child == 0)
1644   {
1645     close(0); // close stdin to avoid sudo request password
1646     close(1);
1647     close(2);
1648     CStdStringArray arrArgs;
1649     StringUtils::SplitString(strCommand, " ", arrArgs);
1650     if (arrArgs.size() > 0)
1651     {
1652       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
1653       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
1654       args[0] = (char *)"/usr/bin/sudo";
1655       args[1] = (char *)"-S";
1656       for (size_t i=0; i<arrArgs.size(); i++)
1657       {
1658         args[i+2] = (char *)arrArgs[i].c_str();
1659       }
1660       execvp("/usr/bin/sudo", args);
1661     }
1662   }
1663   else
1664     waitpid(child, &n, 0);
1665
1666   return WEXITSTATUS(n) == 0;
1667 }
1668 #endif
1669
1670 int CUtil::LookupRomanDigit(char roman_digit)
1671 {
1672   switch (roman_digit)
1673   {
1674     case 'i':
1675     case 'I':
1676       return 1;
1677     case 'v':
1678     case 'V':
1679       return 5;
1680     case 'x':
1681     case 'X':
1682       return 10;
1683     case 'l':
1684     case 'L':
1685       return 50;
1686     case 'c':
1687     case 'C':
1688       return 100;
1689     case 'd':
1690     case 'D':
1691       return 500;
1692     case 'm':
1693     case 'M':
1694       return 1000;
1695     default:
1696       return 0;
1697   }
1698 }
1699
1700 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
1701 {
1702   
1703   int decimal = -1;
1704
1705   if (roman_numeral && roman_numeral[0])
1706   {
1707     int temp_sum  = 0,
1708         last      = 0,
1709         repeat    = 0,
1710         trend     = 1;
1711     decimal = 0;
1712     while (*roman_numeral)
1713     {
1714       int digit = CUtil::LookupRomanDigit(*roman_numeral);
1715       int test  = last;
1716       
1717       // General sanity checks
1718
1719       // numeral not in LUT
1720       if (!digit)
1721         return -1;
1722       
1723       while (test > 5)
1724         test /= 10;
1725       
1726       // N = 10^n may not precede (N+1) > 10^(N+1)
1727       if (test == 1 && digit > last * 10)
1728         return -1;
1729       
1730       // N = 5*10^n may not precede (N+1) >= N
1731       if (test == 5 && digit >= last) 
1732         return -1;
1733
1734       // End general sanity checks
1735
1736       if (last < digit)
1737       {
1738         // smaller numerals may not repeat before a larger one
1739         if (repeat) 
1740           return -1;
1741
1742         temp_sum += digit;
1743         
1744         repeat  = 0;
1745         trend   = 0;
1746       }
1747       else if (last == digit)
1748       {
1749         temp_sum += digit;
1750         repeat++;
1751         trend = 1;
1752       }
1753       else
1754       {
1755         if (!repeat)
1756           decimal += 2 * last - temp_sum;
1757         else
1758           decimal += temp_sum;
1759         
1760         temp_sum = digit;
1761
1762         trend   = 1;
1763         repeat  = 0;
1764       }
1765       // Post general sanity checks
1766
1767       // numerals may not repeat more than thrice
1768       if (repeat == 3)
1769         return -1;
1770
1771       last = digit;
1772       roman_numeral++;
1773     }
1774
1775     if (trend)
1776       decimal += temp_sum;
1777     else
1778       decimal += 2 * last - temp_sum;
1779   }
1780   return decimal;
1781 }
1782
1783 CStdString CUtil::ResolveExecutablePath()
1784 {
1785   CStdString strExecutablePath;
1786 #ifdef TARGET_WINDOWS
1787   static const size_t bufSize = MAX_PATH * 2;
1788   wchar_t* buf = new wchar_t[bufSize];
1789   buf[0] = 0;
1790   ::GetModuleFileNameW(0, buf, bufSize);
1791   buf[bufSize-1] = 0;
1792   g_charsetConverter.wToUTF8(buf,strExecutablePath);
1793   delete[] buf;
1794 #elif defined(TARGET_DARWIN)
1795   char     given_path[2*MAXPATHLEN];
1796   uint32_t path_size =2*MAXPATHLEN;
1797
1798   GetDarwinExecutablePath(given_path, &path_size);
1799   strExecutablePath = given_path;
1800 #elif defined(TARGET_FREEBSD)                                                                                                                                                                   
1801   char buf[PATH_MAX];
1802   size_t buflen;
1803   int mib[4];
1804
1805   mib[0] = CTL_KERN;
1806   mib[1] = KERN_PROC;
1807   mib[2] = KERN_PROC_PATHNAME;
1808   mib[3] = getpid();
1809
1810   buflen = sizeof(buf) - 1;
1811   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
1812     strExecutablePath = "";
1813   else
1814     strExecutablePath = buf;
1815 #else
1816   /* Get our PID and build the name of the link in /proc */
1817   pid_t pid = getpid();
1818   char linkname[64]; /* /proc/<pid>/exe */
1819   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
1820
1821   /* Now read the symbolic link */
1822   char buf[PATH_MAX + 1];
1823   buf[0] = 0;
1824
1825   int ret = readlink(linkname, buf, sizeof(buf) - 1);
1826   if (ret != -1)
1827     buf[ret] = 0;
1828
1829   strExecutablePath = buf;
1830 #endif
1831   return strExecutablePath;
1832 }
1833
1834 CStdString CUtil::GetFrameworksPath(bool forPython)
1835 {
1836   CStdString strFrameworksPath;
1837 #if defined(TARGET_DARWIN)
1838   char     given_path[2*MAXPATHLEN];
1839   uint32_t path_size =2*MAXPATHLEN;
1840
1841   GetDarwinFrameworkPath(forPython, given_path, &path_size);
1842   strFrameworksPath = given_path;
1843 #endif
1844   return strFrameworksPath;
1845 }
1846
1847 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
1848 {
1849   unsigned int startTimer = XbmcThreads::SystemClockMillis();
1850   
1851   // new array for commons sub dirs
1852   const char * common_sub_dirs[] = {"subs",
1853     "Subs",
1854     "subtitles",
1855     "Subtitles",
1856     "vobsubs",
1857     "Vobsubs",
1858     "sub",
1859     "Sub",
1860     "vobsub",
1861     "Vobsub",
1862     "subtitle",
1863     "Subtitle",
1864     NULL};
1865   
1866   vector<CStdString> vecExtensionsCached;
1867   
1868   CFileItem item(strMovie, false);
1869   if ( item.IsInternetStream()
1870     || item.IsHDHomeRun()
1871     || item.IsSlingbox()
1872     || item.IsPlayList()
1873     || item.IsLiveTV()
1874     || !item.IsVideo())
1875     return;
1876   
1877   vector<CStdString> strLookInPaths;
1878   
1879   CStdString strMovieFileName;
1880   CStdString strPath;
1881   
1882   URIUtils::Split(strMovie, strPath, strMovieFileName);
1883   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
1884   strLookInPaths.push_back(strPath);
1885   
1886   if (!CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() && !CSettings::Get().GetString("subtitles.custompath").empty()) // to avoid checking non-existent directories (network) every time..
1887   {
1888     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(CSettings::Get().GetString("subtitles.custompath")))
1889     {
1890       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
1891       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1892     }
1893     else if (!CDirectory::Exists(CSettings::Get().GetString("subtitles.custompath")))
1894     {
1895       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
1896       CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1897     }
1898     
1899     CMediaSettings::Get().SetAdditionalSubtitleDirectoryChecked(1);
1900   }
1901   
1902   if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
1903   {
1904     CURL url(strMovie);
1905     CStdString strArchive = url.GetHostName();
1906     URIUtils::Split(strArchive, strPath, strMovieFileName);
1907     strLookInPaths.push_back(strPath);
1908   }
1909   
1910   int iSize = strLookInPaths.size();
1911   for (int i=0; i<iSize; ++i)
1912   {
1913     CStdStringArray directories;
1914     int nTokens = StringUtils::SplitString( strLookInPaths[i], "/", directories );
1915     if (nTokens == 1)
1916       StringUtils::SplitString( strLookInPaths[i], "\\", directories );
1917
1918     // if it's inside a cdX dir, add parent path
1919     if (directories.size() >= 2 && directories[directories.size()-2].size() == 3 && StringUtils::StartsWithNoCase(directories[directories.size()-2], "cd")) // SplitString returns empty token as last item, hence size-2
1920     {
1921       CStdString strPath2;
1922       URIUtils::GetParentPath(strLookInPaths[i], strPath2);
1923       strLookInPaths.push_back(strPath2);
1924     }
1925   }
1926
1927   // checking if any of the common subdirs exist ..
1928   iSize = strLookInPaths.size();
1929   for (int i=0;i<iSize;++i)
1930   {
1931     for (int j=0; common_sub_dirs[j]; j++)
1932     {
1933       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
1934       URIUtils::AddSlashAtEnd(strPath2);
1935       if (CDirectory::Exists(strPath2))
1936         strLookInPaths.push_back(strPath2);
1937     }
1938   }
1939   // .. done checking for common subdirs
1940   
1941   // check if there any cd-directories in the paths we have added so far
1942   iSize = strLookInPaths.size();
1943   for (int i=0;i<9;++i) // 9 cd's
1944   {
1945     CStdString cdDir = StringUtils::Format("cd%i",i+1);
1946     for (int i=0;i<iSize;++i)
1947     {
1948       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
1949       URIUtils::AddSlashAtEnd(strPath2);
1950       bool pathAlreadyAdded = false;
1951       for (unsigned int i=0; i<strLookInPaths.size(); i++)
1952       {
1953         // if movie file is inside cd-dir, this directory can exist in vector already
1954         if (strLookInPaths[i].Equals( strPath2 ) )
1955           pathAlreadyAdded = true;
1956       }
1957       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
1958         strLookInPaths.push_back(strPath2);
1959     }
1960   }
1961   // .. done checking for cd-dirs
1962   
1963   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
1964   if (CMediaSettings::Get().GetAdditionalSubtitleDirectoryChecked() == 1)
1965   {
1966     strPath = CSettings::Get().GetString("subtitles.custompath");
1967     URIUtils::AddSlashAtEnd(strPath);
1968     strLookInPaths.push_back(strPath);
1969   }
1970   
1971   CStdString strLExt;
1972   CStdString strDest;
1973   CStdString strItem;
1974   
1975   // 2 steps for movie directory and alternate subtitles directory
1976   CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
1977   for (unsigned int step = 0; step < strLookInPaths.size(); step++)
1978   {
1979     if (strLookInPaths[step].length() != 0)
1980     {
1981       CFileItemList items;
1982       
1983       CDirectory::GetDirectory(strLookInPaths[step], items, g_advancedSettings.m_subtitlesExtensions, DIR_FLAG_NO_FILE_DIRS);
1984       
1985       for (int j = 0; j < items.Size(); j++)
1986       {
1987         URIUtils::Split(items[j]->GetPath(), strPath, strItem);
1988         
1989         if (StringUtils::StartsWithNoCase(strItem, strMovieFileNameNoExt))
1990         {
1991           // is this a rar or zip-file
1992           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
1993           {
1994             // zip-file name equals strMovieFileNameNoExt, don't check in zip-file
1995             ScanArchiveForSubtitles( items[j]->GetPath(), "", vecSubtitles );
1996           }
1997           else    // not a rar/zip file
1998           {
1999             for (int i = 0; sub_exts[i]; i++)
2000             {
2001               //Cache subtitle with same name as movie
2002               if (URIUtils::HasExtension(strItem, sub_exts[i]))
2003               {
2004                 vecSubtitles.push_back( items[j]->GetPath() ); 
2005                 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2006               }
2007             }
2008           }
2009         }
2010         else
2011         {
2012           // is this a rar or zip-file
2013           if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2014           {
2015             // check strMovieFileNameNoExt in zip-file
2016             ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2017           }
2018         }
2019       }
2020     }
2021   }
2022
2023   iSize = vecSubtitles.size();
2024   for (int i = 0; i < iSize; i++)
2025   {
2026     if (URIUtils::HasExtension(vecSubtitles[i], ".smi"))
2027     {
2028       //Cache multi-language sami subtitle
2029       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2030       if(pStream->Open(vecSubtitles[i]))
2031       {
2032         CDVDSubtitleTagSami TagConv;
2033         TagConv.LoadHead(pStream);
2034         if (TagConv.m_Langclass.size() >= 2)
2035         {
2036           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2037           {
2038             strDest = StringUtils::Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name.c_str(), i);
2039             if (CFile::Cache(vecSubtitles[i], strDest))
2040             {
2041               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2042               vecSubtitles.push_back(strDest);
2043             }
2044           }
2045         }
2046       }
2047       delete pStream;
2048     }
2049   }
2050   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2051 }
2052
2053 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2054 {
2055   int nSubtitlesAdded = 0;
2056   CFileItemList ItemList;
2057  
2058   // zip only gets the root dir
2059   if (URIUtils::HasExtension(strArchivePath, ".zip"))
2060   {
2061    CStdString strZipPath;
2062    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2063    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",DIR_FLAG_NO_FILE_DIRS))
2064     return false;
2065   }
2066   else
2067   {
2068  #ifdef HAS_FILESYSTEM_RAR
2069    // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2070    // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2071    if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2072     return false;
2073  #else
2074    return false;
2075  #endif
2076   }
2077   for (int it= 0 ; it <ItemList.Size();++it)
2078   {
2079    CStdString strPathInRar = ItemList[it]->GetPath();
2080    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2081    
2082    CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2083    // always check any embedded rar archives
2084    // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2085    if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2086    {
2087     CStdString strRarInRar;
2088     if (strExt == ".rar")
2089       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2090     else
2091       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2092     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2093    }
2094    // done checking if this is a rar-in-rar
2095
2096    // check that the found filename matches the movie filename
2097    int fnl = strMovieFileNameNoExt.size();
2098    if (fnl && !StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar), strMovieFileNameNoExt))
2099      continue;
2100
2101    int iPos=0;
2102     while (sub_exts[iPos])
2103     {
2104      if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2105      {
2106       CStdString strSourceUrl;
2107       if (URIUtils::HasExtension(strArchivePath, ".rar"))
2108        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2109       else
2110        strSourceUrl = strPathInRar;
2111       
2112        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2113        vecSubtitles.push_back( strSourceUrl );
2114        nSubtitlesAdded++;
2115      }
2116      
2117      iPos++;
2118     }
2119   }
2120
2121   return nSubtitlesAdded;
2122 }
2123
2124 void CUtil::GetExternalStreamDetailsFromFilename(const CStdString& strVideo, const CStdString& strStream, ExternalStreamInfo& info)
2125 {
2126   CStdString videoBaseName = URIUtils::GetFileName(strVideo);
2127   URIUtils::RemoveExtension(videoBaseName);
2128
2129   CStdString toParse = URIUtils::GetFileName(strStream);
2130   URIUtils::RemoveExtension(toParse);
2131
2132   // we check left part - if it's same as video base name - strip it
2133   if (toParse.Left(videoBaseName.length()).Equals(videoBaseName))
2134     toParse = toParse.substr(videoBaseName.length());
2135
2136   // trim any non-alphanumeric char in the begining
2137   std::string::iterator result = std::find_if(toParse.begin(), toParse.end(), ::isalnum);
2138
2139   std::string name;
2140   if (result != toParse.end()) // if we have anything to parse
2141   {
2142     std::string inputString(result, toParse.end());
2143     std::string delimiters(" .-");
2144     std::vector<std::string> tokens;
2145     StringUtils::Tokenize(inputString, tokens, delimiters);
2146
2147     for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
2148     {
2149       if (info.language.empty())
2150       {
2151         CStdString langTmp(*it);
2152         CStdString langCode;
2153         // try to recognize language
2154         if (g_LangCodeExpander.ConvertToThreeCharCode(langCode, langTmp))
2155         {
2156           info.language = langCode;
2157           continue;
2158         }
2159       }
2160       if (info.flag == 0x1111)
2161       {
2162         std::string  flag_tmp(*it);
2163         StringUtils::ToLower(flag_tmp);
2164         if (!flag_tmp.compare("none"))
2165         {
2166           info.flag = CDemuxStream::FLAG_NONE;
2167           continue;
2168         }
2169         else if (!flag_tmp.compare("default"))
2170         {
2171           info.flag = CDemuxStream::FLAG_DEFAULT;
2172           continue;
2173         }
2174         else if (!flag_tmp.compare("forced"))
2175         {
2176           info.flag = CDemuxStream::FLAG_FORCED;
2177           continue;
2178         }
2179       }
2180       name += " " + (*it);
2181     }
2182   }
2183   name += " ";
2184   name += g_localizeStrings.Get(21602); // External
2185   StringUtils::Trim(name);
2186   info.name = StringUtils::RemoveDuplicatedSpacesAndTabs(name);
2187   if (info.flag == 0x1111)
2188     info.flag = CDemuxStream::FLAG_NONE;
2189
2190   CLog::Log(LOGDEBUG, "%s - Language = '%s' / Name = '%s' / Flag = '%u' from %s", __FUNCTION__, info.language.c_str(), info.name.c_str(), info.flag, strStream.c_str());
2191 }
2192
2193 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2194  */
2195 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2196 {
2197   if (URIUtils::HasExtension(strIdxPath, ".idx"))
2198   {
2199     CStdString strIdxFile;
2200     CStdString strIdxDirectory;
2201     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2202     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2203     {
2204       CStdString strSubFile;
2205       CStdString strSubDirectory;
2206       URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2207       if (URIUtils::IsInArchive(vecSubtitles[j]))
2208         CURL::Decode(strSubDirectory);
2209       if (URIUtils::HasExtension(strSubFile, ".sub") &&
2210           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2211            StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(strIdxPath,""))))
2212       {
2213         strSubPath = vecSubtitles[j];
2214         return true;
2215       }
2216     }
2217   }
2218   return false;
2219 }
2220
2221 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2222  */
2223 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2224 {
2225   if (URIUtils::HasExtension(strSubPath, ".sub"))
2226   {
2227     CStdString strSubFile;
2228     CStdString strSubDirectory;
2229     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2230     if (URIUtils::IsInArchive(strSubPath))
2231       CURL::Decode(strSubDirectory);
2232     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2233     {
2234       CStdString strIdxFile;
2235       CStdString strIdxDirectory;
2236       URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
2237       if (URIUtils::HasExtension(strIdxFile, ".idx") &&
2238           (URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
2239            StringUtils::EqualsNoCase(strSubDirectory.substr(6, strSubDirectory.length()-11), URIUtils::ReplaceExtension(vecSubtitles[j],""))))
2240         return true;
2241     }
2242   }
2243   return false;
2244 }
2245
2246 bool CUtil::CanBindPrivileged()
2247 {
2248 #ifdef TARGET_POSIX
2249
2250   if (geteuid() == 0)
2251     return true; //root user can always bind to privileged ports
2252
2253 #ifdef HAVE_LIBCAP
2254
2255   //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2256   bool canbind = false;
2257   cap_t capabilities = cap_get_proc();
2258   if (capabilities)
2259   {
2260     cap_flag_value_t value;
2261     if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
2262       canbind = value;
2263
2264     cap_free(capabilities);
2265   }
2266
2267   return canbind;
2268
2269 #else //HAVE_LIBCAP
2270
2271   return false;
2272
2273 #endif //HAVE_LIBCAP
2274
2275 #else //TARGET_POSIX
2276
2277   return true;
2278
2279 #endif //TARGET_POSIX
2280 }
2281
2282 bool CUtil::ValidatePort(int port)
2283 {
2284   // check that it's a valid port
2285 #ifdef TARGET_POSIX
2286   if (!CUtil::CanBindPrivileged() && (port < 1024 || port > 65535))
2287     return false;
2288   else
2289 #endif
2290   if (port <= 0 || port > 65535)
2291     return false;
2292
2293   return true;
2294 }
2295
2296 int CUtil::GetRandomNumber()
2297 {
2298 #ifdef TARGET_WINDOWS
2299   unsigned int number;
2300   if (rand_s(&number) == 0)
2301     return (int)number;
2302 #else
2303   return rand_r(&s_randomSeed);
2304 #endif
2305
2306   return rand();
2307 }
2308