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 (IsStack(strFile))
484 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
486 if (IsSpecial(strFile))
487 return IsRemote(CSpecialProtocol::TranslatePath(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 (IsStack(strFileName))
624 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
626 if (IsSpecial(strFileName))
627 return IsHD(CSpecialProtocol::TranslatePath(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::IsLibraryContent(const std::string &strFile)
951 return (StringUtils::StartsWith(strFile, "library://") ||
952 StringUtils::StartsWith(strFile, "videodb://") ||
953 StringUtils::StartsWith(strFile, "musicdb://") ||
954 StringUtils::EndsWith(strFile, ".xsp"));
957 bool URIUtils::IsDOSPath(const CStdString &path)
959 if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
962 // windows network drives
963 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
969 void URIUtils::AddSlashAtEnd(std::string& strFolder)
971 if (IsURL(strFolder))
974 std::string file = url.GetFileName();
975 if(!file.empty() && file != strFolder)
978 url.SetFileName(file);
979 strFolder = url.Get();
984 if (!HasSlashAtEnd(strFolder))
986 if (IsDOSPath(strFolder))
993 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
995 if (strFile.empty()) return false;
996 if (checkURL && IsURL(strFile))
999 CStdString file = url.GetFileName();
1000 return file.empty() || HasSlashAtEnd(file, false);
1002 char kar = strFile.c_str()[strFile.size() - 1];
1004 if (kar == '/' || kar == '\\')
1010 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
1012 if (IsURL(strFolder))
1014 CURL url(strFolder);
1015 std::string file = url.GetFileName();
1016 if (!file.empty() && file != strFolder)
1018 RemoveSlashAtEnd(file);
1019 url.SetFileName(file);
1020 strFolder = url.Get();
1023 if(url.GetHostName().empty())
1027 while (HasSlashAtEnd(strFolder))
1028 strFolder.erase(strFolder.size()-1, 1);
1031 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
1033 CStdString strc1 = strPath1, strc2 = strPath2;
1034 RemoveSlashAtEnd(strc1);
1035 RemoveSlashAtEnd(strc2);
1036 return strc1.Equals(strc2);
1040 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
1042 const size_t len = path.length();
1043 if (startFrom >= len)
1046 std::string result(path, 0, startFrom);
1047 result.reserve(len);
1049 const char* const str = path.c_str();
1050 size_t pos = startFrom;
1053 if (str[pos] == '\\' || str[pos] == '/')
1055 result.push_back(slashCharacter); // append one slash
1057 // skip any following slashes
1058 while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1062 result.push_back(str[pos++]); // append current char and advance pos to next char
1064 } while (pos < len);
1070 std::string URIUtils::CanonicalizePath(const std::string& path, const char slashCharacter /*= '\\'*/)
1072 assert(slashCharacter == '\\' || slashCharacter == '/');
1077 const std::string slashStr(1, slashCharacter);
1078 vector<std::string> pathVec, resultVec;
1079 StringUtils::Tokenize(path, pathVec, slashStr);
1081 for (vector<std::string>::const_iterator it = pathVec.begin(); it != pathVec.end(); ++it)
1084 { /* skip - do nothing */ }
1085 else if (*it == ".." && !resultVec.empty() && resultVec.back() != "..")
1086 resultVec.pop_back();
1088 resultVec.push_back(*it);
1092 if (path[0] == slashCharacter)
1093 result.push_back(slashCharacter); // add slash at the begin
1095 result += StringUtils::Join(resultVec, slashStr);
1097 if (path[path.length() - 1] == slashCharacter && !result.empty() && result[result.length() - 1] != slashCharacter)
1098 result.push_back(slashCharacter); // add slash at the end if result isn't empty and result isn't "/"
1103 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder,
1104 const CStdString& strFile)
1106 if (IsURL(strFolder))
1108 CURL url(strFolder);
1109 if (url.GetFileName() != strFolder)
1111 url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1116 CStdString strResult = strFolder;
1117 if (!strResult.empty())
1118 AddSlashAtEnd(strResult);
1120 // Remove any slash at the start of the file
1121 if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1122 strResult += strFile.substr(1);
1124 strResult += strFile;
1126 // correct any slash directions
1127 if (!IsDOSPath(strFolder))
1128 StringUtils::Replace(strResult, '\\', '/');
1130 StringUtils::Replace(strResult, '/', '\\');
1135 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1137 // Will from a full filename return the directory the file resides in.
1138 // Keeps the final slash at end and possible |option=foo options.
1140 size_t iPosSlash = strFilePath.find_last_of("/\\");
1141 if (iPosSlash == string::npos)
1142 return ""; // No slash, so no path (ignore any options)
1144 size_t iPosBar = strFilePath.rfind('|');
1145 if (iPosBar == string::npos)
1146 return strFilePath.substr(0, iPosSlash + 1); // Only path
1148 return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
1151 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1152 const CStdString& strType,
1153 const CStdString& strArchivePath,
1154 const CStdString& strFilePathInArchive,
1155 const CStdString& strPwd)
1157 strUrlPath = strType+"://";
1159 if( !strPwd.empty() )
1161 strUrlPath += CURL::Encode(strPwd);
1165 strUrlPath += CURL::Encode(strArchivePath);
1167 CStdString strBuffer(strFilePathInArchive);
1168 StringUtils::Replace(strBuffer, '\\', '/');
1169 StringUtils::TrimLeft(strBuffer, "/");
1172 strUrlPath += strBuffer;
1174 #if 0 // options are not used
1175 strBuffer = strCachePath;
1176 strBuffer = CURL::Encode(strBuffer);
1178 strUrlPath += "?cache=";
1179 strUrlPath += strBuffer;
1181 strBuffer = StringUtils::Format("%i", wOptions);
1182 strUrlPath += "&flags=";
1183 strUrlPath += strBuffer;
1187 string URIUtils::GetRealPath(const string &path)
1193 url.SetHostName(GetRealPath(url.GetHostName()));
1194 url.SetFileName(resolvePath(url.GetFileName()));
1199 std::string URIUtils::resolvePath(const std::string &path)
1204 size_t posSlash = path.find('/');
1205 size_t posBackslash = path.find('\\');
1206 string delim = posSlash < posBackslash ? "/" : "\\";
1207 vector<string> parts = StringUtils::Split(path, delim);
1208 vector<string> realParts;
1210 for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1212 if (part->empty() || part->compare(".") == 0)
1215 // go one level back up
1216 if (part->compare("..") == 0)
1218 if (!realParts.empty())
1219 realParts.pop_back();
1223 realParts.push_back(*part);
1226 CStdString realPath;
1227 // re-add any / or \ at the beginning
1228 for (std::string::const_iterator itPath = path.begin(); itPath != path.end(); ++itPath)
1230 if (*itPath != delim.at(0))
1235 // put together the path
1236 realPath += StringUtils::Join(realParts, delim);
1237 // re-add any / or \ at the end
1238 if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
1244 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1246 if (strFilename.empty())
1249 CURL url(strFilename);
1250 // if this is a stack:// URL we need to work with its filename
1251 if (URIUtils::IsStack(strFilename))
1253 vector<CStdString> files;
1254 if (!CStackDirectory::GetPaths(strFilename, files))
1257 for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1259 std::string filePath = *file;
1260 UpdateUrlEncoding(filePath);
1264 CStdString stackPath;
1265 if (!CStackDirectory::ConstructStackPath(files, stackPath))
1268 url.Parse(stackPath);
1270 // if the protocol has an encoded hostname we need to work with its hostname
1271 else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1273 std::string hostname = url.GetHostName();
1274 UpdateUrlEncoding(hostname);
1275 url.SetHostName(hostname);
1280 std::string newFilename = url.Get();
1281 if (newFilename == strFilename)
1284 strFilename = newFilename;