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