2 * Copyright (C) 2005-2010 Team XBMC
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)
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.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include "Application.h"
25 #include "filesystem/MultiPathDirectory.h"
26 #include "filesystem/MythDirectory.h"
27 #include "filesystem/SpecialProtocol.h"
28 #include "filesystem/StackDirectory.h"
29 #include "network/DNSNameCache.h"
30 #include "settings/Settings.h"
31 #include "settings/AdvancedSettings.h"
33 #include "StringUtils.h"
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
39 using namespace XFILE;
41 CStdString URIUtils::GetParentFolderURI(const CStdString& uri, bool preserveFileNameInPath)
43 if (preserveFileNameInPath)
44 return AddFileToFolder(GetParentPath(uri), GetFileName(uri));
46 return GetParentPath(uri);
49 bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
51 CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
52 CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
53 return (strncmp(uriPath.c_str(), basePath.c_str(), basePath.GetLength()) == 0);
56 /* returns filename extension including period of filename */
57 const CStdString URIUtils::GetExtension(const CStdString& strFileName)
59 if(IsURL(strFileName))
61 CURL url(strFileName);
62 return GetExtension(url.GetFileName());
65 int period = strFileName.find_last_of('.');
68 if( strFileName.find_first_of('/', period+1) != string::npos ) return "";
69 if( strFileName.find_first_of('\\', period+1) != string::npos ) return "";
70 return strFileName.substr(period);
76 void URIUtils::GetExtension(const CStdString& strFile, CStdString& strExtension)
78 strExtension = GetExtension(strFile);
81 void URIUtils::RemoveExtension(CStdString& strFileName)
83 if(IsURL(strFileName))
85 CURL url(strFileName);
86 strFileName = url.GetFileName();
87 RemoveExtension(strFileName);
88 url.SetFileName(strFileName);
89 strFileName = url.Get();
93 int iPos = strFileName.ReverseFind(".");
97 CStdString strExtension;
98 GetExtension(strFileName, strExtension);
99 strExtension.ToLower();
102 CStdString strFileMask;
103 strFileMask = g_settings.m_pictureExtensions;
104 strFileMask += "|" + g_settings.m_musicExtensions;
105 strFileMask += "|" + g_settings.m_videoExtensions;
106 #if defined(__APPLE__)
107 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
109 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
113 if (strFileMask.Find(strExtension) >= 0)
114 strFileName = strFileName.Left(iPos);
118 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
119 const CStdString& strNewExtension)
124 url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
128 CStdString strChangedFile;
129 CStdString strExtension;
130 GetExtension(strFile, strExtension);
131 if ( strExtension.size() )
133 strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
134 strChangedFile += strNewExtension;
138 strChangedFile = strFile;
139 strChangedFile += strNewExtension;
141 return strChangedFile;
144 /* returns a filename given an url */
145 /* handles both / and \, and options in urls*/
146 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
148 if(IsURL(strFileNameAndPath))
150 CURL url(strFileNameAndPath);
151 return GetFileName(url.GetFileName());
154 /* find any slashes */
155 const int slash1 = strFileNameAndPath.find_last_of('/');
156 const int slash2 = strFileNameAndPath.find_last_of('\\');
158 /* select the last one */
165 return strFileNameAndPath.substr(slash+1);
168 void URIUtils::Split(const CStdString& strFileNameAndPath,
169 CStdString& strPath, CStdString& strFileName)
171 //Splits a full filename in path and file.
172 //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
173 //Trailing slash will be preserved
176 int i = strFileNameAndPath.size() - 1;
179 char ch = strFileNameAndPath[i];
180 // Only break on ':' if it's a drive separator for DOS (ie d:foo)
181 if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
187 strPath = strFileNameAndPath.Left(i + 1);
188 strFileName = strFileNameAndPath.Right(strFileNameAndPath.size() - i - 1);
191 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
195 // silly CStdString can't take a char in the constructor
196 CStdString sep(1, url.GetDirectorySeparator());
198 // split the filename portion of the URL up into separate dirs
199 CStdStringArray dirs;
200 StringUtils::SplitString(url.GetFileName(), sep, dirs);
202 // we start with the root path
203 CStdString dir = url.GetWithoutFilename();
206 dirs.insert(dirs.begin(), dir);
208 // we don't need empty token on the end
209 if (dirs.size() > 1 && dirs.back().IsEmpty())
210 dirs.erase(dirs.end() - 1);
215 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
217 // find the common path of parent and path
219 while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
221 strParent = strParent.Left(j - 1);
222 // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
223 if (!HasSlashAtEnd(strParent))
225 // currently GetDirectory() removes trailing slashes
226 GetDirectory(strParent.Mid(0), strParent);
227 AddSlashAtEnd(strParent);
231 CStdString URIUtils::GetParentPath(const CStdString& strPath)
233 CStdString strReturn;
234 GetParentPath(strPath, strReturn);
238 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
243 CStdString strFile = url.GetFileName();
244 if ( ((url.GetProtocol() == "rar") || (url.GetProtocol() == "zip")) && strFile.IsEmpty())
246 strFile = url.GetHostName();
247 return GetParentPath(strFile, strParent);
249 else if (url.GetProtocol() == "stack")
253 dir.GetDirectory(strPath,items);
254 GetDirectory(items[0]->GetPath(),items[0]->m_strDVDLabel);
255 if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://"))
256 GetParentPath(items[0]->m_strDVDLabel, strParent);
258 strParent = items[0]->m_strDVDLabel;
259 for( int i=1;i<items.Size();++i)
261 GetDirectory(items[i]->GetPath(),items[i]->m_strDVDLabel);
262 if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://"))
263 items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
265 items[i]->SetPath(items[i]->m_strDVDLabel);
267 GetCommonPath(strParent,items[i]->GetPath());
271 else if (url.GetProtocol() == "multipath")
273 // get the parent path of the first item
274 return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
276 else if (url.GetProtocol() == "plugin")
278 if (!url.GetOptions().IsEmpty())
281 strParent = url.Get();
284 if (!url.GetFileName().IsEmpty())
287 strParent = url.Get();
290 if (!url.GetHostName().IsEmpty())
293 strParent = url.Get();
296 return true; // already at root
298 else if (url.GetProtocol() == "special")
300 if (HasSlashAtEnd(strFile) )
301 strFile = strFile.Left(strFile.size() - 1);
302 if(strFile.ReverseFind('/') < 0)
305 else if (strFile.size() == 0)
307 if (url.GetHostName().size() > 0)
309 // we have an share with only server or workgroup name
310 // set hostname to "" and return true to get back to root
312 strParent = url.Get();
318 if (HasSlashAtEnd(strFile) )
320 strFile = strFile.Left(strFile.size() - 1);
323 int iPos = strFile.ReverseFind('/');
327 iPos = strFile.ReverseFind('\\');
333 strParent = url.Get();
337 strFile = strFile.Left(iPos);
339 AddSlashAtEnd(strFile);
341 url.SetFileName(strFile);
342 strParent = url.Get();
346 CStdString URIUtils::SubstitutePath(const CStdString& strPath)
348 for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
349 i != g_advancedSettings.m_pathSubstitutions.end(); i++)
351 if (strncmp(strPath.c_str(), i->first.c_str(), i->first.size()) == 0)
352 return URIUtils::AddFileToFolder(i->second, strPath.Mid(i->first.size()));
357 bool URIUtils::IsRemote(const CStdString& strFile)
359 if (IsCDDA(strFile) || IsISO9660(strFile))
362 if (IsSpecial(strFile))
363 return IsRemote(CSpecialProtocol::TranslatePath(strFile));
366 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
368 if(IsMultiPath(strFile))
369 { // virtual paths need to be checked separately
370 vector<CStdString> paths;
371 if (CMultiPathDirectory::GetPaths(strFile, paths))
373 for (unsigned int i = 0; i < paths.size(); i++)
374 if (IsRemote(paths[i])) return true;
380 if(IsInArchive(strFile))
381 return IsRemote(url.GetHostName());
389 bool URIUtils::IsOnDVD(const CStdString& strFile)
392 if (strFile.Mid(1,1) == ":")
393 return (GetDriveType(strFile.Left(2)) == DRIVE_CDROM);
395 if (strFile.Left(2).CompareNoCase("d:") == 0)
399 if (strFile.Left(4).CompareNoCase("dvd:") == 0)
402 if (strFile.Left(4).CompareNoCase("udf:") == 0)
405 if (strFile.Left(8).CompareNoCase("iso9660:") == 0)
408 if (strFile.Left(5).CompareNoCase("cdda:") == 0)
414 bool URIUtils::IsOnLAN(const CStdString& strPath)
416 if(IsMultiPath(strPath))
417 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
420 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
422 if(IsSpecial(strPath))
423 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
428 if(IsPlugin(strPath))
431 if(IsTuxBox(strPath))
438 if(IsInArchive(strPath))
439 return IsOnLAN(url.GetHostName());
441 if(!IsRemote(strPath))
444 CStdString host = url.GetHostName();
445 if(host.length() == 0)
448 // assume a hostname without dot's
449 // is local (smb netbios hostnames)
450 if(host.find('.') == string::npos)
453 unsigned long address = ntohl(inet_addr(host.c_str()));
454 if(address == INADDR_NONE)
457 if(CDNSNameCache::Lookup(host, ip))
458 address = ntohl(inet_addr(ip.c_str()));
461 if(address != INADDR_NONE)
463 // check if we are on the local subnet
464 if (!g_application.getNetwork().GetFirstConnectedInterface())
467 if (g_application.getNetwork().HasInterfaceForIP(address))
474 bool URIUtils::IsMultiPath(const CStdString& strPath)
476 return strPath.Left(10).Equals("multipath:");
479 bool URIUtils::IsHD(const CStdString& strFileName)
481 CURL url(strFileName);
483 if (IsSpecial(strFileName))
484 return IsHD(CSpecialProtocol::TranslatePath(strFileName));
486 if(IsStack(strFileName))
487 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
489 if (IsInArchive(strFileName))
490 return IsHD(url.GetHostName());
492 return url.IsLocal();
495 bool URIUtils::IsDVD(const CStdString& strFile)
498 if (strFile.Left(6).Equals("dvd://"))
501 if(strFile.Mid(1) != ":\\"
502 && strFile.Mid(1) != ":")
505 if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
508 CStdString strFileLow = strFile;
509 strFileLow.MakeLower();
510 if (strFileLow == "d:/" || strFileLow == "d:\\" || strFileLow == "d:" || strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
517 bool URIUtils::IsStack(const CStdString& strFile)
519 return strFile.Left(6).Equals("stack:");
522 bool URIUtils::IsRAR(const CStdString& strFile)
524 CStdString strExtension;
525 GetExtension(strFile,strExtension);
527 if (strExtension.Equals(".001") && strFile.Mid(strFile.length()-7,7).CompareNoCase(".ts.001"))
530 if (strExtension.CompareNoCase(".cbr") == 0)
533 if (strExtension.CompareNoCase(".rar") == 0)
539 bool URIUtils::IsInArchive(const CStdString &strFile)
541 return IsInZIP(strFile) || IsInRAR(strFile);
544 bool URIUtils::IsInZIP(const CStdString& strFile)
548 return url.GetProtocol() == "zip" && url.GetFileName() != "";
551 bool URIUtils::IsInRAR(const CStdString& strFile)
555 return url.GetProtocol() == "rar" && url.GetFileName() != "";
558 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
560 CStdString strExtension;
561 GetExtension(strFile,strExtension);
563 if (strExtension.CompareNoCase(".zip") == 0)
566 if (strExtension.CompareNoCase(".cbz") == 0)
572 bool URIUtils::IsSpecial(const CStdString& strFile)
574 CStdString strFile2(strFile);
576 if (IsStack(strFile))
577 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
579 return strFile2.Left(8).Equals("special:");
582 bool URIUtils::IsPlugin(const CStdString& strFile)
585 return url.GetProtocol().Equals("plugin");
588 bool URIUtils::IsScript(const CStdString& strFile)
591 return url.GetProtocol().Equals("script");
594 bool URIUtils::IsAddonsPath(const CStdString& strFile)
597 return url.GetProtocol().Equals("addons");
600 bool URIUtils::IsSourcesPath(const CStdString& strPath)
603 return url.GetProtocol().Equals("sources");
606 bool URIUtils::IsCDDA(const CStdString& strFile)
608 return strFile.Left(5).Equals("cdda:");
611 bool URIUtils::IsISO9660(const CStdString& strFile)
613 return strFile.Left(8).Equals("iso9660:");
616 bool URIUtils::IsSmb(const CStdString& strFile)
618 CStdString strFile2(strFile);
620 if (IsStack(strFile))
621 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
623 return strFile2.Left(4).Equals("smb:");
626 bool URIUtils::IsURL(const CStdString& strFile)
628 return strFile.Find("://") >= 0;
631 bool URIUtils::IsFTP(const CStdString& strFile)
633 CStdString strFile2(strFile);
635 if (IsStack(strFile))
636 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
640 return url.GetTranslatedProtocol() == "ftp" ||
641 url.GetTranslatedProtocol() == "ftps";
644 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
647 CStdString strProtocol = url.GetProtocol();
649 if (strProtocol.IsEmpty())
652 // there's nothing to stop internet streams from being stacked
653 if (strProtocol == "stack")
654 return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
656 CStdString strProtocol2 = url.GetTranslatedProtocol();
658 // Special case these
659 if (strProtocol2 == "ftp" || strProtocol2 == "ftps" ||
660 strProtocol == "dav" || strProtocol == "davs")
663 if (strProtocol2 == "http" || strProtocol2 == "https" ||
664 strProtocol == "rtp" || strProtocol == "udp" ||
665 strProtocol == "rtmp" || strProtocol == "rtsp")
671 bool URIUtils::IsDAAP(const CStdString& strFile)
673 return strFile.Left(5).Equals("daap:");
676 bool URIUtils::IsUPnP(const CStdString& strFile)
678 return strFile.Left(5).Equals("upnp:");
681 bool URIUtils::IsTuxBox(const CStdString& strFile)
683 return strFile.Left(7).Equals("tuxbox:");
686 bool URIUtils::IsMythTV(const CStdString& strFile)
688 return strFile.Left(5).Equals("myth:");
691 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
693 return strFile.Left(10).Equals("hdhomerun:");
696 bool URIUtils::IsSlingbox(const CStdString& strFile)
698 return strFile.Left(6).Equals("sling:");
701 bool URIUtils::IsVTP(const CStdString& strFile)
703 return strFile.Left(4).Equals("vtp:");
706 bool URIUtils::IsHTSP(const CStdString& strFile)
708 return strFile.Left(5).Equals("htsp:");
711 bool URIUtils::IsLiveTV(const CStdString& strFile)
715 || IsHDHomeRun(strFile)
716 || IsSlingbox(strFile)
718 || strFile.Left(4).Equals("sap:"))
721 if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
727 bool URIUtils::IsMusicDb(const CStdString& strFile)
729 return strFile.Left(8).Equals("musicdb:");
732 bool URIUtils::IsNfs(const CStdString& strFile)
734 CStdString strFile2(strFile);
736 if (IsStack(strFile))
737 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
739 return strFile2.Left(4).Equals("nfs:");
742 bool URIUtils::IsAfp(const CStdString& strFile)
744 CStdString strFile2(strFile);
746 if (IsStack(strFile))
747 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
749 return strFile2.Left(4).Equals("afp:");
753 bool URIUtils::IsVideoDb(const CStdString& strFile)
755 return strFile.Left(8).Equals("videodb:");
758 bool URIUtils::IsLastFM(const CStdString& strFile)
760 return strFile.Left(7).Equals("lastfm:");
763 bool URIUtils::IsDOSPath(const CStdString &path)
765 if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
768 // windows network drives
769 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
775 void URIUtils::AddSlashAtEnd(CStdString& strFolder)
777 if (IsURL(strFolder))
780 CStdString file = url.GetFileName();
781 if(!file.IsEmpty() && file != strFolder)
784 url.SetFileName(file);
785 strFolder = url.Get();
790 if (!HasSlashAtEnd(strFolder))
792 if (IsDOSPath(strFolder))
799 bool URIUtils::HasSlashAtEnd(const CStdString& strFile)
801 if (strFile.size() == 0) return false;
802 char kar = strFile.c_str()[strFile.size() - 1];
804 if (kar == '/' || kar == '\\')
810 void URIUtils::RemoveSlashAtEnd(CStdString& strFolder)
812 if (IsURL(strFolder))
815 CStdString file = url.GetFileName();
816 if (!file.IsEmpty() && file != strFolder)
818 RemoveSlashAtEnd(file);
819 url.SetFileName(file);
820 strFolder = url.Get();
823 if(url.GetHostName().IsEmpty())
827 while (HasSlashAtEnd(strFolder))
828 strFolder.Delete(strFolder.size() - 1);
831 void URIUtils::AddFileToFolder(const CStdString& strFolder,
832 const CStdString& strFile,
833 CStdString& strResult)
835 if (IsURL(strFolder))
838 if (url.GetFileName() != strFolder)
840 AddFileToFolder(url.GetFileName(), strFile, strResult);
841 url.SetFileName(strResult);
842 strResult = url.Get();
847 strResult = strFolder;
848 if(!strResult.IsEmpty())
849 AddSlashAtEnd(strResult);
851 // Remove any slash at the start of the file
852 if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
853 strResult += strFile.Mid(1);
855 strResult += strFile;
857 // correct any slash directions
858 if (!IsDOSPath(strFolder))
859 strResult.Replace('\\', '/');
861 strResult.Replace('/', '\\');
864 void URIUtils::GetDirectory(const CStdString& strFilePath,
865 CStdString& strDirectoryPath)
867 // Will from a full filename return the directory the file resides in.
868 // Keeps the final slash at end
870 int iPos1 = strFilePath.ReverseFind('/');
871 int iPos2 = strFilePath.ReverseFind('\\');
880 strDirectoryPath = strFilePath.Left(iPos1 + 1); // include the slash
882 // Keep possible |option=foo options for certain paths
883 iPos2 = strFilePath.ReverseFind('|');
886 strDirectoryPath += strFilePath.Mid(iPos2);
892 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
893 const CStdString& strType,
894 const CStdString& strArchivePath,
895 const CStdString& strFilePathInArchive,
896 const CStdString& strPwd)
898 CStdString strBuffer;
900 strUrlPath = strType+"://";
902 if( !strPwd.IsEmpty() )
905 CURL::Encode(strBuffer);
906 strUrlPath += strBuffer;
910 strBuffer = strArchivePath;
911 CURL::Encode(strBuffer);
913 strUrlPath += strBuffer;
915 strBuffer = strFilePathInArchive;
916 strBuffer.Replace('\\', '/');
917 strBuffer.TrimLeft('/');
920 strUrlPath += strBuffer;
922 #if 0 // options are not used
923 strBuffer = strCachePath;
924 CURL::Encode(strBuffer);
926 strUrlPath += "?cache=";
927 strUrlPath += strBuffer;
929 strBuffer.Format("%i", wOptions);
930 strUrlPath += "&flags=";
931 strUrlPath += strBuffer;