2 * Copyright (C) 2005-2013 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, see
17 * <http://www.gnu.org/licenses/>.
21 #include "network/Network.h"
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/AdvancedSettings.h"
31 #include "settings/MediaSettings.h"
33 #include "StringUtils.h"
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
39 using namespace XFILE;
41 bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
43 CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
44 CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
45 return StringUtils::StartsWith(uriPath, basePath);
48 /* returns filename extension including period of filename */
49 CStdString URIUtils::GetExtension(const CStdString& strFileName)
51 if (IsURL(strFileName))
53 CURL url(strFileName);
54 return GetExtension(url.GetFileName());
57 size_t period = strFileName.find_last_of("./\\");
58 if (period == string::npos || strFileName[period] != '.')
61 return strFileName.substr(period);
64 bool URIUtils::HasExtension(const CStdString& strFileName)
66 if (IsURL(strFileName))
68 CURL url(strFileName);
69 return HasExtension(url.GetFileName());
72 size_t iPeriod = strFileName.find_last_of("./\\");
73 return iPeriod != string::npos && strFileName[iPeriod] == '.';
76 bool URIUtils::HasExtension(const CStdString& strFileName, const CStdString& strExtensions)
78 if (IsURL(strFileName))
80 CURL url(strFileName);
81 return HasExtension(url.GetFileName(), strExtensions);
84 // Search backwards so that '.' can be used as a search terminator.
85 CStdString::const_reverse_iterator itExtensions = strExtensions.rbegin();
86 while (itExtensions != strExtensions.rend())
88 // Iterate backwards over strFileName untill we hit a '.' or a mismatch
89 for (CStdString::const_reverse_iterator itFileName = strFileName.rbegin();
90 itFileName != strFileName.rend() && itExtensions != strExtensions.rend() &&
91 tolower(*itFileName) == *itExtensions;
92 ++itFileName, ++itExtensions)
94 if (*itExtensions == '.')
98 // No match. Look for more extensions to try.
99 while (itExtensions != strExtensions.rend() && *itExtensions != '|')
102 while (itExtensions != strExtensions.rend() && *itExtensions == '|')
109 void URIUtils::RemoveExtension(CStdString& strFileName)
111 if(IsURL(strFileName))
113 CURL url(strFileName);
114 strFileName = url.GetFileName();
115 RemoveExtension(strFileName);
116 url.SetFileName(strFileName);
117 strFileName = url.Get();
121 size_t period = strFileName.find_last_of("./\\");
122 if (period != string::npos && strFileName[period] == '.')
124 CStdString strExtension = strFileName.substr(period);
125 StringUtils::ToLower(strExtension);
128 CStdString strFileMask;
129 strFileMask = g_advancedSettings.m_pictureExtensions;
130 strFileMask += "|" + g_advancedSettings.m_musicExtensions;
131 strFileMask += "|" + g_advancedSettings.m_videoExtensions;
132 strFileMask += "|" + g_advancedSettings.m_subtitlesExtensions;
133 #if defined(TARGET_DARWIN)
134 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
136 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
140 if (strFileMask.find(strExtension) != std::string::npos)
141 strFileName.erase(period);
145 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
146 const CStdString& strNewExtension)
151 url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
155 CStdString strChangedFile;
156 CStdString strExtension = GetExtension(strFile);
157 if ( strExtension.size() )
159 strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
160 strChangedFile += strNewExtension;
164 strChangedFile = strFile;
165 strChangedFile += strNewExtension;
167 return strChangedFile;
170 /* returns a filename given an url */
171 /* handles both / and \, and options in urls*/
172 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
174 if(IsURL(strFileNameAndPath))
176 CURL url(strFileNameAndPath);
177 return GetFileName(url.GetFileName());
180 /* find the last slash */
181 const size_t slash = strFileNameAndPath.find_last_of("/\\");
182 return strFileNameAndPath.substr(slash+1);
185 void URIUtils::Split(const CStdString& strFileNameAndPath,
186 CStdString& strPath, CStdString& strFileName)
188 std::string strPathT, strFileNameT;
189 Split(strFileNameAndPath, strPathT, strFileNameT);
191 strFileName = strFileNameT;
194 void URIUtils::Split(const std::string& strFileNameAndPath,
195 std::string& strPath, std::string& strFileName)
197 //Splits a full filename in path and file.
198 //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
199 //Trailing slash will be preserved
202 int i = strFileNameAndPath.size() - 1;
205 char ch = strFileNameAndPath[i];
206 // Only break on ':' if it's a drive separator for DOS (ie d:foo)
207 if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
213 // take left including the directory separator
214 strPath = strFileNameAndPath.substr(0, i+1);
215 // everything to the right of the directory separator
216 strFileName = strFileNameAndPath.substr(i+1);
219 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
223 // silly CStdString can't take a char in the constructor
224 CStdString sep(1, url.GetDirectorySeparator());
226 // split the filename portion of the URL up into separate dirs
227 CStdStringArray dirs;
228 StringUtils::SplitString(url.GetFileName(), sep, dirs);
230 // we start with the root path
231 CStdString dir = url.GetWithoutFilename();
234 dirs.insert(dirs.begin(), dir);
236 // we don't need empty token on the end
237 if (dirs.size() > 1 && dirs.back().empty())
238 dirs.erase(dirs.end() - 1);
243 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
245 // find the common path of parent and path
247 while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
249 strParent.erase(j - 1);
250 // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
251 if (!HasSlashAtEnd(strParent))
253 strParent = GetDirectory(strParent);
254 AddSlashAtEnd(strParent);
258 bool URIUtils::ProtocolHasParentInHostname(const CStdString& prot)
260 return prot.Equals("zip")
261 || prot.Equals("rar")
262 || prot.Equals("apk")
263 || prot.Equals("bluray")
264 || prot.Equals("udf");
267 bool URIUtils::ProtocolHasEncodedHostname(const CStdString& prot)
269 return ProtocolHasParentInHostname(prot)
270 || prot.Equals("musicsearch")
271 || prot.Equals("image");
274 bool URIUtils::ProtocolHasEncodedFilename(const CStdString& prot)
276 CStdString prot2 = CURL::TranslateProtocol(prot);
278 // For now assume only (quasi) http internet streams use URL encoding
279 return prot2 == "http" ||
283 CStdString URIUtils::GetParentPath(const CStdString& strPath)
285 CStdString strReturn;
286 GetParentPath(strPath, strReturn);
290 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
295 CStdString strFile = url.GetFileName();
296 if ( URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFile.empty())
298 strFile = url.GetHostName();
299 return GetParentPath(strFile, strParent);
301 else if (url.GetProtocol() == "stack")
305 dir.GetDirectory(strPath,items);
306 items[0]->m_strDVDLabel = GetDirectory(items[0]->GetPath());
307 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
308 GetParentPath(items[0]->m_strDVDLabel, strParent);
310 strParent = items[0]->m_strDVDLabel;
311 for( int i=1;i<items.Size();++i)
313 items[i]->m_strDVDLabel = GetDirectory(items[i]->GetPath());
314 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
315 items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
317 items[i]->SetPath(items[i]->m_strDVDLabel);
319 GetCommonPath(strParent,items[i]->GetPath());
323 else if (url.GetProtocol() == "multipath")
325 // get the parent path of the first item
326 return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
328 else if (url.GetProtocol() == "plugin")
330 if (!url.GetOptions().empty())
333 strParent = url.Get();
336 if (!url.GetFileName().empty())
339 strParent = url.Get();
342 if (!url.GetHostName().empty())
345 strParent = url.Get();
348 return true; // already at root
350 else if (url.GetProtocol() == "special")
352 if (HasSlashAtEnd(strFile))
353 strFile.erase(strFile.size() - 1);
354 if(strFile.rfind('/') == std::string::npos)
357 else if (strFile.size() == 0)
359 if (url.GetHostName().size() > 0)
361 // we have an share with only server or workgroup name
362 // set hostname to "" and return true to get back to root
364 strParent = url.Get();
370 if (HasSlashAtEnd(strFile) )
372 strFile.erase(strFile.size() - 1);
375 size_t iPos = strFile.rfind('/');
377 if (iPos == std::string::npos)
379 iPos = strFile.rfind('\\');
382 if (iPos == std::string::npos)
385 strParent = url.Get();
391 AddSlashAtEnd(strFile);
393 url.SetFileName(strFile);
394 strParent = url.Get();
398 std::string URLEncodePath(const std::string& strPath)
400 vector<string> segments = StringUtils::Split(strPath, "/");
401 for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
402 *i = CURL::Encode(*i);
404 return StringUtils::Join(segments, "/");
407 std::string URLDecodePath(const std::string& strPath)
409 vector<string> segments = StringUtils::Split(strPath, "/");
410 for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
411 *i = CURL::Decode(*i);
413 return StringUtils::Join(segments, "/");
416 std::string URIUtils::ChangeBasePath(const std::string &fromPath, const std::string &fromFile, const std::string &toPath)
418 std::string toFile = fromFile;
420 // Convert back slashes to forward slashes, if required
421 if (IsDOSPath(fromPath) && !IsDOSPath(toPath))
422 StringUtils::Replace(toFile, "\\", "/");
424 // Handle difference in URL encoded vs. not encoded
425 if ( ProtocolHasEncodedFilename(CURL(fromPath).GetProtocol() )
426 && !ProtocolHasEncodedFilename(CURL(toPath).GetProtocol() ) )
428 toFile = URLDecodePath(toFile); // Decode path
430 else if (!ProtocolHasEncodedFilename(CURL(fromPath).GetProtocol() )
431 && ProtocolHasEncodedFilename(CURL(toPath).GetProtocol() ) )
433 toFile = URLEncodePath(toFile); // Encode path
436 // Convert forward slashes to back slashes, if required
437 if (!IsDOSPath(fromPath) && IsDOSPath(toPath))
438 StringUtils::Replace(toFile, "/", "\\");
440 return AddFileToFolder(toPath, toFile);
443 CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
445 for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
446 i != g_advancedSettings.m_pathSubstitutions.end(); i++)
453 fromPath = i->first; // Fake path
454 toPath = i->second; // Real path
458 fromPath = i->second; // Real path
459 toPath = i->first; // Fake path
462 if (strncmp(strPath.c_str(), fromPath.c_str(), HasSlashAtEnd(fromPath) ? fromPath.size() - 1 : fromPath.size()) == 0)
464 if (strPath.size() > fromPath.size())
466 CStdString strSubPathAndFileName = strPath.substr(fromPath.size());
467 return ChangeBasePath(fromPath, strSubPathAndFileName, toPath); // Fix encoding + slash direction
478 bool URIUtils::IsRemote(const CStdString& strFile)
480 if (IsCDDA(strFile) || IsISO9660(strFile))
483 if (IsSpecial(strFile))
484 return IsRemote(CSpecialProtocol::TranslatePath(strFile));
487 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
489 if(IsMultiPath(strFile))
490 { // virtual paths need to be checked separately
491 vector<CStdString> paths;
492 if (CMultiPathDirectory::GetPaths(strFile, paths))
494 for (unsigned int i = 0; i < paths.size(); i++)
495 if (IsRemote(paths[i])) return true;
501 if(ProtocolHasParentInHostname(url.GetProtocol()))
502 return IsRemote(url.GetHostName());
510 bool URIUtils::IsOnDVD(const CStdString& strFile)
512 #ifdef TARGET_WINDOWS
513 if (strFile.size() >= 2 && strFile.substr(1,1) == ":")
514 return (GetDriveType(strFile.substr(0, 3).c_str()) == DRIVE_CDROM);
517 if (StringUtils::StartsWith(strFile, "dvd:"))
520 if (StringUtils::StartsWith(strFile, "udf:"))
523 if (StringUtils::StartsWith(strFile, "iso9660:"))
526 if (StringUtils::StartsWith(strFile, "cdda:"))
532 bool URIUtils::IsOnLAN(const CStdString& strPath)
534 if(IsMultiPath(strPath))
535 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
538 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
540 if(IsSpecial(strPath))
541 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
546 if(IsPlugin(strPath))
549 if(IsTuxBox(strPath))
556 if (ProtocolHasParentInHostname(url.GetProtocol()))
557 return IsOnLAN(url.GetHostName());
559 if(!IsRemote(strPath))
562 CStdString host = url.GetHostName();
564 return IsHostOnLAN(host);
567 static bool addr_match(uint32_t addr, const char* target, const char* submask)
569 uint32_t addr2 = ntohl(inet_addr(target));
570 uint32_t mask = ntohl(inet_addr(submask));
571 return (addr & mask) == (addr2 & mask);
574 bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
576 if(host.length() == 0)
579 // assume a hostname without dot's
580 // is local (smb netbios hostnames)
581 if(host.find('.') == string::npos)
584 uint32_t address = ntohl(inet_addr(host.c_str()));
585 if(address == INADDR_NONE)
588 if(CDNSNameCache::Lookup(host, ip))
589 address = ntohl(inet_addr(ip.c_str()));
592 if(address != INADDR_NONE)
594 if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
597 addr_match(address, "192.168.0.0", "255.255.0.0") ||
598 addr_match(address, "10.0.0.0", "255.0.0.0") ||
599 addr_match(address, "172.16.0.0", "255.240.0.0")
603 // check if we are on the local subnet
604 if (!g_application.getNetwork().GetFirstConnectedInterface())
607 if (g_application.getNetwork().HasInterfaceForIP(address))
614 bool URIUtils::IsMultiPath(const CStdString& strPath)
616 return StringUtils::StartsWithNoCase(strPath, "multipath:");
619 bool URIUtils::IsHD(const CStdString& strFileName)
621 CURL url(strFileName);
623 if (IsSpecial(strFileName))
624 return IsHD(CSpecialProtocol::TranslatePath(strFileName));
626 if(IsStack(strFileName))
627 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
629 if (ProtocolHasParentInHostname(url.GetProtocol()))
630 return IsHD(url.GetHostName());
632 return url.GetProtocol().empty() || url.GetProtocol() == "file";
635 bool URIUtils::IsDVD(const CStdString& strFile)
637 CStdString strFileLow = strFile;
638 StringUtils::ToLower(strFileLow);
639 if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile))
642 #if defined(TARGET_WINDOWS)
643 if (StringUtils::StartsWithNoCase(strFile, "dvd://"))
646 if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
649 if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
652 if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
659 bool URIUtils::IsStack(const CStdString& strFile)
661 return StringUtils::StartsWithNoCase(strFile, "stack:");
664 bool URIUtils::IsRAR(const CStdString& strFile)
666 CStdString strExtension = GetExtension(strFile);
668 if (strExtension.Equals(".001") && !StringUtils::EndsWithNoCase(strFile, ".ts.001"))
671 if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
674 if (StringUtils::EqualsNoCase(strExtension, ".rar"))
680 bool URIUtils::IsInArchive(const CStdString &strFile)
682 return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
685 bool URIUtils::IsInAPK(const CStdString& strFile)
689 return url.GetProtocol() == "apk" && url.GetFileName() != "";
692 bool URIUtils::IsInZIP(const CStdString& strFile)
696 return url.GetProtocol() == "zip" && url.GetFileName() != "";
699 bool URIUtils::IsInRAR(const CStdString& strFile)
703 return url.GetProtocol() == "rar" && url.GetFileName() != "";
706 bool URIUtils::IsAPK(const CStdString& strFile)
708 return HasExtension(strFile, ".apk");
711 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
713 return HasExtension(strFile, ".zip|.cbz");
716 bool URIUtils::IsArchive(const CStdString& strFile)
718 return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
721 bool URIUtils::IsSpecial(const CStdString& strFile)
723 CStdString strFile2(strFile);
725 if (IsStack(strFile))
726 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
728 return StringUtils::StartsWithNoCase(strFile2, "special:");
731 bool URIUtils::IsPlugin(const CStdString& strFile)
734 return url.GetProtocol().Equals("plugin");
737 bool URIUtils::IsScript(const CStdString& strFile)
740 return url.GetProtocol().Equals("script");
743 bool URIUtils::IsAddonsPath(const CStdString& strFile)
746 return url.GetProtocol().Equals("addons");
749 bool URIUtils::IsSourcesPath(const CStdString& strPath)
752 return url.GetProtocol().Equals("sources");
755 bool URIUtils::IsCDDA(const CStdString& strFile)
757 return StringUtils::StartsWithNoCase(strFile, "cdda:");
760 bool URIUtils::IsISO9660(const CStdString& strFile)
762 return StringUtils::StartsWithNoCase(strFile, "iso9660:");
765 bool URIUtils::IsSmb(const CStdString& strFile)
767 CStdString strFile2(strFile);
769 if (IsStack(strFile))
770 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
772 return StringUtils::StartsWithNoCase(strFile2, "smb:");
775 bool URIUtils::IsURL(const CStdString& strFile)
777 return strFile.find("://") != std::string::npos;
780 bool URIUtils::IsFTP(const CStdString& strFile)
782 CStdString strFile2(strFile);
784 if (IsStack(strFile))
785 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
787 return StringUtils::StartsWithNoCase(strFile2, "ftp:") ||
788 StringUtils::StartsWithNoCase(strFile2, "ftps:");
791 bool URIUtils::IsDAV(const CStdString& strFile)
793 CStdString strFile2(strFile);
795 if (IsStack(strFile))
796 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
798 return StringUtils::StartsWithNoCase(strFile2, "dav:") ||
799 StringUtils::StartsWithNoCase(strFile2, "davs:");
802 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
804 CStdString strProtocol = url.GetProtocol();
806 if (strProtocol.empty())
809 // there's nothing to stop internet streams from being stacked
810 if (strProtocol == "stack")
811 return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
813 CStdString strProtocol2 = url.GetTranslatedProtocol();
815 // Special case these
816 if (strProtocol == "ftp" || strProtocol == "ftps" ||
817 strProtocol == "dav" || strProtocol == "davs")
820 if (strProtocol2 == "http" || strProtocol2 == "https" ||
821 strProtocol2 == "tcp" || strProtocol2 == "udp" ||
822 strProtocol2 == "rtp" || strProtocol2 == "sdp" ||
823 strProtocol2 == "mms" || strProtocol2 == "mmst" ||
824 strProtocol2 == "mmsh" || strProtocol2 == "rtsp" ||
825 strProtocol2 == "rtmp" || strProtocol2 == "rtmpt" ||
826 strProtocol2 == "rtmpe" || strProtocol2 == "rtmpte" ||
827 strProtocol2 == "rtmps")
833 bool URIUtils::IsDAAP(const CStdString& strFile)
835 return StringUtils::StartsWithNoCase(strFile, "daap:");
838 bool URIUtils::IsUPnP(const CStdString& strFile)
840 return StringUtils::StartsWithNoCase(strFile, "upnp:");
843 bool URIUtils::IsTuxBox(const CStdString& strFile)
845 return StringUtils::StartsWithNoCase(strFile, "tuxbox:");
848 bool URIUtils::IsMythTV(const CStdString& strFile)
850 return StringUtils::StartsWithNoCase(strFile, "myth:");
853 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
855 return StringUtils::StartsWithNoCase(strFile, "hdhomerun:");
858 bool URIUtils::IsSlingbox(const CStdString& strFile)
860 return StringUtils::StartsWithNoCase(strFile, "sling:");
863 bool URIUtils::IsVTP(const CStdString& strFile)
865 return StringUtils::StartsWithNoCase(strFile, "vtp:");
868 bool URIUtils::IsHTSP(const CStdString& strFile)
870 return StringUtils::StartsWithNoCase(strFile, "htsp:");
873 bool URIUtils::IsLiveTV(const CStdString& strFile)
875 CStdString strFileWithoutSlash(strFile);
876 RemoveSlashAtEnd(strFileWithoutSlash);
880 || IsHDHomeRun(strFile)
881 || IsSlingbox(strFile)
883 || StringUtils::StartsWithNoCase(strFile, "sap:")
884 ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !StringUtils::StartsWithNoCase(strFileWithoutSlash, "pvr://recordings")))
887 if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
893 bool URIUtils::IsPVRRecording(const CStdString& strFile)
895 CStdString strFileWithoutSlash(strFile);
896 RemoveSlashAtEnd(strFileWithoutSlash);
898 return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
899 StringUtils::StartsWithNoCase(strFile, "pvr://recordings");
902 bool URIUtils::IsMusicDb(const CStdString& strFile)
904 return StringUtils::StartsWithNoCase(strFile, "musicdb:");
907 bool URIUtils::IsNfs(const CStdString& strFile)
909 CStdString strFile2(strFile);
911 if (IsStack(strFile))
912 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
914 return StringUtils::StartsWithNoCase(strFile2, "nfs:");
917 bool URIUtils::IsAfp(const CStdString& strFile)
919 CStdString strFile2(strFile);
921 if (IsStack(strFile))
922 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
924 return StringUtils::StartsWithNoCase(strFile2, "afp:");
928 bool URIUtils::IsVideoDb(const CStdString& strFile)
930 return StringUtils::StartsWithNoCase(strFile, "videodb:");
933 bool URIUtils::IsBluray(const CStdString& strFile)
935 return StringUtils::StartsWithNoCase(strFile, "bluray:");
938 bool URIUtils::IsAndroidApp(const CStdString &path)
940 return StringUtils::StartsWithNoCase(path, "androidapp:");
943 bool URIUtils::IsLibraryFolder(const CStdString& strFile)
946 return url.GetProtocol().Equals("library");
949 bool URIUtils::IsDOSPath(const CStdString &path)
951 if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
954 // windows network drives
955 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
961 void URIUtils::AddSlashAtEnd(std::string& strFolder)
963 if (IsURL(strFolder))
966 std::string file = url.GetFileName();
967 if(!file.empty() && file != strFolder)
970 url.SetFileName(file);
971 strFolder = url.Get();
976 if (!HasSlashAtEnd(strFolder))
978 if (IsDOSPath(strFolder))
985 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
987 if (strFile.empty()) return false;
988 if (checkURL && IsURL(strFile))
991 CStdString file = url.GetFileName();
992 return file.empty() || HasSlashAtEnd(file, false);
994 char kar = strFile.c_str()[strFile.size() - 1];
996 if (kar == '/' || kar == '\\')
1002 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
1004 if (IsURL(strFolder))
1006 CURL url(strFolder);
1007 std::string file = url.GetFileName();
1008 if (!file.empty() && file != strFolder)
1010 RemoveSlashAtEnd(file);
1011 url.SetFileName(file);
1012 strFolder = url.Get();
1015 if(url.GetHostName().empty())
1019 while (HasSlashAtEnd(strFolder))
1020 strFolder.erase(strFolder.size()-1, 1);
1023 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
1025 CStdString strc1 = strPath1, strc2 = strPath2;
1026 RemoveSlashAtEnd(strc1);
1027 RemoveSlashAtEnd(strc2);
1028 return strc1.Equals(strc2);
1032 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
1034 const size_t len = path.length();
1035 if (startFrom >= len)
1038 std::string result(path, 0, startFrom);
1039 result.reserve(len);
1041 const char* const str = path.c_str();
1042 size_t pos = startFrom;
1045 if (str[pos] == '\\' || str[pos] == '/')
1047 result.push_back(slashCharacter); // append one slash
1049 // skip any following slashes
1050 while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1054 result.push_back(str[pos++]); // append current char and advance pos to next char
1056 } while (pos < len);
1062 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder,
1063 const CStdString& strFile)
1065 if (IsURL(strFolder))
1067 CURL url(strFolder);
1068 if (url.GetFileName() != strFolder)
1070 url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1075 CStdString strResult = strFolder;
1076 if (!strResult.empty())
1077 AddSlashAtEnd(strResult);
1079 // Remove any slash at the start of the file
1080 if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1081 strResult += strFile.substr(1);
1083 strResult += strFile;
1085 // correct any slash directions
1086 if (!IsDOSPath(strFolder))
1087 StringUtils::Replace(strResult, '\\', '/');
1089 StringUtils::Replace(strResult, '/', '\\');
1094 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1096 // Will from a full filename return the directory the file resides in.
1097 // Keeps the final slash at end and possible |option=foo options.
1099 size_t iPosSlash = strFilePath.find_last_of("/\\");
1100 if (iPosSlash == string::npos)
1101 return ""; // No slash, so no path (ignore any options)
1103 size_t iPosBar = strFilePath.rfind('|');
1104 if (iPosBar == string::npos)
1105 return strFilePath.substr(0, iPosSlash + 1); // Only path
1107 return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
1110 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1111 const CStdString& strType,
1112 const CStdString& strArchivePath,
1113 const CStdString& strFilePathInArchive,
1114 const CStdString& strPwd)
1116 strUrlPath = strType+"://";
1118 if( !strPwd.empty() )
1120 strUrlPath += CURL::Encode(strPwd);
1124 strUrlPath += CURL::Encode(strArchivePath);
1126 CStdString strBuffer(strFilePathInArchive);
1127 StringUtils::Replace(strBuffer, '\\', '/');
1128 StringUtils::TrimLeft(strBuffer, "/");
1131 strUrlPath += strBuffer;
1133 #if 0 // options are not used
1134 strBuffer = strCachePath;
1135 strBuffer = CURL::Encode(strBuffer);
1137 strUrlPath += "?cache=";
1138 strUrlPath += strBuffer;
1140 strBuffer = StringUtils::Format("%i", wOptions);
1141 strUrlPath += "&flags=";
1142 strUrlPath += strBuffer;
1146 string URIUtils::GetRealPath(const string &path)
1152 url.SetHostName(GetRealPath(url.GetHostName()));
1153 url.SetFileName(resolvePath(url.GetFileName()));
1158 std::string URIUtils::resolvePath(const std::string &path)
1163 size_t posSlash = path.find('/');
1164 size_t posBackslash = path.find('\\');
1165 string delim = posSlash < posBackslash ? "/" : "\\";
1166 vector<string> parts = StringUtils::Split(path, delim);
1167 vector<string> realParts;
1169 for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1171 if (part->empty() || part->compare(".") == 0)
1174 // go one level back up
1175 if (part->compare("..") == 0)
1177 if (!realParts.empty())
1178 realParts.pop_back();
1182 realParts.push_back(*part);
1185 CStdString realPath;
1187 // re-add any / or \ at the beginning
1188 while (path.at(i) == delim.at(0))
1193 // put together the path
1194 realPath += StringUtils::Join(realParts, delim);
1195 // re-add any / or \ at the end
1196 if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
1202 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1204 if (strFilename.empty())
1207 CURL url(strFilename);
1208 // if this is a stack:// URL we need to work with its filename
1209 if (URIUtils::IsStack(strFilename))
1211 vector<CStdString> files;
1212 if (!CStackDirectory::GetPaths(strFilename, files))
1215 for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1217 std::string filePath = *file;
1218 UpdateUrlEncoding(filePath);
1222 CStdString stackPath;
1223 if (!CStackDirectory::ConstructStackPath(files, stackPath))
1226 url.Parse(stackPath);
1228 // if the protocol has an encoded hostname we need to work with its hostname
1229 else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1231 std::string hostname = url.GetHostName();
1232 UpdateUrlEncoding(hostname);
1233 url.SetHostName(hostname);
1238 std::string newFilename = url.Get();
1239 if (newFilename == strFilename)
1242 strFilename = newFilename;